git代码:https://gitee.com/wwj912790488/multiple-data-sources

DynamicDataSourceAspect切面 必须定义@Order(-10),保证该aop在@Transaction之前执行

配置如下,分别加载三个数据库配置

1.利用ImportBeanDefinitionRegistrar和EnvironmentAware 加载注册多个数据源bean
package org.spring.boot.multiple.ds;

import com.alibaba.druid.pool.DruidDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata; import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map; /**
* @author donghongchen
* @create 2017-09-04 15:34
* <p>
* 动态数据源注册
**/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private Logger logger = LoggerFactory.getLogger(this.getClass()); //如果配置文件中未指定数据源类型,使用默认值
private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource"; private ConversionService conversionService = new DefaultConversionService(); private PropertyValues dataSourcePropertyValues;
//默认数据源
private DataSource defaultDataSource; private Map<String, DataSource> customDataSources = new HashMap<>(); /**
* 加载多数据源配置
*
* @param environment
*/
@Override
public void setEnvironment(Environment environment) {
initDefaultDataSource(environment);
initCustomDataSources(environment);
} private void initDefaultDataSource(Environment environment) {
//读取主数据源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment, "spring.datasource.");
Map<String, Object> dsMap = new HashMap<>();
dsMap.put("type", propertyResolver.getProperty("type"));
dsMap.put("driverClassName", propertyResolver.getProperty("driverClassName"));
dsMap.put("url", propertyResolver.getProperty("url"));
dsMap.put("username", propertyResolver.getProperty("username"));
dsMap.put("password", propertyResolver.getProperty("password"));
//创建数据源
defaultDataSource = buildDataSource(dsMap);
dataBinder(defaultDataSource, environment);
} private void initCustomDataSources(Environment environment) {
//读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment, "custom.datasource.");
String dsPrefixs = propertyResolver.getProperty("names");
if (null == dsPrefixs || "".equals(dsPrefixs)) {
return;
}
String[] dsPrefixsArr = dsPrefixs.split(",");
for (String dsPrefix : dsPrefixsArr) {
Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
DataSource ds = buildDataSource(dsMap);
customDataSources.put(dsPrefix, ds);
dataBinder(ds, environment);
}
} private DataSource buildDataSource(Map<String, Object> dsMap) {
Object type = dsMap.get("type");
if (type == null) {
type = DATASOURCE_TYPE_DEFAULT;
}
Class<? extends DataSource> dataSourceType;
try {
dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
String driverClassName = dsMap.get("driverClassName").toString();
String url = dsMap.get("url").toString();
String username = dsMap.get("username").toString();
String password = dsMap.get("password").toString(); DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClassName); return dataSource; // DataSourceBuilder factory = DataSourceBuilder.create().
// driverClassName(driverClassName).type(dataSourceType).url(url).username(username).password(password);
// return factory.build(); } catch (ClassNotFoundException ex) {
logger.error(ex.getMessage(), ex);
}
return null;
} private void dataBinder(DataSource dataSource, Environment environment) {
RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
dataBinder.setConversionService(conversionService);
dataBinder.setIgnoreNestedProperties(false);
dataBinder.setIgnoreInvalidFields(false);
dataBinder.setIgnoreUnknownFields(true);
if (dataSourcePropertyValues == null) {
Map<String, Object> rpr = new RelaxedPropertyResolver(environment, "spring.datasource").
getSubProperties(".");
Map<String, Object> values = new HashMap<>(rpr);
//排除已经设置的属性
values.remove("type");
values.remove("driverClassName");
values.remove("url");
values.remove("username");
values.remove("password");
dataSourcePropertyValues = new MutablePropertyValues(values);
}
dataBinder.bind(dataSourcePropertyValues);
} @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> targetDataSource = new HashMap<>();
//将主数据源添加到更多数据源中
targetDataSource.put("dataSource", defaultDataSource);
DynamicDataSourceContextHolder.dataSourceIDS.add("dataSource"); //添加更多数据源
targetDataSource.putAll(customDataSources);
for (String key : customDataSources.keySet()) {
DynamicDataSourceContextHolder.dataSourceIDS.add(key);
}
//创建DynamicDataSource
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
//添加属性
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSource);
registry.registerBeanDefinition("dataSource", beanDefinition); } }

 

根据@annotation 去动态切换数据源

package org.spring.boot.multiple.ds;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; /**
* @author donghongchen
* @create 2017-09-04 14:44
* <p>
* 切换数据源Advice
**/
@Aspect
@Order(-10) //保证该aop在@Transaction之前执行
@Component
public class DynamicDataSourceAspect { private Logger logger = LoggerFactory.getLogger(this.getClass()); /**
* * @Before("@annotation(ds)")
* 的意思是:@Before:在方法执行之前进行执行; @annotation(targetDataSource):会拦截注解targetDataSource的方法,否则不拦截;
* @param point
* @param targetDataSource
*/
@Before("@annotation(targetDataSource)")
public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource){
//获取当前的指定数据源
String dsID = targetDataSource.value();
//如果不在我们注入的所有的数据源范围内,输出警告信息,系统自动使用默认的数据源
if (!DynamicDataSourceContextHolder.containsDataSource(dsID)){
logger.error("数据源["+dsID+"]不存在,使用默认的数据源 > { " + dsID+", 方法签名:"+point.getSignature()+"}");
}else {
logger.info("Use DataSource: {" +dsID+", 方法签名:"+point.getSignature() +"}");
//找到的话,那么设置动态数据源上下文
DynamicDataSourceContextHolder.setDataSourceType(dsID);
}
} @After("@annotation(targetDataSource)")
public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource){
//方法执行完毕后,销毁当前数据源信息,进行垃圾回收
DynamicDataSourceContextHolder.clearDataSourceType();
}
}

