欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

如何在spring框架中解決多數據源的問題

系統 1618 0

在我們的項目中遇到這樣一個問題:我們的項目需要連接多個數據庫,而且不同的客戶在每次訪問中根據需要會去訪問不同的數據庫。我們以往在 spring hibernate 框架中總是配置一個數據源,因而 sessionFactory dataSource 屬性總是指向這個數據源并且恒定不變,所有 DAO 在使用 sessionFactory 的時候都是通過這個數據源訪問數據庫。但是現在,由于項目的需要,我們的 DAO 在訪問 sessionFactory 的時候都不得不在多個數據源中不斷切換,問題就出現了:如何讓 sessionFactory 在執行數據持久化的時候,根據客戶的需求能夠動態切換不同的數據源?我們能不能在 spring 的框架下通過少量修改得到解決?是否有什么設計模式可以利用呢? ?

?

問題的分析

我首先想到在 spring applicationContext 中配置所有的 dataSource 。這些 dataSource 可能是各種不同類型的,比如不同的數據庫: Oracle SQL Server 、 MySQL 等,也可能是不同的數據源:比如 apache 提供的 org.apache.commons.dbcp.BasicDataSource 、 spring 提供的 org.springframework.jndi.JndiObjectFactoryBean 等。然后 sessionFactory 根據客戶的每次請求,將 dataSource 屬性設置成不同的數據源,以到達切換數據源的目的。

?

但是,我很快發現一個問題:當多用戶同時并發訪問數據庫的時候會出現資源爭用的問題。這都是“單例模式”惹的禍。眾所周知,我們在使用 spring 框架的時候,在 beanFactory 中注冊的 bean 基本上都是采用單例模式,即 spring 在啟動的時候,這些 bean 就裝載到內存中,并且每個 bean 在整個項目中只存在一個對象。正因為只存在一個對象,對象的所有屬性,更準確說是實例變量,表現得就如同是個靜態變量(實際上“靜態”與“單例”往往是非常相似的兩個東西,我們常常用“靜態”來實現“單例”)。拿我們的問題來說, sessionFactory 在整個項目中只有一個對象,它的實例變量 dataSource 也就只有一個,就如同一個靜態變量一般。如果不同的用戶都不斷地去修改 dataSource 的值,必然會出現多用戶爭用一個變量的問題,對系統產生隱患。

?

通過以上的分析,解決多數據源訪問問題的關鍵,就集中在 sessionFactory 在執行數據持久化的時候,能夠通過某段代碼去根據客戶的需要動態切換數據源,并解決資源爭用的問題。

?

問題的解決

  • 采用 Decorator 設計模式

要解決這個問題,我的思路鎖定在了這個 dataSource 上了。如果 sessionFactory 指向的 dataSource 可以根據客戶的需求去連接客戶所需要的真正的數據源,即提供動態切換數據源的功能,那么問題就解決了。那么我們怎么做呢?去修改那些我們要使用的 dataSource 源碼嗎?這顯然不是一個好的方案,我們希望我們的修改與原 dataSource 代碼是分離的。根據以上的分析,使用 GoF 設計模式中的 Decorator 模式(裝飾者模式)應當是我們可以選擇的最佳方案。

?

什么是“ Decorator 模式”?簡單點兒說就是當我們需要修改原有的功能,但我們又不愿直接去修改原有的代碼時,設計一個 Decorator 套在原有代碼外面。當我們使用 Decorator 的時候與原類完全一樣,當 Decorator 的某些功能卻已經修改為了我們需要修改的功能。 Decorator 模式的結構如圖。

?

?

我們本來需要修改圖中所有具體的 Component 類的一些功能,但卻并不是去直接修改它們的代碼,而是在它們的外面增加一個 Decorator 。 Decorator 與具體的 Component 類都是繼承的 AbstractComponent ,因此它長得和具體的 Component 類一樣,也就是說我們在使用 Decorator 的時候就如同在使用 ConcreteComponentA 或者 ConcreteComponentB 一樣,甚至那些使用 ConcreteComponentA 或者 ConcreteComponentB 的客戶程序都不知道它們用的類已經改為了 Decorator ,但是 Decorator 已經對具體的 Component 類的部分方法進行了修改,執行這些方法的結果已經不同了。

