Spring动态创建bean切换数据源

应用场景:查询业务为主,多个数据源,根据不同的请求,切换到不同的数据源;

 

1:创建DynamicDataSource

扩展一个Spring提供的AbstractRoutingDataSource,Override 其中的 determineCurrentLookupKey方法实现数据源的路由

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. import java.sql.SQLException;  
  2.   
  3. import javax.sql.DataSource;  
  4.   
  5. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
  6. import org.springframework.util.Assert;  
  7.   
  8. /** 
  9.  * Dynamic DataSource   
  10.  * 
  11.  */  
  12. public class DynamicDataSource  extends AbstractRoutingDataSource {  
  13.   
  14.     //@Override  
  15.     protected Object determineCurrentLookupKey() {  
  16.         return CustomerContextHolder.getCustomerType();    
  17.     }  
  18.   
  19. }  

2:创建CustomerContextHolder

CustomerContextHolder这是开发人员自己实现的一个封装了ThreadLocal类型的ContextHolder。


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class CustomerContextHolder {         
  2.     private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();    
  3.         
  4.     public static void setCustomerType(String customerType) {    
  5.         contextHolder.set(customerType);    
  6.     }    
  7.         
  8.     public static String getCustomerType() {    
  9.         return contextHolder.get();    
  10.     }    
  11.         
  12.     public static void clearCustomerType() {    
  13.         contextHolder.remove();    
  14.     }    
  15. }    

 

3:bean.xml

xml 中有一个默认的数据源,多数据库的信息在默认数据源表中存放着;

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.    <bean id="dataSource"  
  2.     class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" >  
  3.     <property name="driverClassName">  
  4.         <value>org.gjt.mm.mysql.Driver</value>  
  5.     </property>  
  6.     <property name="url">  
  7.         <value>jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf-8</value>  
  8.     </property>  
  9.     <property name="username">  
  10.         <value>root</value>  
  11.     </property>  
  12.     <property name="password">  
  13.         <value>root</value>  
  14.     </property>  
  15.     <property name="timeBetweenEvictionRunsMillis">  
  16.         <value>3600000</value>  
  17.     </property>  
  18.     <property name="minEvictableIdleTimeMillis">  
  19.         <value>3600000</value>  
  20.     </property>         
  21. </bean>  
  22.   
  23. <!-- S  -->  
  24.    <bean id="dynamicDataSource" class="common.DynamicDataSource" >    
  25.     <property name="targetDataSources">    
  26.         <map key-type="java.lang.String">   
  27.             <!-- 动态初始化map-->    
  28.         </map>    
  29.     </property>    
  30.     <property name="defaultTargetDataSource" ref="dataSource" >    
  31.     </property>    
  32. </bean>   
  33.    <!--  E -->  
  34.   
  35. <bean id="jdbcTemplate"  class="org.springframework.jdbc.core.JdbcTemplate" >  
  36.     <property name="dataSource" ref="dynamicDataSource" ></property>  
  37. </bean>  
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.    

 启动时,需要用到默认数据源资源,从其表中得到数据,从而创建新的数据源bean

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <bean id="applicationEventListener"  class="common.DynamicCreateDataSourceBean">  
  2.         <property name="dynamicDataSource" ref="dynamicDataSource" ></property>  
  3.         <property name="jdbcTemplate" ref="jdbcTemplate" ></property>  
  4.     </bean >  

 

4:DynamicCreateDataSourceBean

