spring 动态创建bean

6 篇文章 0 订阅
2 篇文章 0 订阅

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

 

1:创建DynamicDataSource

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

import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.util.Assert;

/**
 * Dynamic DataSource  
 *
 */
public class DynamicDataSource  extends AbstractRoutingDataSource {

	//@Override
	protected Object determineCurrentLookupKey() {
		return CustomerContextHolder.getCustomerType();  
	}

}

2:创建CustomerContextHolder

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


public class CustomerContextHolder {       
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  
      
    public static void setCustomerType(String customerType) {  
        contextHolder.set(customerType);  
    }  
      
    public static String getCustomerType() {  
        return contextHolder.get();  
    }  
      
    public static void clearCustomerType() {  
        contextHolder.remove();  
    }  
}  

 

3:bean.xml

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

    <bean id="dataSource"
		class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" >
		<property name="driverClassName">
			<value>org.gjt.mm.mysql.Driver</value>
		</property>
		<property name="url">
			<value>jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf-8</value>
		</property>
		<property name="username">
			<value>root</value>
		</property>
		<property name="password">
			<value>root</value>
		</property>
		<property name="timeBetweenEvictionRunsMillis">
			<value>3600000</value>
		</property>
		<property name="minEvictableIdleTimeMillis">
			<value>3600000</value>
		</property>		
	</bean>
	
	<!-- S  -->
    <bean id="dynamicDataSource" class="common.DynamicDataSource" >  
	    <property name="targetDataSources">  
	        <map key-type="java.lang.String"> 
	        	<!-- 动态初始化map-->  
	        </map>  
	    </property>  
	    <property name="defaultTargetDataSource" ref="dataSource" >  
	    </property>  
	</bean> 
    <!--  E -->
	
	<bean id="jdbcTemplate"  class="org.springframework.jdbc.core.JdbcTemplate" >
		<property name="dataSource" ref="dynamicDataSource" ></property>
	</bean>
 

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

<bean id="applicationEventListener"  class="common.DynamicCreateDataSourceBean">
		<property name="dynamicDataSource" ref="dynamicDataSource" ></property>
		<property name="jdbcTemplate" ref="jdbcTemplate" ></property>
	</bean >

 

4:DynamicCreateDataSourceBean

动态创建datasourcebean

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.aop.target.HotSwappableTargetSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
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.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;


/**
 *  实现一个实现ApplicationContextAware和ApplicationListener接口的类DynamicDataSourceC3p0,
 * 实现ApplicationContextAware是为了得到ApplicationContext,
 * 实现了ApplicationListener是为了配置spring的加载事件。
 *
 */
public class DynamicCreateDataSourceBean implements ApplicationContextAware,ApplicationListener{

	private ConfigurableApplicationContext app;

