欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

动态代理实现多数据库的定时刷新链接信息的应用

程序员文章站 2022-05-17 14:44:48
开始研究动态代理之前 先简要谈下动态代理的概念 在不改变原有类结构的前提下增强类的功能以及对类原有方法操作,注意是方法不是属性(属性一般被设计为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>

这里只是给出一个动态代理在项目中的应用实际情况,希望给各位同仁一点应用动态的代理的思路和技巧。单独拿出来是无法使用要配合实际的项目背景来调试和使用

如有错误和理解性的错误,请及时指出 大家一起学习。