zoukankan      html  css  js  c++  java
  • springboot项目自定义注解实现的多数据源切换

    一、主要依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/>
    </parent>
        
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
        <version>2.1.4.RELEASE</version>
    </dependency>
    dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>

    二、yml

    # 数据源配置
    spring:
        datasource:
            type: com.alibaba.druid.pool.DruidDataSource
            driverClassName: com.mysql.jdbc.Driver
            druid:
                # 主库数据源
                master:
                    url: jdbc:mysql://127.0.0.1:3306/master?characterEncoding=UTF-8
                    username: root
                    password: root
                #树熊数据源
                slave:
                    enabled : true
                    url: jdbc:mysql:////127.0.0.1:3306/slave?characterEncoding=UTF-8
                    username: root
                    password: root
                
    
                # 初始连接数
                initial-size: 10
                # 最大连接池数量
                max-active: 100
                # 最小连接池数量
                min-idle: 10
                # 配置获取连接等待超时的时间
                max-wait: 60000
                # 打开PSCache,并且指定每个连接上PSCache的大小
                pool-prepared-statements: true
                max-pool-prepared-statement-per-connection-size: 20
                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
                timeBetweenEvictionRunsMillis: 60000
                # 配置一个连接在池中最小生存的时间,单位是毫秒
                min-evictable-idle-time-millis: 300000
                validation-query: SELECT 1 FROM DUAL
                test-while-idle: true
                test-on-borrow: false
                test-on-return: false
                stat-view-servlet:
                    enabled: true
                    url-pattern: /druid/*
                filter:
                    stat:
                        log-slow-sql: true
                        slow-sql-millis: 1000
                        merge-sql: false
                    wall:
                        config:
                            multi-statement-allow: true

    三、实现

    3.1、@DataSource和DataSourceType

    /**
     * 数据源
     * @author DUCHONG
     */
    public enum DataSourceType
    {
        /**
         * 主库
         */
        MASTER,
    
        /**
         * 从库
         */
        SLAVE
    }
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 自定义多数据源切换注解
     *
     * @author DUCHONG
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DataSource
    {
        /**
         * 切换数据源名称
         */
        public DataSourceType value() default DataSourceType.MASTER;
    }

    3.2、DynamicDataSourceContextHolder

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 数据源切换处理
     *
     * @author DUCHONG
     */
    public class DynamicDataSourceContextHolder
    {
        public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
    
        /**
         * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
         *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
         */
        private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    
        /**
         * 设置数据源的变量
         */
        public static void setDateSourceType(String dsType)
        {
            log.info("切换到{}数据源", dsType);
            CONTEXT_HOLDER.set(dsType);
        }
    
        /**
         * 获得数据源的变量
         */
        public static String getDateSourceType()
        {
            return CONTEXT_HOLDER.get();
        }
    
        /**
         * 清空数据源变量
         */
        public static void clearDateSourceType()
        {
            CONTEXT_HOLDER.remove();
        }
    }

    3.3、继承AbstractRoutingDataSource

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    import javax.sql.DataSource;
    import java.util.Map;
    
    /**
     * 动态数据源
     *
     * @author DUCHONG
     */
    public class DynamicDataSource extends AbstractRoutingDataSource
    {
        public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
        {
            super.setDefaultTargetDataSource(defaultTargetDataSource);
            super.setTargetDataSources(targetDataSources);
            super.afterPropertiesSet();
        }
    
        @Override
        protected Object determineCurrentLookupKey()
        {
            return DynamicDataSourceContextHolder.getDateSourceType();
        }
    }

    3.4、定义切面

    import com.starfast.admin.common.annotation.DataSource;
    import com.starfast.admin.datasource.DynamicDataSourceContextHolder;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 多数据源处理
     * @author DUCHONG
     */
    @Aspect
    @Order(1)
    @Component
    public class DataSourceAspect
    {
        protected Logger logger = LoggerFactory.getLogger(getClass());
    
        @Pointcut("@annotation(com.duchong.common.annotation.DataSource)")
        public void dsPointCut()
        {
    
        }
    
        @Around("dsPointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable
        {
            MethodSignature signature = (MethodSignature) point.getSignature();
    
            Method method = signature.getMethod();
    
            DataSource dataSource = method.getAnnotation(DataSource.class);
    
            if (null!=dataSource)
            {
                DynamicDataSourceContextHolder.setDateSourceType(dataSource.value().name());
            }
    
            try
            {
                return point.proceed();
            }
            finally
            {
                // 销毁数据源 在执行方法之后
                DynamicDataSourceContextHolder.clearDateSourceType();
            }
        }
    }

    3.5、@Configuration

    import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
    import com.starfast.admin.common.enums.DataSourceType;
    import com.starfast.admin.datasource.DynamicDataSource;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * druid 配置多数据源
     *
     * @author DUCHONG
     */
    @Configuration
    public class DruidConfig
    {
        @Bean
        @ConfigurationProperties("spring.datasource.druid.master")
        public DataSource masterDataSource()
        {
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties("spring.datasource.druid.slave")
        @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
        public DataSource slaveDataSource()
        {
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean(name = "dynamicDataSource")
        @Primary
        public DynamicDataSource dataSource()
        {
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource());
            targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource());
            return new DynamicDataSource(masterDataSource(), targetDataSources);
        }
    }

    3.6、使用

    需要切换数据源的方法上加

    @DataSource(value = DataSourceType.SLAVE)

    结束。

  • 相关阅读:
    Python yield 使用浅析
    python调试
    程序员常用的技术网站
    使用Pylint规范你的Python代码
    Python面试题整理-更新中
    2019-2020-1 1823《程序设计与数据结构》每周成绩
    2019-2020-1 1823《程序设计与数据结构》问题汇总(正在更新)
    2019-2020-1 1823《程序设计与数据结构》第二、三周作业总结
    2019-2020-1 1823《程序设计与数据结构》第一周作业总结
    2019-2020-1 1823《程序设计与数据结构》预备作业总结
  • 原文地址:https://www.cnblogs.com/geekdc/p/10963476.html
Copyright © 2011-2022 走看看