Spring动态注册bean实现动态多数据源

项目原来已经实现了多数据源配置,实现方式为在beans.xml文件中直接配置多个数据源bean,然后在使用数据源时通过HotSwappableTargetSource动态切换数据源(详细内容请Google)。可领导不满意,要求只在属性文件中配置相应的连接信息,并要求动态数据源除配置的属性外,其他属性都继承系统默认数据源(DataSource)的属性。然后给出的属性文件中数据源的格式为:

#连接数据库地址,该地址可动态添加,“link.”之后,“.jdbc”之前的名称为数据库连接池的名称,其余连接池属性如果不写,将自动继承Hummer的数据库连接池配置
link.eagle2.business.jdbc.jdbcUrl=jdbc:oracle:thin:@192.168.0.155:1521:orcl
link.eagle2.business.jdbc.user=eagle2
link.eagle2.business.jdbc.password=eagle2_password
link.eagle2.interface.jdbc.jdbcUrl=jdbc:oracle:thin:@192.168.0.155:1521:interface
link.eagle2.interface.jdbc.user=interface
link.eagle2.interface.jdbc.password=interface22
link.eagle2.ods.jdbc.jdbcUrl=jdbc:oracle:thin:@192.168.0.10:1521:sifen
link.eagle2.ods.jdbc.user=honghe
link.eagle2.ods.jdbc.password=honghe_pwd

为实现这个要求,我经过google搜索,写代码尝试,最后终于较好的实现了该功能,结果记录如下:
实现一个实现ApplicationContextAware和ApplicationListener接口的类DynamicDataSourceC3p0,实现ApplicationContextAware是为了得到ApplicationContext,实现了ApplicationListener是为了配置spring的加载事件。
类的实现代码如下:

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.ChildBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.io.support.ResourcePropertySource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class DynamicDataSourceC3p0 implements ApplicationContextAware,ApplicationListener {

	private ApplicationContext app;
	@Override
	public void setApplicationContext(ApplicationContext app)
			throws BeansException {
		this.app = app;
	}
	public void onApplicationEvent(ApplicationEvent event) {  
		//如果是容器刷新事件  
		if(event instanceof ContextRefreshedEvent ){//如果是容器关闭事件  
			try {
				regDynamicBean();
			} catch (IOException e) {
				e.printStackTrace();
			}
			//System.out.println(event.getClass().getSimpleName()+" 事件已发生!");
		}
	}  

	private void regDynamicBean() throws IOException{
		// 解析属性文件,得到数据源Map
		Map<String, DataSourceInfo> mapCustom = parsePropertiesFile("hummer.properties");
		// 把数据源bean注册到容器中
		addSourceBeanToApp(mapCustom);
		
	}
	/**
	 * 功能说明:根据DataSource创建bean并注册到容器中
	 * @param acf
	 * @param mapCustom
	 */
	private void addSourceBeanToApp(Map<String, DataSourceInfo> mapCustom) {
		DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app.getAutowireCapableBeanFactory();
		BeanDefinition beanDefinition;
		Iterator<String> iter = mapCustom.keySet().iterator();
		while(iter.hasNext()){
			String beanKey = iter.next();
			// 得到Bean定义,并添加到容器中
			beanDefinition = new ChildBeanDefinition("dataSource");
			// 注意:必须先注册到容器中,再得到Bean进行修改,否则数据源属性不能有效修改
			acf.registerBeanDefinition(beanKey, beanDefinition);
			// 再得到数据源Bean定义,并修改连接相关的属性
			ComboPooledDataSource cpds = (ComboPooledDataSource)app.getBean( beanKey);;

			cpds.setJdbcUrl(mapCustom.get(beanKey).connUrl);
			cpds.setUser(mapCustom.get(beanKey).userName);
			cpds.setPassword(mapCustom.get(beanKey).password);
		}
	}
	/**
	 * 功能说明:解析属性文件,得到数据源Map
	 * @return
	 * @throws IOException
	 */
	private Map<String, DataSourceInfo> parsePropertiesFile(String fileName) throws IOException {
		// 属性文件
		ResourcePropertySource props =  new ResourcePropertySource(fileName);
		
		Matcher matcher;
		Pattern pattern = Pattern.compile("^link\\.(eagle2\\.\\w+)\\.jdbc\\.(jdbcUrl|user|password)$");
		
		Map<String, DataSourceInfo> mapDataSource = new HashMap<String,DataSourceInfo>();
		// 根据配置文件解析数据源
		for(String keyProp : props.getPropertyNames())
		{
			matcher = pattern.matcher(keyProp);
			if(matcher.find()){
				String dsName = matcher.group(1);
				String dsPropName = matcher.group(2);
				DataSourceInfo dsi;
				
				if(mapDataSource.containsKey(dsName)){
					dsi = mapDataSource.get(dsName);
				}
				else{
					dsi = new DataSourceInfo();
				}
				// 根据属性名给数据源属性赋值
				if("jdbcUrl".equals(dsPropName)){
					dsi.connUrl = (String)props.getProperty(keyProp);
				}else if("user".equals(dsPropName)){
					dsi.userName = (String)props.getProperty(keyProp);
				}else if("password".equals(dsPropName)){
					dsi.password = (String)props.getProperty(keyProp);
				}
				mapDataSource.put(dsName, dsi);
			}
		}
		return mapDataSource;
	}
	
	private class DataSourceInfo{
		public String connUrl;
		public String userName;
		public String password;
		
		public String toString(){
			
			return "(JcbcUrl:"+connUrl+", user:"+userName+", password:"+password+")";
		}
	}
}

然后在beans.xml文件中添加配置
"ApplicationEventListener" class="com.dfsoft.hummer.domain.common.DynamicDataSourceC3p0" />
让spring在加载时调用DynamicDataSourceC3p0中的onApplicationEvent方法实现动态注册数据源。而原来实现多数据源动态切换的地方完全不用修改。
附加说明:
1、 做完上面的工作之后,在beans.xml中去掉原来的多数据源配置(已经没有用了)。
2、 acf.registerBeanDefinition不用判断容器中是否已经有相应名字的bean,spring会在该名称的bean存在时覆盖,不存在时添加。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值