spring boot 多数据源加载原理

  • 2019 年 12 月 16 日
  • 筆記

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层调用 。