• SpringBoot运行时动态添加数据源


    此方案适用于解决springboot项目运行时动态添加数据源,非静态切换多数据源!!!

    一、多数据源应用场景:

    1.配置文件配置多数据源,如默认数据源:master,数据源1:salve1...,运行时动态切换已配置的数据源(master、salve1互相切换),无法在运行时动态添加配置文件中未配置的数据源。

    2.配置一个默认数据源,运行时动态添加新数据源使用(本博客适用于此场景)

    二、解决方案:

    Spring提供了AbstractRoutingDataSource用于动态路由数据源,第一种场景继承AbstractRoutingDataSource类并覆写其protected abstract Object determineCurrentLookupKey()即可;
    而第二种场景我们直接覆写protected DataSource determineTargetDataSource方法即可。原理可看下AbstractRoutingDataSource对应源码,比较简单,不做赘述。
    直接上干货:

    import com.yaoshun.util.spring.SpringUtils;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * <p>使用步骤:</p>
     * <blockquote><pre>
     *     DynamicDataSource.dataSourcesMap.put(dataSourceKey, druidDataSource);
     *     DynamicDataSource.setDataSource(dataSourceKey);
     *     调用业务代码</i>
     *     DynamicDataSource.clear();
     * </pre></blockquote>
     *
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
    
        private static final ThreadLocal<String> dataSourceKey = ThreadLocal.withInitial(() -> "defaultDataSource");
    
        public static Map<Object, Object> dataSourcesMap = new ConcurrentHashMap<>(10);
    
        static {
            dataSourcesMap.put("defaultDataSource", SpringUtils.getBean("defaultDataSource"));
        }
    
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicDataSource.dataSourceKey.get();
        }
    
        public static void setDataSource(String dataSource) {
            DynamicDataSource.dataSourceKey.set(dataSource);
            DynamicDataSource dynamicDataSource = (DynamicDataSource) SpringUtils.getBean("dataSource");
            dynamicDataSource.afterPropertiesSet();
        }
    
        public static String getDataSource() {
            return DynamicDataSource.dataSourceKey.get();
        }
    
        public static void clear() {
            DynamicDataSource.dataSourceKey.remove();
        }
    }
    
    import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.context.annotation.Primary;
    import javax.sql.DataSource;
    
    @Configuration
    public class DataSourceConfig {
    
        @Bean
        @ConfigurationProperties("spring.datasource.druid")
        public DataSource defaultDataSource() {
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @Primary
        @DependsOn({"springUtils", "defaultDataSource"})
        public DynamicDataSource dataSource() {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            dynamicDataSource.setTargetDataSources(DynamicDataSource.dataSourcesMap);
            return dynamicDataSource;
        }
    }
    

    使用时直接调用DynamicDataSource.setDataSource(DataSource dataSource)方法即可,使用完后调用DynamicDataSource.clear()防止内存泄漏并重置默认数据源。

    附上详细使用方法:

            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setUrl("jdbc:mysql://localhost:3306/sys?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC&useAffectedRows=true");
            druidDataSource.setUsername("root");
            druidDataSource.setPassword("root");
            DynamicDataSource.dataSourcesMap.put("dbkey", druidDataSource);
            DynamicDataSource.setDataSource("dbkey");
            此时数据源已切换到druidDataSource ,调用自己的业务方法即可。
            使用完后调用DynamicDataSource.clear();重置为默认数据源。
    

    附上工具类SpringUtils :

    import lombok.Getter;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    @Component
    public final class SpringUtils implements ApplicationContextAware {
    
        @Getter
        private static ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            if (SpringUtils.applicationContext == null) {
                SpringUtils.applicationContext = applicationContext;
            }
        }
    
        public static <T> T getBean(Class<T> clazz) {
            return SpringUtils.applicationContext.getBean(clazz);
        }
    
        public static Object getBean(String name) {
            return SpringUtils.applicationContext.getBean(name);
        }
    
        public static String getProperty(String key) {
            return SpringUtils.applicationContext.getEnvironment().getProperty(key);
        }
    }
    
  • 相关阅读:
    Java中不定参的使用规则
    关于泛型中<T extends comparable>的理解
    Java泛型的定义以及对于<? extends T>和<? super T>
    spring入门(一)
    谷歌游览器对<input type='file'> change只能响应1次解决和样式的改变
    .net资源文件及卫星程序集使用
    datetimekind.unspecified理解
    ILMerge工具
    .net 数据库抽象类
    格式相关类
  • 原文地址:https://www.cnblogs.com/tswhq/p/11668078.html
Copyright © 2020-2023  润新知