動態代理實現多資料庫的定時刷新鏈接信息的應用

来源:https://www.cnblogs.com/tongAngle/archive/2019/01/19/10292864.html
-Advertisement-
Play Games

開始研究動態代理之前 先簡要談下動態代理的概念 在不改變原有類結構的前提下增強類的功能以及對類原有方法操作,註意是方法不是屬性(屬性一般被設計為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>

這裡只是給出一個動態代理在項目中的應用實際情況,希望給各位同仁一點應用動態的代理的思路和技巧。單獨拿出來是無法使用要配合實際的項目背景來調試和使用

如有錯誤和理解性的錯誤,請及時指出 大家一起學習。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、安裝matplotlib 在 cmd 中鍵入 python -m pip install matplotlib,系統將自動安裝,需要等一段時間,待完成後 python -m pip list ,顯示 敲黑板劃重點:一定通過 cdm 指定具體安裝文件夾。 cd 文件夾名 可進入指定文件夾。 2、簡 ...
  • beego 框架 cache 目錄是 Go 實現的一個緩存管理器,是beego框架自帶工具之一,當然,如果你只想使用 cache 而不是整個 beego 框架,可以選擇性安裝: go get github.com/astaxie/beego/cache 在我寫這篇博文時, beego 版本是 v1. ...
  • IDE進行Gradle操作,那麼還需要設置IDE的參數。例如在IDEA中,需要打開File->Other Settings->Default Settings->Gradle,在Gradle Vm Options中設置-Dfile.encoding=utf-8。這樣IDEA中的Gradle也可以正確 ...
  • GitHub RSA密碼 RSA密碼是1978年美國麻省理工學院三位密碼學者R.L.Rivest、A.Shamir和L.Adleman提出的一種基於大合數因數分解困難性的公開密鑰密碼。由於RSA密碼既可用於加密,又可用於數字簽名,通俗易懂,因此RSA密碼已成為目前應用最廣泛的公開密鑰密碼。 RSA加 ...
  • 首先需要安裝最新的python:安裝步驟見:https://www.cnblogs.com/weven/p/7252917.html 其次下載python源碼: 鏈接:https://pan.baidu.com/s/1UZmMEjt5nc7clMtatgLwzA 提取碼:qatx 然後就開始以下步驟 ...
  • 2019-01-19 多重背包:每種東西有多個,因此可以把它拆分成多個01背包 優化:二進位拆分(拆成1+2+4+8+16+...,分別表示2的n次冪) 比如18=1+2+4+8+3,可以證明18以內的任何數都可以用這幾個數的和或差表示(每個數只能用一次)(0時則背包為空), 所以就把2個,4個.. ...
  • 最近想要做一個小東西,用到了下麵幾個中間件或者環境: Java Tomcat Maven MongoDB ZooKeeper Node 並且恰好碰到騰訊雲打折,雲主機原價100多一個月,花了30塊錢買了三個月。買下後立即動手準備開始環境配置。 說到環境,少則2小時,多則兩三天可能都要整矇蔽,環境好了 ...
  • 寫之前的說明 0. 其實吧。 這個東西已經寫好了,地址在:https://github.com/hjx601496320/JdbcPlus 這 系列 文章算是我寫的過程的總結吧。(恩系列,說明我可能會寫好久,╮(╯▽╰)╭) 1. 現在有很多的現成的orm框架,為什麼還要自己寫一個? 框架這種東西個 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...