spring boot 多数据源加载原理

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

spring boot 多数据源加载原理_第1张图片

 

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

 spring boot 多数据源加载原理_第2张图片

 

 

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

spring boot 多数据源加载原理_第3张图片

 

 

 

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
 * 

* 动态数据源注册 **/ 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 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 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 dsMap = propertyResolver.getSubProperties(dsPrefix + "."); DataSource ds = buildDataSource(dsMap); customDataSources.put(dsPrefix, ds); dataBinder(ds, environment); } } private DataSource buildDataSource(Map dsMap) { Object type = dsMap.get("type"); if (type == null) { type = DATASOURCE_TYPE_DEFAULT; } Class dataSourceType; try { dataSourceType = (Class) 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 rpr = new RelaxedPropertyResolver(environment, "spring.datasource"). getSubProperties("."); Map 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 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
 * 

* 切换数据源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 多数据源加载原理)