使用spring 实现真正多数据源的动态加载及动态切换
1 前言:
公司需要做一个分析统计系统,该系统需要连接N台服务器结点,进行数据的统计分析操作,
项目是以spring为基础框架搭建的.收集现在网上的所有关于多数据源配置的方式,并没有自己十分满意的,例如我有N个数据源,按照现网可以搜索到的配置方式,都是在spring配置文件中配置N个datasource,并通过实现AbstractRoutingDataSource抽象类的子类进行多数据源的管理.这种情况个人认为很不合理,一来维护起来困难,二来,数据源的基本信息基本都一致的情况下,会造成配置文件重复性的文字.(比如:初始化连接数,最小连接数,最大连接数,等等通用的信息.)
而配置AbstractRoutingDataSource的子类必须进行targetDataSources属性的初始化,这也决定了如上所说的情况,如果有N个数据源的情况,会让配置文件显得非常冗长,也容易侵染其他业务bean配置.原因请看代码:
AbstractRoutingDataSource.java
public void afterPropertiesSet() {if (this.targetDataSources == null) {throw new IllegalArgumentException("Property 'targetDataSources' is required");}this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());for (Map.Entry entry : this.targetDataSources.entrySet()) {Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());this.resolvedDataSources.put(lookupKey, dataSource);}if (this.defaultTargetDataSource != null) {this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);}}
//查找当前用户上下文变量中设置的数据源.@Overrideprotected Object determineCurrentLookupKey() {DataSourceType dataSourceType= DataSourceContextHolder.getDataSourceType();return dataSourceType;}//设置默认的数据源@Overridepublic void setDefaultTargetDataSource(Object defaultTargetDataSource) {super.setDefaultTargetDataSource(defaultTargetDataSource);}//设置数据源集合.@Overridepublic void setTargetDataSources(Map targetDataSources) {super.setTargetDataSources(targetDataSources);}
/***数据源线程上下文对象.**/public class DataSourceContextHolder {private static final ThreadLocal contextHolder=new ThreadLocal();public static void setDataSourceType(DataSourceType dataSourceType){contextHolder.set(dataSourceType);}public static DataSourceType getDataSourceType(){return (DataSourceType) contextHolder.get();}public static void clearDataSourceType(){contextHolder.remove();}}
/** * 初始化动态数据源 * @author Administrator * *//** * 初始化动态数据源 * @author Administrator * */@Component("mutiGameDs")public class MutiDataSourceBean extends AbstractRoutingDataSource implements ApplicationContextAware{private static Logger log = Logger.getLogger("InistailizeMutiDataSourceBean");private static ApplicationContext ac ;@Overridepublic void afterPropertiesSet() {log.info("初始化多数据源");try {initailizeMutiDataSource();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}log.info("多数据源加入spring容器中成功!");super.afterPropertiesSet();}@Overridepublic void setApplicationContext(ApplicationContext ctx)throws BeansException {// TODO Auto-generated method stubac=ctx;}private void initailizeMutiDataSource() throws Exception{Document doc = XmlUtils.loadXMLClassPath("game-ds.xml");List servers = doc.selectNodes("//server");DruidDataSource ds = null;............ DefaultListableBeanFactory acf = (DefaultListableBeanFactory)ac.getAutowireCapableBeanFactory(); Map<Object,DruidDataSource> dsMap = new HashMap<Object, DruidDataSource>(); for (Object object : servers) {Element el =(Element)object; ds = new DruidDataSource();String id = el.attributeValue("id");String username = el.attributeValue("username");String url = el.attributeValue("url");String pwd = el.attributeValue("pwd");ds.setUsername(username);ds.setUrl(url);ds.setPassword(pwd);ds.setInitialSize( Integer.valueOf(initialSize));ds.setMaxActive(Integer.valueOf(maxActive));ds.setMinIdle(Integer.valueOf(minIdle));ds.setMaxWait(Integer.valueOf(maxWait));ds.setTestOnBorrow(testOnBorrow.equals("true")?true:false);ds.setTestOnReturn(testOnReturn.equals("true")?true:false);ds.setTestWhileIdle(testWhileIdle.equals("true")?true:false);ds.setTimeBetweenEvictionRunsMillis(Long.valueOf(timeBetweenEvictionRunsMillis));ds.setMinEvictableIdleTimeMillis(Long.valueOf(minEvictableIdleTimeMillis));ds.setRemoveAbandoned(removeAbandoned.equals("true")?true:false);ds.setRemoveAbandonedTimeout(Integer.valueOf(removeAbandonedTimeout));ds.setLogAbandoned(logAbandoned.equals("true")?true:false);ds.setFilters(filters);acf.registerSingleton(id, ds);dsMap.put(DataSourceType.valueOf(id), ds);}this.setTargetDataSources(dsMap);setDefaultTargetDataSource(dsMap.get("game_server_1"));//设置默认数据源}@Overrideprotected Object determineCurrentLookupKey() {DataSourceType dataSourceType= DataSourceContextHolder.getDataSourceType();return dataSourceType;}@Overridepublic void setDataSourceLookup(DataSourceLookup dataSourceLookup) {super.setDataSourceLookup(dataSourceLookup);}@Overridepublic void setDefaultTargetDataSource(Object defaultTargetDataSource) {super.setDefaultTargetDataSource(defaultTargetDataSource);}@Overridepublic void setTargetDataSources(Map targetDataSources) {super.setTargetDataSources(targetDataSources);}}
public class TestMutiDataSource extends SpringTxTestCase{@Autowired@Qualifier("jdbcTemplate4Game")JdbcTemplate jt;@Testpublic void test() {DataSourceContextHolder.setDataSourceType(DataSourceType.game_server_1);List<Map<String, Object>> list = jt.queryForList("select* from fs_accountlog.t_accountlogin");for (Map<String, Object> map : list) {System.out.println(map);}}}