?

  • 設計 MultiDataSource

現在回到我們的問題,我們需要對 dataSource 的功能進行變更,但又不希望修改 dataSource 中的任何代碼。我這里指的 dataSource 是所有實現 javax.sql.DataSource 接口的類,我們常用的包括 apache 提供的 org.apache.commons.dbcp.BasicDataSource 、 spring 提供的 org.springframework.jndi.JndiObjectFactoryBean 等,這些類我們不可能修改它們本身,更不可能對它們一個個地修改以實現動態分配數據源的功能,同時,我們又希望使用 dataSource sessionFactory 根本就感覺不到這樣的變化。 Decorator 模式就正是解決這個問題的設計模式。

?

首先寫一個 Decorator 類,我取名叫 MultiDataSource,通過它來動態切換數據源 。同時在配置文件中將sessionFactory的dataSource屬性由原來的某個具體的dataSource改為MultiDataSource。如圖:

?

?

對比原 Decorator 模式, AbstractComponent 是一個抽象類,但在這里我們可以將這個抽象類用接口來代替,即 DataSource 接口,而 ConcreteComponent 就是那些 DataSource 的實現類,如 BasicDataSource 、 JndiObjectFactoryBean 等。 MultiDataSource 封裝了具體的dataSource,并實現了數據源動態切換:

?

?

java 代碼
  1. public ? class ?MultiDataSource? implements ?DataSource?{ ??
  2. ???? private ?DataSource?dataSource?=? null ; ??
  3. public ?MultiDataSource(DataSource?dataSource){ ??
  4. ???????? this .dataSource?=?dataSource; ??
  5. ????} ??
  6. ???? /*?(non-Javadoc) ?
  7. ?????*?@see?javax.sql.DataSource#getConnection() ?
  8. ?????*/ ??
  9. ???? public ?Connection?getConnection()? throws ?SQLException?{ ??
  10. ???????? return ?getDataSource().getConnection(); ??
  11. ????} ??
  12. ???? //其它DataSource接口應當實現的方法 ??
  13. ??
  14. ???? public ?DataSource?getDataSource(){ ??
  15. ???????? return ? this .dataSource; ??
  16. ????????} ??
  17. ????} ??
  18. ???? public ? void ?setDataSource(DataSource?dataSource)?{ ??
  19. ???????? this .dataSource?=?dataSource; ??
  20. ????} ??
  21. } ??

?

客戶在發出請求的時候,將dataSourceName放到request中,然后把request中的數據源名通過調用new MultiDataSource(dataSource) 時可以告訴 MultiDataSource 客戶需要的數據源,就可以實現動態切換數據源了。但細心的朋友會發現這在單例的情況下就是問題的,因為 MultiDataSource 在系統中只有一個對象,它的實例變量 dataSource 也只有一個,就如同一個靜態變量一般。正因為如此, 單例模式讓許多設計模式都不得不需要更改,這將在我的《“單例”更改了我們的設計模式》中詳細討論。那么,我們在單例模式下如何設計呢?

?

  • 單例模式下的 MultiDataSource

在單例模式下,由于我們在每次調用 MultiDataSource 的方法的時候, dataSource 都可能是不同的,所以我們不能將 dataSource 放在實例變量 dataSource 中,最簡單的方式就是在方法 getDataSource() 中增加參數,告訴 MultiDataSource 我到底調用的是哪個 dataSource

?

