`
icezx
  • 浏览: 14745 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

使用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);
		}
	}

如代码所示,AbstractRoutingDataSource实现了InitializingBean类,实现了afterPropertiesSet方法.afterPropertiesSet方法在bean的属性赋值之后执行,
并检查targetDataSources 是否有值,如果有值就将targetDataSources  转换成
resolvedDataSources.也就是说,如果你要使用AbstractRoutingDataSource,就必须在方法afterPropertiesSet执行之前,进行targetDataSources 属性的初始化.这也就是目前网上的配置方式,在配置文件里配置N个数据源的由来.




笔者认为很不爽快,按着自己想法,多数据源管理应该满足如下条件:
1 多数据源配置信息应该独立出来
2 实现动态加载,即通过自定义配置文件,让系统动态加载数据源,而不是通过spring配置文件去配置.
3 AbstractRoutingDataSource的子类无需配置datasource集合,只需要简单的通过bean标签,声明在配置文件里,或者仅仅需要一个@Component注解.
4 实现动态切换数据源.

2 实现:
   spring为多数据源的支持提供了AbstractRoutingDataSource.java类.
该类通过运行时指定当前的datasourcename来进行数据源的动态切换.您应该根据需要
重写AbstractRoutingDataSource的几个方法
AbstractRoutingDataSource.java
//查找当前用户上下文变量中设置的数据源.
@Override
	protected Object determineCurrentLookupKey() {
		DataSourceType dataSourceType= DataSourceContextHolder.getDataSourceType();
		
		return dataSourceType;
	}

//设置默认的数据源
	@Override
	public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
		super.setDefaultTargetDataSource(defaultTargetDataSource);
	}
//设置数据源集合.
	@Override
	public void setTargetDataSources(Map targetDataSources) {
		super.setTargetDataSources(targetDataSources);
	}



DataSourceContextHolder.java
/**
*数据源线程上下文对象.
*
*/
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();
	}
	
}



有了以上两个基础类之后,我们还需要解决一个问题,如何让我们的数据源管理器(AbstractRoutingDataSource的子类)实现声明的时候零配置,即无需对targetDataSources(数据源集合)进行配置,而是采取系统初始化时加载的方式.

思路:
1 必须在afterPropertiesSet方法之前,将必须属性的值进行填充.
2 必须把动态加载的数据源注册为spring容器内的bean.

因此,为了实现以上2点需求,我们必须继承 AbstractRoutingDataSource类,并且实线 ApplicationContextAware 接口.
MutiDataSourceBean.java

/**
 * 初始化动态数据源
 * @author Administrator
 *
 */
/**
 * 初始化动态数据源
 * @author Administrator
 *
 */
@Component("mutiGameDs")
public class MutiDataSourceBean extends AbstractRoutingDataSource implements ApplicationContextAware{

	private static Logger log = Logger.getLogger("InistailizeMutiDataSourceBean");
	private static ApplicationContext ac ;

	@Override
	public void afterPropertiesSet() {
		
		log.info("初始化多数据源");
		try {
			initailizeMutiDataSource();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		log.info("多数据源加入spring容器中成功!");

		super.afterPropertiesSet();
	}

	@Override
	public void setApplicationContext(ApplicationContext ctx)
			throws BeansException {
		// TODO Auto-generated method stub
		ac=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"));//设置默认数据源
	}



	@Override
	protected Object determineCurrentLookupKey() {
		DataSourceType dataSourceType= DataSourceContextHolder.getDataSourceType();
		
		return dataSourceType;
	}

	@Override
	public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
		super.setDataSourceLookup(dataSourceLookup);
	}

	@Override
	public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
		super.setDefaultTargetDataSource(defaultTargetDataSource);
	}

	@Override
	public void setTargetDataSources(Map targetDataSources) {
		super.setTargetDataSources(targetDataSources);
	}

}




OK,大公告成.
public class TestMutiDataSource  extends SpringTxTestCase{

	@Autowired
	@Qualifier("jdbcTemplate4Game")
	JdbcTemplate  jt;
	
	@Test
	public 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);
		}
	}

}



分享到:
评论
4 楼 xiaohuzi2008 2017-12-20  
能不能给出完整的代码,包括配置文件,谢谢 xiaohuzi2008@126.com
3 楼 icezx 2015-08-20  
icezx 写道
jd2bs 写道
这个其实换汤不换药啊  只不过从spring conf里挪到了自定义xml文件里

我碰到的问题是:客户所需的多数据源节点ip和个数都不一定,是由客户在管理平台动态添加或者修改的,而且添加修改完最好不用重启,这种场景下如何启动和切换多数据源。

jd2bs 写道
这个其实换汤不换药啊  只不过从spring conf里挪到了自定义xml文件里

我碰到的问题是:客户所需的多数据源节点ip和个数都不一定,是由客户在管理平台动态添加或者修改的,而且添加修改完最好不用重启,这种场景下如何启动和切换多数据源。


这个配置来源于用户输入肯定存在数据库里咯,启动的时候加载出来作为缓存,添加的时候刷新缓存,然后用我上面的代码就可以


我处理的业务场景是动态切换数据源,到每个数据源里去取数据,对着N个节点。无需认为干涉,半夜自动跑定时任务
2 楼 icezx 2015-08-20  
jd2bs 写道
这个其实换汤不换药啊  只不过从spring conf里挪到了自定义xml文件里

我碰到的问题是:客户所需的多数据源节点ip和个数都不一定,是由客户在管理平台动态添加或者修改的,而且添加修改完最好不用重启,这种场景下如何启动和切换多数据源。

jd2bs 写道
这个其实换汤不换药啊  只不过从spring conf里挪到了自定义xml文件里

我碰到的问题是:客户所需的多数据源节点ip和个数都不一定,是由客户在管理平台动态添加或者修改的,而且添加修改完最好不用重启,这种场景下如何启动和切换多数据源。


这个配置来源于用户输入肯定存在数据库里咯,启动的时候加载出来作为缓存,添加的时候刷新缓存,然后用我上面的代码就可以
1 楼 jd2bs 2015-04-13  
这个其实换汤不换药啊  只不过从spring conf里挪到了自定义xml文件里

我碰到的问题是:客户所需的多数据源节点ip和个数都不一定,是由客户在管理平台动态添加或者修改的,而且添加修改完最好不用重启,这种场景下如何启动和切换多数据源。

相关推荐

    springboot-AOP实现多数据源动态切换(Druid连接池)

    在Spring Boot项目中实现多数据源动态切换是一项高级特性,能够使应用根据不同业务需求访问不同的数据库,从而实现服务的解耦和数据库操作的优化。该技术的关键在于如何在同一个应用中配置和使用多个数据源,以及...

    Springcloud 多数库 多数据源整合,查询动态切换数据库

    本主题聚焦于在Spring Cloud环境中实现多数据库和多数据源的整合,并且能够动态切换查询的数据库。这是一个复杂但至关重要的需求,特别是在大型企业级应用中,可能需要根据业务逻辑或用户权限连接到不同的数据库。 ...

    使用springboot + JPA / MyBatis 实现多数据源动态切换

    本教程将详细讲解如何使用Spring Boot结合JPA或MyBatis框架实现多数据源的动态切换。 **一、Spring Boot与JPA** 1. **Spring Boot简介**:Spring Boot是Spring Framework的一个模块,旨在简化Spring应用的初始搭建...

    SpringBoot整合mybatis-plus实现多数据源的动态切换且支持分页查询.pdf

    在SpringBoot项目中,整合Mybatis-Plus并实现多数据源的动态切换,同时支持分页查询是一项常见的需求。以下将详细阐述这个过程中的关键步骤和技术要点。 首先,我们需要引入必要的Maven依赖。这里提到了四个关键...

    真正意义的spring动态切换数据源源码

    总结,Spring动态切换数据源是企业级应用中不可或缺的功能,它通过灵活的策略和强大的容器管理能力,实现了在运行时无缝地在多个数据源之间切换,从而满足了复杂的应用场景需求。通过深入理解和掌握这一技术,我们...

    spring 动态切换数据源

    在Spring框架中,动态切换数据源是一项重要的功能,它允许应用程序根据业务需求在多个数据库之间灵活切换。这一特性对于多租户系统、读写分离、分布式数据库等场景尤其有用。以下将详细介绍如何实现Spring的动态数据...

    spring mybatis 多数据源动态切换

    Spring 和 MyBatis 结合使用时,实现多数据源动态切换是一项重要的技术。本文将深入探讨如何在 Spring 中配置和管理多个数据源,并实现动态切换。 首先,我们需要理解“多数据源”是什么。它是指在一个应用中同时...

    spring boot多数据源(AOP注解动态切换)

    本教程将详细讲解如何在Spring Boot项目中集成Druid连接池,并利用AOP注解实现多数据源的动态切换。我们将基于JDK 1.8和Spring Boot 1.5.14版本进行讨论。 首先,我们需要理解Spring Boot的自动配置特性。Spring ...

    spring动态切换数据源

    在Java的Spring框架中,动态切换数据源是一项重要的功能,尤其在多租户、微服务或者需要根据业务逻辑切换数据库的场景下。本知识点主要围绕如何在Spring中实现数据源的动态切换进行深入探讨。 首先,我们需要理解...

    springboot实现多数据源而且加上事务不会使aop切换数据源失效

    本示例主要讲解如何使用Spring Boot结合MyBatis实现多数据源切换,并确保AOP事务管理仍然有效。 首先,我们需要配置多数据源。在Spring Boot中,可以使用`DataSource`接口的实现类,如`HikariCP`或`Druid`,创建两...

    SpringBoot+Atomikos+动态多数据源+事务+2种切换数据源的方式

    本主题将深入探讨如何利用SpringBoot结合Atomikos实现动态多数据源以及事务管理,并介绍两种切换数据源的方法。 首先,SpringBoot简化了传统Spring应用的初始化过程,它通过自动配置和starter包让开发者快速搭建...

    Spring+SpringMVC+Mybatis动态链接多数据源

    本项目“Spring+SpringMVC+Mybatis动态链接多数据源”旨在实现一个灵活、可扩展的数据源切换机制,以适应复杂的业务场景。 Spring框架作为Java领域中最广泛使用的轻量级框架,它提供了强大的依赖注入和AOP(面向切...

    spring-boot集成mybtis+druid实现hive/mysql多数据源切换

    本文将详细讨论如何使用Spring Boot、MyBatis和Druid来实现MySQL与Hive之间的多数据源切换,以满足不同业务场景的需求。 首先,Spring Boot是Spring框架的一种轻量级实现,它简化了配置并提供了快速开发新应用的...

    SpringBoot+Atomikos分布式事务及多数据源动态切换,两种demo

    本文将深入探讨如何在Spring Boot项目中实现Atomikos分布式事务以及动态数据源切换的两种示例。 首先,我们需要理解什么是分布式事务。在分布式系统中,事务需要跨越多个独立的数据库或服务进行操作。分布式事务的...

    spring boot多数据源配置

    2. 使用`AbstractRoutingDataSource`实现动态数据源切换 创建一个继承自`AbstractRoutingDataSource`的类,通过设置当前数据源上下文来实现数据源的切换: ```java @Component public class DynamicDataSource ...

    Spring Boot整合Mybatis使用druid实现多数据源自动切换

    以上就是Spring Boot整合Mybatis并使用Druid实现多数据源自动切换的详细步骤。通过这样的方式,我们可以灵活地管理多个数据库,提高系统的扩展性和可用性。同时,Druid提供的监控功能可以帮助我们更好地管理和优化...

    spring boot数据源切换demo

    在Spring Boot应用中,数据源切换是一个常见的需求,特别是在分布式系统或者微服务架构中,可能需要连接多个数据库来实现不同的业务功能。本示例“spring boot数据源切换demo”将展示如何在Spring Boot中集成MyBatis...

    ssm动态切换数据源

    本文将深入探讨如何在基于SSM(Spring、Spring MVC、MyBatis)的项目中实现“动态切换数据源”,这是一个在多数据库环境下非常重要的功能。 首先,我们来理解“动态切换数据源”的概念。在大型应用中,通常会根据...

    spring boot动态多数据源demo

    自定义多数据源类MultiplyDataSource(继承HikariDataSource),通过多线程类ThreadLocal控制当前需要使用的数据源id,实现多数据源动态切换查询功能(默认数据源和其他数据源都统一使用这一个,简化了配置)。...

    spring boot AOP注解方式实现多数据源

    本文将深入探讨如何使用Spring Boot的AOP注解方式来实现多数据源的集成。 首先,我们要了解Spring Boot的基础概念。Spring Boot简化了Spring应用程序的创建,它提供了自动配置、起步依赖等特性,使得开发者可以快速...

Global site tag (gtag.js) - Google Analytics