Spring Boot 多數據源

JesEller 8年前發布 | 40K 次閱讀 Spring JEE框架

來自: 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);
    }
}

}</pre>

將該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</pre>

配置文件包括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) {

}</pre>

或者

// 類成員屬性注入方式
@Autowired
@Qualifier("ds1")
private DataSource dataSource1;

@Resource(name = "ds2") private DataSource dataSource2;</pre>

本文共享的代碼可以直接使用了,大家可以根據自己需要進行調整。


然而我們在項目中不一定需要直接使用dataSource的,大家都習慣使用JDBC的jdbcTemplate、Mybatis的sqlSessionTemplate,再或者就是以Mybatis為例直接動態代理到Mapper接口上。

那么如何做到完全動態數據源呢,以至于實現我們可以為同一個Java類的不同方法,分別指定使用不同的數據源?下篇文章將為大家揭曉。

</div>

 本文由用戶 JesEller 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!