最后对应到对应的DAO层调用 。

spring boot 多数据源加载原理的更多相关文章

  1. Spring boot 国际化自动加载资源文件问题

    Spring boot 国际化自动加载资源文件问题 最近在做基于Spring boot配置的项目.中间遇到一个国际化资源加载的问题,正常来说只要在application.properties文件中定义 ...

  2. Spring Boot的属性加载顺序

        伴随着团队的不断壮大,往往不需要开发人员知道测试或者生产环境的全部配置细节,比如数据库密码,帐号信息等.而是希望由运维或者指定的人员去维护配置信息,那么如果要修改某项配置信息,就不得不去修改项 ...

  3. Spring Boot JPA 懒加载

    最近在使用spring jpa 的过程中经常遇到懒加载的错误:"` org.hibernate.LazyInitializationException: could not initiali ...

  4. Spring Boot 实现配置文件加解密原理

    Spring Boot 配置文件加解密原理就这么简单 背景 接上文<失踪人口回归,mybatis-plus 3.3.2 发布>[1] ,提供了一个非常实用的功能 「数据安全保护」 功能,不 ...

  5. 「新特性」Spring Boot 全局懒加载机制了解一下

    关于延迟加载 在 Spring 中,默认情况下所有定的 bean 及其依赖项目都是在应用启动时创建容器上下文是被初始化的.测试代码如下: @Slf4j @Configuration public cl ...

  6. 在IDEA下使用Spring Boot的热加载(Hotswap)

    你是否遇到过这样的困扰: 当你写完一段代码后,要看到效果,必须点击IDEA的停止按钮,然后再次重启启动项目,你是否觉得这样很烦呢? 如果你觉得很烦,本文就是用来解决你的问题的. 所谓热加载,就是让我们 ...

  7. Spring Boot JDBC:加载DataSource过程的源码分析及yml中DataSource的配置

    装载至:https://www.cnblogs.com/storml/p/8611388.html Spring Boot实现了自动加载DataSource及相关配置.当然,使用时加上@EnableA ...

  8. spring boot 是如何加载jackson的?

    Spring Boot 自动引入jackson: 通过:Spring-Boot-starter-web Jackson自动配置 这里的configurations是读取的这里: 通过反射加载Jacks ...

  9. Spring Boot 如何热加载jar实现动态插件?

    一.背景 动态插件化编程是一件很酷的事情,能实现业务功能的 解耦 便于维护,另外也可以提升 可扩展性 随时可以在不停服务器的情况下扩展功能,也具有非常好的 开放性 除了自己的研发人员可以开发功能之外, ...

随机推荐

  1. Android实战:手把手实现“捧腹网”APP(二)-----捧腹APP原型设计、实现框架选取

    Android实战:手把手实现"捧腹网"APP(一)-–捧腹网网页分析.数据获取 Android实战:手把手实现"捧腹网"APP(二)-–捧腹APP原型设计.实 ...

  2. 2019-9-2-win10-uwp-打包第三方字体到应用

    title author date CreateTime categories win10 uwp 打包第三方字体到应用 lindexi 2019-09-02 12:57:38 +0800 2018- ...

  3. install jqdatasdk

    install jqdatasdk pip3 install jqdatasdk ... 54% |█████████████████ | 3.2MB 84kB/s eta 0:0 54% |████ ...

  4. Python 常量

  5. SDUT-2772_数据结构实验之串一:KMP简单应用

    数据结构实验之串一:KMP简单应用 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 给定两个字符串string1和str ...

  6. Python中的生成器(generator)

    生成器: 在函数内部包含yield关键字,那么该函数执行的结果就是生成器(生成器即是迭代器) yield的功能:1.把函数的执行结果做成迭代器(帮函数封装好__iter__(),__next__()方 ...

  7. @atcoder - ARC066F@ Contest with Drinks Hard

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定序列 T1, T2, ... TN,你可以从中选择一些 Ti ...

  8. 【SDOI2015】bzoj3990 排序

    A. 排序 题目描述 输入格式 输出格式 一行,一个整数,表示可以将数组A从小到大排序的不同的操作序列的个数. 样例 样例输入 3 7 8 5 6 1 2 4 3 样例输出 6 数据范围与提示 对于3 ...

  9. iPhone:constrainedToSize获取字符串的宽高

    在使用UILabel存放字符串时,经常需要获取label的长宽数据,本文列出了部分常用的计算方法. 1.获取宽度,获取字符串不折行单行显示时所需要的长度 CGSize titleSize = [aSt ...

  10. 利用伪类选择器与better-scroll的on事件所完成的上拉加载

    之前给大家分享过一篇上拉加载 利用了better-scroll的pullUpDown 和DOM元素的删除添加  感觉那样不太好 今天给大家分享一个不同的上拉加载思想 代码如下 class List { ...