动态代理实现多数据库的定时刷新链接信息的应用
程序员文章站
2023-01-16 21:31:01
开始研究动态代理之前 先简要谈下动态代理的概念 在不改变原有类结构的前提下增强类的功能以及对类原有方法操作,注意是方法不是属性(属性一般被设计为private修饰,不可以直接被调用) 动态代理的基本实例不做阐述,网上一大把 不理解的同学可以直接去搜索。 今天说的是自己在项目中遇到的一个实际的动态代理 ......
开始研究动态代理之前 先简要谈下动态代理的概念
在不改变原有类结构的前提下增强类的功能以及对类原有方法操作,注意是方法不是属性(属性一般被设计为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>
这里只是给出一个动态代理在项目中的应用实际情况,希望给各位同仁一点应用动态的代理的思路和技巧。单独拿出来是无法使用要配合实际的项目背景来调试和使用
如有错误和理解性的错误,请及时指出 大家一起学习。
上一篇: 他是康熙最信任的大臣,怎么成了清朝罪人?
下一篇: 不是真见鬼了吧