	private JdbcTemplate jdbcTemplate;
	
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate)
	{
		this.jdbcTemplate = jdbcTemplate;
	}

	private DynamicDataSource dynamicDataSource;
	
	public void setDynamicDataSource(DynamicDataSource dynamicDataSource) {
		this.dynamicDataSource = dynamicDataSource;
	}

	public void setApplicationContext(ApplicationContext app) throws BeansException {
		this.app = (ConfigurableApplicationContext)app;
	}

	
	public void onApplicationEvent(ApplicationEvent event) {
		// 如果是容器刷新事件OR Start 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();
		// 把数据源bean注册到容器中
		addSourceBeanToApp(mapCustom);
	}

	/**
	 * 功能说明:根据DataSource创建bean并注册到容器中
	 * 
	 * @param acf
	 * @param mapCustom
	 */
	private void addSourceBeanToApp(Map<String, DataSourceInfo> mapCustom) {
		DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app
				.getAutowireCapableBeanFactory();

		String DATASOURCE_BEAN_CLASS = "org.apache.commons.dbcp.BasicDataSource";
		BeanDefinitionBuilder bdb;
		
		Iterator<String> iter = mapCustom.keySet().iterator();
		
		Map<String,Object> targetDataSources = new LinkedHashMap<String, Object>();
		
		baseBeanComm = new ChildBeanDefinition("dataSource");
		//	将默认数据源放入 targetDataSources map中
		targetDataSources.put("dataSourceA", app.getBean("dataSource"));

		// 根据数据源得到数据,动态创建数据源bean 并将bean注册到applicationContext中去
		while (iter.hasNext()) {
			
			//	bean ID
			String beanKey = iter.next();
			//	创建bean
			bdb = BeanDefinitionBuilder.rootBeanDefinition(DATASOURCE_BEAN_CLASS);
			bdb.getBeanDefinition().setAttribute("id", beanKey);
			bdb.addPropertyValue("driverClassName", "org.gjt.mm.mysql.Driver");
			bdb.addPropertyValue("url", mapCustom.get(beanKey).connUrl);
			bdb.addPropertyValue("username", mapCustom.get(beanKey).userName);
			bdb.addPropertyValue("password", mapCustom.get(beanKey).password);
			bdb.addPropertyValue("timeBetweenEvictionRunsMillis", 3600000);
			bdb.addPropertyValue("minEvictableIdleTimeMillis", 3600000);
			//	注册bean
			acf.registerBeanDefinition(beanKey, bdb.getBeanDefinition());
			
			//	放入map中,注意一定是刚才创建bean对象
			targetDataSources.put(beanKey, app.getBean(beanKey));
			
		}
		//	将创建的map对象set到 targetDataSources;
		dynamicDataSource.setTargetDataSources(targetDataSources);
		
		//	必须执行此操作,才会重新初始化AbstractRoutingDataSource 中的 resolvedDataSources,也只有这样,动态切换才会起效
		dynamicDataSource.afterPropertiesSet();
		
	}

	/**
	 * 功能说明:GET ALL SM_STATIONS FROM DB1
	 * 
	 * @return
	 * @throws IOException
	 */
	private Map<String, DataSourceInfo> parsePropertiesFile(String fileName)
			throws IOException {
		
		String sql = "SELECT STATION_ID,CENTER_DB_NAME FROM SM_STATION ";
		
		List list = jdbcTemplate.queryForList(sql);
		Iterator iterator = list.iterator();
		Map<String, DataSourceInfo> mds = new HashMap<String, DataSourceInfo>();
		while (iterator.hasNext()) {
			
			Map map4station = (Map) iterator.next();
			DataSourceInfo dsi  = new DataSourceInfo();  

			String username = "root";
			String password = "root";
			String url_1 = "jdbc:mysql://127.0.0.1:3306/";
			String dbName = map4station.get("CENTER_DB_NAME")+"";
			String url_2 = "?useUnicode=true&characterEncoding=utf-8";
			
			
			String key = "dataSource_";
			String sid = map4station.get("STATION_ID")+"";
			key = key.concat(sid);			
			
			String url = url_1.concat(dbName).concat(url2);

			dsi.connUrl = url;
			dsi.userName = username;
			dsi.password = password;
			mds.put(key, dsi);
		}

		return mds;
	}

	//	自定义数据结构
	private class DataSourceInfo{  

		public String connUrl;  
		public String userName;  
		public String password;  
		  
		public String toString() {
			return "(url:" + connUrl + ", username:" + userName + ", password:"
				+ password + ")";
		} 
	}  

}


5:如何切换

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

CustomerContextHolder.setCustomerType(beanKey);

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



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

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

 

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Spring中,我们可以使用BeanFactory或者ApplicationContext来动态创建Bean实例。 1. 使用BeanFactory动态创建Bean实例 ```java DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Person.class); builder.addPropertyValue("name", "张三"); builder.addPropertyValue("age", 18); beanFactory.registerBeanDefinition("person", builder.getBeanDefinition()); Person person = (Person) beanFactory.getBean("person"); ``` 以上代码中,我们先创建一个DefaultListableBeanFactory实例,然后使用BeanDefinitionBuilder创建一个BeanDefinition,指定要创建的Bean类型为Person,并设置Bean的属性值(name和age)。最后将BeanDefinition注册到BeanFactory中,然后通过getBean()方法获取动态创建的Person实例。 2. 使用ApplicationContext动态创建Bean实例 ```java GenericApplicationContext context = new GenericApplicationContext(); context.refresh(); GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(Person.class); beanDefinition.getPropertyValues().add("name", "张三"); beanDefinition.getPropertyValues().add("age", 18); context.registerBeanDefinition("person", beanDefinition); Person person = (Person) context.getBean("person"); ``` 以上代码中,我们创建了一个GenericApplicationContext实例,并调用refresh()方法初始化容器。然后创建一个GenericBeanDefinition实例,设置Bean的类型为Person,并设置Bean的属性值(name和age)。最后将BeanDefinition注册到ApplicationContext中,然后通过getBean()方法获取动态创建的Person实例。 总的来说,使用Spring动态创建Bean实例的方法比较灵活,可以根据具体的需求来选择使用BeanFactory或者ApplicationContext。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值