动态代理实现多数据库的定时刷新链接信息的应用
Posted tongangle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态代理实现多数据库的定时刷新链接信息的应用相关的知识,希望对你有一定的参考价值。
开始研究动态代理之前 先简要谈下动态代理的概念
在不改变原有类结构的前提下增强类的功能以及对类原有方法操作,注意是方法不是属性(属性一般被设计为private修饰,不可以直接被调用)
动态代理的基本实例不做阐述,网上一大把 不理解的同学可以直接去搜索。
今天说的是自己在项目中遇到的一个实际的动态代理应用--》定时刷新多数据库的连接接属性
项目背景:项目中存在三个数据库 redis PostgreSQL(PT库) oracle
我们做的需求是将oracle和redis的数据库链接存储在 PT库中 然后项目启动后从PT库的自定义配置表中读取oracle和redis的数据库链接
在这里我们使用的是druid连接池 有兴趣的伙伴可以去研究下
废话不多说直接上代码实例
基于CGlib实现
1 package com.manager.aop; 2 3 import java.lang.reflect.Method; 4 import java.sql.SQLException; 5 import java.util.concurrent.ScheduledFuture; 6 import javax.annotation.PostConstruct; 7 import javax.sql.DataSource; 8 import org.springframework.beans.factory.FactoryBean; 9 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 10 import com.alibaba.druid.pool.DruidDataSource; 11 import net.sf.cglib.proxy.Enhancer; 12 import net.sf.cglib.proxy.MethodInterceptor; 13 import net.sf.cglib.proxy.MethodProxy; 14 15 16 17 18 public class DynamicProxy implements FactoryBean<DataSource> { 19 20 private DruidDataSource target; 21 private DataSource proxy; 22 23 24 //这两个类是线程池的类用来做定时器任务 25 //需要用注解注入进来 26 private ThreadPoolTaskScheduler scheduler; 27 private ScheduledFuture<?> future; 28 29 30 //存储每次刷新的上一次的数据库的链接属性,以便和最新的数据库链接对比 31 private String key; 32 @PostConstruct 33 private void init() throws SQLException { 34 //项目启动后 第一次实例化连接池 35 target=createDataSource(); 36 //生成Druid链接池的代理对象 37 proxy=(DataSource) Enhancer.create(target.getClass(),//设置代理目标类的字节码对象 38 CallBacks()//设置代理对象的回调对象 39 ); 40 //判断是否创建新的代理对象 41 refresh(); 42 future=scheduler.scheduleWithFixedDelay(new Runnable() { 43 public void run() { 44 // TODO Auto-generated method stub 45 try { 46 refresh(); 47 } catch (SQLException e) { 48 // TODO Auto-generated catch block 49 e.printStackTrace(); 50 } 51 } 52 }, 1000*10); 53 } 54 private void refresh() throws SQLException { 55 //这里的key的值是从PT数据库读取而来的 oracle的链接属性的拼接值 没有全写 56 //写一个service去从pt数据库里面 定时 获取oracle的连接参数 定时任务和此处的定时器一致 57 String key="username+password+url"; 58 //如果最新的连接池属性拼接参数key和上一次的key不相同 则销毁上次的委托对象并创建新的数据库连接 如果相同者不做处理 59 if(this.key.equals(key)) { 60 if(target!=null) { 61 target.close(); 62 } 63 //当数据库链接不同的时候才会重新给key添加新的引用 64 this.key=key; 65 target=createDataSource(); 66 } 67 } 68 private DruidDataSource createDataSource() throws SQLException { 69 70 //在这里设置连接池的属性 同样没有写全 71 DruidDataSource druid=new DruidDataSource(); 72 druid.setUrl("url"); 73 //阿里连接池的自行初始化 如果不初始化 代理的连接池对象的属性为空 74 druid.init(); 75 return druid; 76 } 77 78 private MethodInterceptor CallBacks() { 79 return new MethodInterceptor() { 80 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 81 // TODO Auto-generated method stub 82 //在这里执行被代理对象的操作 83 Object result=proxy.invokeSuper(target, args); 84 //在这里执行被代理对象的操作 85 return result; 86 } 87 }; 88 } 89 public DataSource getObject() throws Exception { 90 // TODO Auto-generated method stub 91 return proxy; 92 } 93 public Class<?> getObjectType() { 94 // TODO Auto-generated method stub 95 return null; 96 } 97 public boolean isSingleton() { 98 // TODO Auto-generated method stub 99 return true; 100 } 101 102 }
这里实现了 FactoryBean<DataSource> 为什么要实现FactoryBean 主要是为了在xml做的ref引用时获得DataSource代理对象
基于JDK实现
1 package com.manager.aop; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 import java.sql.SQLException; 7 import java.util.concurrent.ScheduledFuture; 8 9 import javax.annotation.PostConstruct; 10 import javax.sql.DataSource; 11 12 import org.springframework.beans.factory.FactoryBean; 13 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 14 import org.springframework.util.ClassUtils; 15 16 import com.alibaba.druid.pool.DruidDataSource; 17 18 19 public class JdkProxy implements FactoryBean<DataSource> { 20 private DruidDataSource target; 21 private DataSource proxy; 22 23 24 //这两个类是线程池的类用来做定时器任务 25 private ThreadPoolTaskScheduler scheduler; 26 private ScheduledFuture<?> future; 27 28 29 //存储每次刷新的上一次的数据库的链接属性,以便和最新的数据库链接对比 30 private String key; 31 @PostConstruct 32 private void init() throws SQLException { 33 //项目启动后 第一次实例化连接池 34 target=createDataSource(); 35 //生成Druid链接池的代理对象 36 proxy=(DataSource) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), //获取类加载器 37 DataSource.class.getInterfaces(), //获取DataSource所实现的接口 38 handler()); 39 //判断是否创建新的代理对象 40 refresh(); 41 future=scheduler.scheduleWithFixedDelay(new Runnable() { 42 public void run() { 43 // TODO Auto-generated method stub 44 try { 45 refresh(); 46 } catch (SQLException e) { 47 // TODO Auto-generated catch block 48 e.printStackTrace(); 49 } 50 } 51 }, 1000*10); 52 } 53 private void refresh() throws SQLException { 54 //这里的key的值是从PT数据库读取而来的 oracle的链接属性的拼接值 没有全写 55 String key="username+password+url"; 56 //如果最新的连接池属性拼接参数key和上一次的key不相同 则销毁上次的委托对象并创建新的数据库连接 如果相同者不做处理 57 if(this.key.equals(key)) { 58 if(target!=null) { 59 target.close(); 60 } 61 //当数据库链接不同的时候才会重新给key添加新的引用 62 this.key=key; 63 target=createDataSource(); 64 } 65 } 66 private DruidDataSource createDataSource() throws SQLException { 67 68 //在这里设置连接池的属性 同样没有写全 69 DruidDataSource druid=new DruidDataSource(); 70 druid.setUrl("url"); 71 //阿里连接池的自行初始化 如果不初始化 代理的连接池对象的属性为空 72 druid.init(); 73 return druid; 74 } 75 76 private InvocationHandler handler() { 77 return new InvocationHandler() { 78 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 79 // TODO Auto-generated method stub 80 Object obj=method.invoke(target, args); 81 return obj; 82 } 83 }; 84 } 85 public DataSource getObject() throws Exception { 86 // TODO Auto-generated method stub 87 return proxy; 88 } 89 public Class<?> getObjectType() { 90 // TODO Auto-generated method stub 91 return null; 92 } 93 public boolean isSingleton() { 94 // TODO Auto-generated method stub 95 return true; 96 } 97 }
再来看xml如何到底是怎么配置的
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 6 xmlns:util="http://www.springframework.org/schema/util" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 8 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd 9 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd 10 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> 11 <!-- 配置dao层扫描 --> 12 <context:property-placeholder location="classpath:conf/database.properties" /> 13 <!-- 通过JDBC模版获取数据库连接 --> 14 <bean scope="singleton" id="jdbcTemplate" 15 class="org.springframework.jdbc.core.JdbcTemplate"> 16 <property name="dataSource" ref="dataSource"></property> 17 </bean> 18 <!-- 数据库连接池 --> 19 //这里的class写的是我们所写的代理类的类路径 20 <bean id="dataSource" class="com.manager.aop.JdkProxy"> 21 //这里什么都可以不用写 因为我们在类中已经设置了属性 22 </bean> 23 24 <!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 --> 25 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 26 <!-- 数据库连接池 --> 27 //这里的ref引用的时候 会调用类中的getObject()方法拿到代理对象 28 <property name="dataSource" ref="dataSource" /> 29 <!-- 加载mybatis的全局配置文件 --> 30 <property name="mapperLocations" value="classpath:com/mapper/*.xml" /> 31 </bean> 32 //其他的配置没有写 只是示例在xml怎么拿到代理类所返回的代理对象供其他bean使用 33 </beans>
这里只是给出一个动态代理在项目中的应用实际情况,希望给各位同仁一点应用动态的代理的思路和技巧。单独拿出来是无法使用要配合实际的项目背景来调试和使用
如有错误和理解性的错误,请及时指出 大家一起学习。
以上是关于动态代理实现多数据库的定时刷新链接信息的应用的主要内容,如果未能解决你的问题,请参考以下文章