• 三、Spring Boot 多数据源配置


    下面一个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中
     *
     */
    @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
         */
        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类的不同方法,分别指定使用不同的数据源?

  • 相关阅读:
    北京一家JAVA开发公司面试题(留给后人)
    线人
    线人
    潜罪犯
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/chenliangcl/p/7345982.html
Copyright © 2020-2023  润新知