java 代碼
  1. public ?DataSource?getDataSource(String?dataSourceName){ ??
  2. ????????log.debug( "dataSourceName:" +dataSourceName); ??
  3. ???????? try { ??
  4. ???????????? if (dataSourceName== null ||dataSourceName.equals( "" )){ ??
  5. ???????????????? return ? this .dataSource; ??
  6. ????????????} ??
  7. ???????????? return ?(DataSource) this .applicationContext.getBean(dataSourceName); ??
  8. ????????} catch (NoSuchBeanDefinitionException?ex){ ??
  9. ???????????? throw ? new ?DaoException( "There?is?not?the?dataSource?
  10. ????????} ??
  11. ????} ??

?

值得一提的是,我需要的數據源已經都在 spring 的配置文件中注冊, dataSourceName 就是其對應的 id 。

?

xml 代碼
  1. < bean ? id = "dataSource1" ??
  2. ???? class = "org.apache.commons.dbcp.BasicDataSource" > ??
  3. ???? < property ? name = "driverClassName" > ??
  4. ???????? < value > oracle.jdbc.driver.OracleDriver value > ??
  5. ???? property > ?
  6. ????...... ??
  7. bean > ??
  8. < bean ? id = "dataSource2" ??
  9. ???? class = "org.apache.commons.dbcp.BasicDataSource" > ??
  10. ???? < property ? name = "driverClassName" > ??
  11. ???????? < value > oracle.jdbc.driver.OracleDriver value > ?
  12. ???? property > ?? ?
  13. ????...... ??
  14. bean > ?? ?

為了得到 spring ApplicationContext MultiDataSource 類必須實現接口 org.springframework.context.ApplicationContextAware ,并且實現方法:

java 代碼
  1. private ?ApplicationContext?applicationContext?=? null ; ??
  2. public ? void ?setApplicationContext(ApplicationContext?applicationContext)? throws ?BeansException?{ ??
  3. ???????? this .applicationContext?=?applicationContext; ??
  4. ????} ??

?

如此這樣,我就可以通過 this . applicationContext .getBean(dataSourceName) 得到 dataSource 了。

?

  • 通過線程傳遞 dataSourceName

查看以上設計, MultiDataSource 依然無法運行,因為用戶在發出請求時,他需要連接什么數據庫,其數據源名是放在 request 中的,要將 request 中的數據源名傳給 MultiDataSource ,需要經過 BUS DAO ,也就是說為了把數據源名傳給 MultiDataSource BUS DAO 的所有方法都要增加 dataSourceName 的參數,這是我們不愿看到的。寫一個類,通過線程的方式跳過 BUS DAO 直接傳遞給 MultiDataSource 是一個不錯的設計:

?

java 代碼
  1. public ? class ?SpObserver?{ ??
  2. ???? private ? static ?ThreadLocal?local?=? new ?ThreadLocal(); ??
  3. ???? public ? static ? void ?putSp(String?sp)?{ ??
  4. ????????local.set(sp); ??
  5. ????} ??
  6. ???? public ? static ?String?getSp()?{ ??
  7. ???????? return ?(String)local.get(); ??
  8. ????} ??
  9. } ??

?

做一個 filter ,每次客戶發出請求的時候就調用 SpObserver. petSp ( dataSourceName ) ,將 request 中的 dataSourceName 傳遞給 SpObserver 對象。 最后修改 MultiDataSource 的方法 getDataSource()

?

java 代碼
  1. public ?DataSource?getDataSource(){ ??
  2. ????????String?sp?=?SpObserver.getSp(); ??
  3. ???????? return ?getDataSource(sp); ??
  4. ????} ??

?

完整的 MultiDataSource 代碼在附件中。

?

  • 動態添加數據源

通過以上方案,我們解決了動態分配數據源的問題,但你可能提出疑問:方案中的數據源都是配置在 spring ApplicationContext 中,如果我在程序運行過程中動態添加數據源怎么辦?這確實是一個問題,而且在我們的項目中也確實遇到。 spring ApplicationContext 是在項目啟動的時候加載的。加載以后,我們如何動態地加載新的 bean ApplicationContext 中呢?我想到如果用 spring 自己的方法解決這個問題就好了。所幸的是,在查看 spring 的源代碼后,我找到了這樣的代碼,編寫了 DynamicLoadBean 類,只要 調用 loadBean() 方法,就可以將某個或某幾個配置文件中的 bean 加載到 ApplicationContext 中(見附件)。不通過配置文件直接加載對象,在 spring 的源碼中也有,感興趣的朋友可以自己研究。

?

?

  • spring 中配置

在完成了所有這些設計以后,我最后再嘮叨一句。我們應當在 spring 中做如下配置:

?

xml 代碼
  1. < bean ? id = "dynamicLoadBean" ? class = "com.htxx.service.dao.DynamicLoadBean" > bean > ??
  2. < bean ? id = "dataSource" ? class = "com.htxx.service.dao.MultiDataSource" > ??
  3. ???????? < property ? name = "dataSource" > ??
  4. ???????????? < ref ? bean = "dataSource1" ? /> ??
  5. ???????? property > ??
  6. ???? bean > ??
  7. ???? < bean ? id = "sessionFactory" ? class = "org.springframework.orm.hibernate3.LocalSessionFactoryBean" > ??
  8. ???????? < property ? name = "dataSource" > ??
  9. ???????????? < ref ? bean = "dataSource" ? /> ??
  10. ???????? property > ??
  11. ????????...... ??
  12. ???? bean > ??

?

其中 dataSource 屬性實際上更準確地說應當是 defaultDataSource ,即 spring 啟動時以及在客戶沒有指定數據源時應當指定的默認數據源。

?

該方案的優勢

?

以上方案與其它方案相比,它有哪些優勢呢?

?

首先,這個方案完全是在 spring 的框架下解決的,數據源依然配置在 spring 的配置文件中, sessionFactory 依然去配置它的 dataSource 屬性,它甚至都不知道 dataSource 的改變。唯一不同的是在真正的 dataSource sessionFactory 之間增加了一個 MultiDataSource

?

其次,實現簡單,易于維護。這個方案雖然我說了這么多東西,其實都是分析,真正需要我們寫的代碼就只有 MultiDataSource 、 SpObserver 兩個類。 MultiDataSource 類真正要寫的只有 getDataSource() getDataSource(sp) 兩個方法,而 SpObserver 類更簡單了。實現越簡單,出錯的可能就越小,維護性就越高。

?

最后,這個方案可以使單數據源與多數據源兼容。這個方案完全不影響 BUS DAO 的編寫。如果我們的項目在開始之初是單數據源的情況下開發,隨著項目的進行,需要變更為多數據源,則只需要修改 spring 配置,并少量修改 MVC 層以便在請求中寫入需要的數據源名,變更就完成了。如果我們的項目希望改回單數據源,則只需要簡單修改配置文件。這樣,為我們的項目將增加更多的彈性。

?

特別說明:實例中的DynamicLoadBean在web環境下運行會出錯,需要將類中AbstractApplicationContext改為org.springframework.context.ConfigurableApplicationContext。

?

相關博客: 再析在spring框架中解決多數據源的問題

?

如何在spring框架中解決多數據源的問題


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 亚洲视频在线观看免费 | 91久久久久久久久 | 最新一级毛片 | 欧美日韩国产综合视频在线看 | 激情毛片 | 国产高清美女一级毛片 | 一级黄色片武则天 | 日本不卡在线一区二区三区视频 | 日韩在线观看中文 | 国产一级一区 | 久久观看免费视频 | 欧美综合视频在线 | www.亚洲 | 亚洲 欧美 另类 综合 偷拍 | 亚洲人成亚洲人成在线观看 | 欧美高清一级片 | 精品一区二区三区不卡 | 夜夜爽99久久国产综合精品女不卡 | 国产成人无码AA片免费看 | 久草在线视频在线 | 涩涩久久| 日韩经典欧美一区二区三区 | jizz亚洲大全 | 久久999 | 久久久久久国产精品mv | 久久精品国产视频 | 中文字幕专区 | 国产欧美在线观看视频 | 欧美顶级xxxxbbbb | 中文字幕 国产 | 日韩美女一区二区三区在线观看 | 久久亚洲精品玖玖玖玖 | 男人天堂网av | 亚洲午夜精品A片久久不卡蜜桃 | 国产亚洲精品久久久久久一区二区 | 亚洲日本久久久午夜精品 | 久热国产在线视频 | 91色综合 | 天天天天天天天操 | 成人午夜爽爽爽免费视频 | 泰国一级毛片aaa下面毛多 |