动态创建datasourcebean

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. import java.io.IOException;  
  2. import java.util.Iterator;  
  3. import java.util.LinkedHashMap;  
  4. import java.util.Map;  
  5.   
  6. import javax.sql.DataSource;  
  7.   
  8. import org.springframework.aop.target.HotSwappableTargetSource;  
  9. import org.springframework.beans.BeansException;  
  10. import org.springframework.beans.factory.config.BeanDefinition;  
  11. import org.springframework.beans.factory.support.BeanDefinitionBuilder;  
  12. import org.springframework.beans.factory.support.ChildBeanDefinition;  
  13. import org.springframework.beans.factory.support.DefaultListableBeanFactory;  
  14. import org.springframework.context.ApplicationContext;  
  15. import org.springframework.context.ApplicationContextAware;  
  16. import org.springframework.context.ApplicationEvent;  
  17. import org.springframework.context.ApplicationListener;  
  18. import org.springframework.context.ConfigurableApplicationContext;  
  19. import org.springframework.context.event.ContextRefreshedEvent;  
  20.   
  21.   
  22. /** 
  23.  *  实现一个实现ApplicationContextAware和ApplicationListener接口的类DynamicDataSourceC3p0, 
  24.  * 实现ApplicationContextAware是为了得到ApplicationContext, 
  25.  * 实现了ApplicationListener是为了配置spring的加载事件。 
  26.  * 
  27.  */  
  28. public class DynamicCreateDataSourceBean implements ApplicationContextAware,ApplicationListener{  
  29.   
  30.     private ConfigurableApplicationContext app;  
  31.   
  32.     private JdbcTemplate jdbcTemplate;  
  33.       
  34.     public void setJdbcTemplate(JdbcTemplate jdbcTemplate)  
  35.     {  
  36.         this.jdbcTemplate = jdbcTemplate;  
  37.     }  
  38.   
  39.     private DynamicDataSource dynamicDataSource;  
  40.       
  41.     public void setDynamicDataSource(DynamicDataSource dynamicDataSource) {  
  42.         this.dynamicDataSource = dynamicDataSource;  
  43.     }  
  44.   
  45.     public void setApplicationContext(ApplicationContext app) throws BeansException {  
  46.         this.app = (ConfigurableApplicationContext)app;  
  47.     }  
  48.   
  49.       
  50.     public void onApplicationEvent(ApplicationEvent event) {  
  51.         // 如果是容器刷新事件OR Start Event  
  52.         if (event instanceof ContextRefreshedEvent) {  
  53.             try {  
  54.                 regDynamicBean();  
  55.             } catch (IOException e) {  
  56.                 e.printStackTrace();  
  57.             }  
  58.             //System.out.println(event.getClass().getSimpleName()+" 事件已发生!");  
  59.         }  
  60.           
  61.     }  
  62.   
  63.     private void regDynamicBean() throws IOException {  
  64.         // 解析属性文件,得到数据源Map  
  65.         Map<String, DataSourceInfo> mapCustom = parsePropertiesFile();  
  66.         // 把数据源bean注册到容器中  
  67.         addSourceBeanToApp(mapCustom);  
  68.     }  
  69.   
  70.     /** 
  71.      * 功能说明:根据DataSource创建bean并注册到容器中 
  72.      *  
  73.      * @param acf 
  74.      * @param mapCustom 
  75.      */  
  76.     private void addSourceBeanToApp(Map<String, DataSourceInfo> mapCustom) {  
  77.         DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app  
  78.                 .getAutowireCapableBeanFactory();  
  79.   
  80.         String DATASOURCE_BEAN_CLASS = "org.apache.commons.dbcp.BasicDataSource";  
  81.         BeanDefinitionBuilder bdb;  
  82.           
  83.         Iterator<String> iter = mapCustom.keySet().iterator();  
  84.           
  85.         Map<String,Object> targetDataSources = new LinkedHashMap<String, Object>();  
  86.           
  87.         baseBeanComm = new ChildBeanDefinition("dataSource");  
  88.         //  将默认数据源放入 targetDataSources map中  
  89.         targetDataSources.put("dataSourceA", app.getBean("dataSource"));  
  90.   
  91.         // 根据数据源得到数据,动态创建数据源bean 并将bean注册到applicationContext中去  
  92.         while (iter.hasNext()) {  
  93.               
  94.             //  bean ID  
  95.             String beanKey = iter.next();  
  96.             //  创建bean  
  97.             bdb = BeanDefinitionBuilder.rootBeanDefinition(DATASOURCE_BEAN_CLASS);  
  98.             bdb.getBeanDefinition().setAttribute("id", beanKey);  
  99.             bdb.addPropertyValue("driverClassName""org.gjt.mm.mysql.Driver");  
  100.             bdb.addPropertyValue("url", mapCustom.get(beanKey).connUrl);  
  101.             bdb.addPropertyValue("username", mapCustom.get(beanKey).userName);  
  102.             bdb.addPropertyValue("password", mapCustom.get(beanKey).password);  
  103.             bdb.addPropertyValue("timeBetweenEvictionRunsMillis"3600000);  
  104.             bdb.addPropertyValue("minEvictableIdleTimeMillis"3600000);  
  105.             //  注册bean  
  106.             acf.registerBeanDefinition(beanKey, bdb.getBeanDefinition());  
  107.               
  108.             //  放入map中,注意一定是刚才创建bean对象  
  109.             targetDataSources.put(beanKey, app.getBean(beanKey));  
  110.               
  111.         }  
  112.         //  将创建的map对象set到 targetDataSources;  
  113.         dynamicDataSource.setTargetDataSources(targetDataSources);  
  114.           
  115.         //  必须执行此操作,才会重新初始化AbstractRoutingDataSource 中的 resolvedDataSources,也只有这样,动态切换才会起效  
  116.         dynamicDataSource.afterPropertiesSet();  
  117.           
  118.     }  
  119.   
  120.     /** 
  121.      * 功能说明:GET ALL SM_STATIONS FROM DB1 
  122.      *  
  123.      * @return 
  124.      * @throws IOException 
  125.      */  
  126.     private Map<String, DataSourceInfo> parsePropertiesFile(String fileName)  
  127.             throws IOException {  
  128.           
  129.         String sql = "SELECT STATION_ID,CENTER_DB_NAME FROM SM_STATION ";  
  130.           
  131.         List list = jdbcTemplate.queryForList(sql);  
  132.         Iterator iterator = list.iterator();  
  133.         Map<String, DataSourceInfo> mds = new HashMap<String, DataSourceInfo>();  
  134.         while (iterator.hasNext()) {  
  135.               
  136.             Map map4station = (Map) iterator.next();  
  137.             DataSourceInfo dsi  = new DataSourceInfo();    
  138.   
  139.             String username = "root";  
  140.             String password = "root";  
  141.             String url_1 = "jdbc:mysql://127.0.0.1:3306/";  
  142.             String dbName = map4station.get("CENTER_DB_NAME")+"";  
  143.             String url_2 = "?useUnicode=true&characterEncoding=utf-8";  
  144.               
  145.               
  146.             String key = "dataSource_";  
  147.             String sid = map4station.get("STATION_ID")+"";  
  148.             key = key.concat(sid);            
  149.               
  150.             String url = url_1.concat(dbName).concat(url2);  
  151.   
  152.             dsi.connUrl = url;  
  153.             dsi.userName = username;  
  154.             dsi.password = password;  
  155.             mds.put(key, dsi);  
  156.         }  
  157.   
  158.         return mds;  
  159.     }  
  160.   
  161.     //  自定义数据结构  
  162.     private class DataSourceInfo{    
  163.   
  164.         public String connUrl;    
  165.         public String userName;    
  166.         public String password;    
  167.             
  168.         public String toString() {  
  169.             return "(url:" + connUrl + ", username:" + userName + ", password:"  
  170.                 + password + ")";  
  171.         }   
  172.     }    
  173.   
  174. }  


5:如何切换

创建过滤器,得到页面传过来的切换标示,直接根据切换标示切换数据源即可;而实际后台的业务不需要做任何修改;

CustomerContextHolder.setCustomerType(beanKey);

//beanKey 就是我们创建bean 的ID 值;



参考:http://lyunabc.iteye.com/blog/1544423 --动态切换写死在配置文件的数据源;

http://blog.csdn.net/littlechang/article/details/8071882--动态创建bean;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值