Spring Boot 多数据源

JesEller 5年前

来自: http://blog.csdn.net/catoop/article/details/50564513


上篇文章介绍了如何手工使用Java代码将对象注册到Spring中,为本文“多数据源”做了基础。

下面一个Java类是我已经写好的根据配置文件动态创建多dataSource的代码,其原理也很简单,就是读取配置文件,根据配置文件中配置的数据源数量,动态创建dataSource并注册到Spring中。
代码如下:

package org.springboot.sample.config;    import java.util.HashMap;  import java.util.Map;  import java.util.Map.Entry;    import javax.sql.DataSource;    import org.slf4j.Logger;  import org.slf4j.LoggerFactory;  import org.springframework.beans.BeansException;  import org.springframework.beans.MutablePropertyValues;  import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;  import org.springframework.beans.factory.config.BeanDefinition;  import org.springframework.beans.factory.config.BeanDefinitionHolder;  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;  import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;  import org.springframework.beans.factory.support.BeanDefinitionRegistry;  import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;  import org.springframework.beans.factory.support.BeanNameGenerator;  import org.springframework.boot.bind.RelaxedPropertyResolver;  import org.springframework.context.EnvironmentAware;  import org.springframework.context.annotation.AnnotationBeanNameGenerator;  import org.springframework.context.annotation.AnnotationConfigUtils;  import org.springframework.context.annotation.AnnotationScopeMetadataResolver;  import org.springframework.context.annotation.Configuration;  import org.springframework.context.annotation.ScopeMetadata;  import org.springframework.context.annotation.ScopeMetadataResolver;  import org.springframework.core.env.Environment;    /** * 动态创建多数据源注册到Spring中 * * @author 单红宇(365384722) * @myblog http://blog.csdn.net/catoop/ * @create 2016年1月22日 */  @Configuration  public class MultipleDataSourceBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {        private static final Logger logger = LoggerFactory              .getLogger(MultipleDataSourceBeanDefinitionRegistryPostProcessor.class);        // 如配置文件中未指定数据源类型,使用该默认值      private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";  // private static final Object DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";        private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();      private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();        // 存放DataSource配置的集合,模型<dataSourceName,dataSourceMap>      private Map<String, Map<String, Object>> dataSourceMap = new HashMap<>();        @Override      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {          logger.info("Invoke Metho postProcessBeanFactory");          beanFactory.getBeanDefinition("dataSource").setPrimary(true);            BeanDefinition bd = null;          Map<String, Object> dsMap = null;          for (Entry<String, Map<String, Object>> entry : dataSourceMap.entrySet()) {              bd = beanFactory.getBeanDefinition(entry.getKey());              MutablePropertyValues mpv = bd.getPropertyValues();              dsMap = entry.getValue();              mpv.addPropertyValue("driverClassName", dsMap.get("url"));              mpv.addPropertyValue("url", dsMap.get("url"));              mpv.addPropertyValue("username", dsMap.get("username"));              mpv.addPropertyValue("password", dsMap.get("password"));          }      }        @SuppressWarnings("unchecked")      @Override      public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {          logger.info("Invoke Metho postProcessBeanDefinitionRegistry");            try {              if(!dataSourceMap.isEmpty()){                  for (Entry<String, Map<String, Object>> entry : dataSourceMap.entrySet()) {                        Object type = entry.getValue().get("type");                      if(type == null)                          type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource                      registerBean(registry, entry.getKey(), (Class<? extends DataSource>)Class.forName(type.toString()));                  }              }          } catch (ClassNotFoundException e) {              e.printStackTrace();          }        }        /** * 注册Bean到Spring * * @param registry * @param name * @param beanClass * @author SHANHY * @create 2016年1月22日 */      private void registerBean(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {          AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);          abd.setScope(scopeMetadata.getScopeName());          // 可以自动生成name          String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, registry));            AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);          BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);      }        /** * 加载多数据源配置 */      @Override      public void setEnvironment(Environment env) {          RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");          String dsPrefixs = propertyResolver.getProperty("names");          for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源              Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");              dataSourceMap.put(dsPrefix, dsMap);          }      }  }

将该Java文件直接添加到项目中便可,无其他任何代码耦合,就是单纯一个类。


再来看一下在配置文件中配置多数据源的方法:

# 主数据源,默认的  spring.datasource.type=com.zaxxer.hikari.HikariDataSource  spring.datasource.driver-class-name=com.mysql.jdbc.Driver  spring.datasource.url=jdbc:mysql://localhost:3306/test  spring.datasource.username=root  spring.datasource.password=123456    # 更多数据源  custom.datasource.names=ds1,ds2,ds3  custom.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource  custom.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver  custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test  custom.datasource.ds1.username=root  custom.datasource.ds1.password=123456    custom.datasource.ds2.type=com.zaxxer.hikari.HikariDataSource  custom.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver  custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test  custom.datasource.ds2.username=root  custom.datasource.ds2.password=123456    custom.datasource.ds3.type=com.zaxxer.hikari.HikariDataSource  custom.datasource.ds3.driver-class-name=com.mysql.jdbc.Driver  custom.datasource.ds3.url=jdbc:mysql://localhost:3306/test  custom.datasource.ds3.username=root  custom.datasource.ds3.password=123456    # 下面为连接池的补充设置,应用到上面所有数据源中  spring.datasource.maximum-pool-size=100  spring.datasource.max-idle=10  spring.datasource.max-wait=10000  spring.datasource.min-idle=5  spring.datasource.initial-size=5  spring.datasource.validation-query=SELECT 1  spring.datasource.test-on-borrow=false  spring.datasource.test-while-idle=true  spring.datasource.time-between-eviction-runs-millis=18800

配置文件包括1个主数据源和多个数据源,其中主数据源在Spring中的beanName默认为dataSource,另外几个数据源的beanName分包为:ds1、ds2、ds3,大家看一下配置的规则,想必不用多说。
其中datasource的type属性可以具体指定到我们需要的数据源上面,不指定情况下默认为:org.apache.tomcat.jdbc.pool.DataSource

当然你也可以把这些数据源配置到主dataSource数据库中,然后读取数据库生成多数据源。当然这样做的必要性并不大,难不成数据源还会经常变吗。


在需要应用dataSource的地方需要指定名称,如:

// 方法参数注入方式  public void testDataSource(@Qualifier("ds1") DataSource myDataSource, @Qualifier("dataSource") DataSource dataSource) {    }

或者

// 类成员属性注入方式  @Autowired  @Qualifier("ds1")  private DataSource dataSource1;    @Resource(name = "ds2")  private DataSource dataSource2;

本文共享的代码可以直接使用了,大家可以根据自己需要进行调整。


然而我们在项目中不一定需要直接使用dataSource的,大家都习惯使用JDBC的jdbcTemplate、Mybatis的sqlSessionTemplate,再或者就是以Mybatis为例直接动态代理到Mapper接口上。

那么如何做到完全动态数据源呢,以至于实现我们可以为同一个Java类的不同方法,分别指定使用不同的数据源?下篇文章将为大家揭晓。