• 自定义注解实现多数据源+实现原理分析_02


    一、实现原理分析
    1. 浏览器访问方法

    在这里插入图片描述
    在这里插入图片描述

    这里演示的是同一个方法连接2个数据库的场景。
    因此请求方法上不需要添加注解,在具体的执行方法上添加注解即可。
    依次连接sys-order数据库存储数据,然后连接sys-admin数据库查询数据。

    在这里插入图片描述
    在这里插入图片描述

    2. 判断是否添加注解

    先执行aop拦截判断请求方法上是否有添加多数据源注解
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    3. 前置通知获取value值

    如果有,走aop前置通知回调方法,获取该多数据源注解中的value值,将获取多数据源的value值存放到当前线程的ThreadLocal中
    在这里插入图片描述

    4. 获取数据库连接

    执行数据库查询之前spring会先获取数据库连接,执行第18行时,会走到spring的AbstractRoutingDataSource类中的determineTargetDataSource方法
    在这里插入图片描述
    在这里插入图片描述
    这类中的determineCurrentLookupKey方法会判断是否有重写,如果重新就会进行回调
    在这里插入图片描述
    咱们类MultipleDataSource extends AbstractRoutingDataSource并重写了determineCurrentLookupKey方法,因此,会获取咱们自己存储的值。
    咱们在aop的前置通知中已经将数据源信息存储到了当前线程的ThreadLocal中
    了,因此,这里从ThreadLocal中获取的就是咱们提前存储的值。
    在这里插入图片描述
    获取值后,在回到spring的determineCurrentLookupKey方法中,继续走下面的逻辑
    在这里插入图片描述
    在这里插入图片描述

    5. 执行具体逻辑

    当获取数据连接后,就可以执行具体的逻辑处理了。

    二、代码实战
    2.1. 目标方法
    @Autowired
        private MainManage mainManage;
        @Autowired
        private OrderManage orderManage;
        
     @GetMapping("/getTestDatasource")
       @Override
        public String getTestDatasource() {
            // sys-order数据源  解决事务方案 jta
            SysOrder sysOrder = new SysOrder("mayikt");
            int result = orderManage.insert(sysOrder);
    
            // sys-admin数据源
            MayiktDictionaryData mayiktDictionaryData = mainManage.selectById(1);
            return mayiktDictionaryData.getName() + result;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    @Component
    public class OrderManage {
    
        @Autowired
        private SysOrderMapper orderMapper;
    
        @MayiktDataSource("db2")
        public int insert(SysOrder sysOrder) {
            // sys-order数据源  解决事务方案 jta
            int result = orderMapper.insert(sysOrder);
            return result;
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    @Component
    public class MainManage {
    
        @Autowired
        private MayiktDictionaryDataMapper dictionaryDataMapper;
    
        @MayiktDataSource
        public MayiktDictionaryData selectById(Integer id) {
            // sys-admin数据源
            return dictionaryDataMapper.selectById(id);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2.2. 自定义注解
    /**
     * 多数据源注解
     *
     * @author gblfy
     * @date 2022-09-12
     */
    @Target({ElementType.METHOD,// 方法上
            ElementType.TYPE})// 类上
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MayiktDataSource {
    
        String value() default "db1";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    2.3. aop拦截自定义注解
    package com.mayikt.ext;
    
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    @Component
    @Slf4j
    @Aspect
    @Order(-1)
    public class DataSourceAspect {
    
        @Pointcut("@within(com.mayikt.ext.MayiktDataSource)||@annotation(com.mayikt.ext.MayiktDataSource)")
        public void pointCut() {
    
        }
    
        /**
         * 前置通知
         *
         * @param dataSource
         */
        @Before("pointCut() && @annotation(dataSource)")
        public void doBefore(MayiktDataSource dataSource) {
            log.info("=========选择数据源==========={}", dataSource.value());
            DataSourceContextHolder.setDataSource(dataSource.value());
    
        }
    
        /**
         * 后置通知
         */
        @After("pointCut() ")
        public void doAfter() {
            DataSourceContextHolder.clearDataSource();
            log.info("=========清除上下文数据源===========");
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    2.4. ThreadLocal
    package com.mayikt.ext;
    
    public class DataSourceContextHolder {
    
        public static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<>();
    
        /**
         * 设置数据源
         *
         * @param db
         */
        public static void setDataSource(String db) {
            contextHolder.set(db);
        }
        /**
         * 获取当前数据源
         *
         */
        public static String getDataSource() {
            return contextHolder.get();
        }
        /**
         * 清除上下文数据源
         *
         */
        public static void clearDataSource() {
            contextHolder.remove();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    2.5. 多数据源配置
    package com.mayikt.config;
    
    import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
    import com.mayikt.ext.MultipleDataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    
    @Configuration
    @MapperScan(value = {"com.mayikt.order.mapper", "com.mayikt.main.mapper"})
    public class MybatisPlusConfiguration {
    
        static final String MAPPER_LOCATION = "classpath*:/mapping/*.xml";
    
    
        @Bean("db1DataSource")
        @ConfigurationProperties(prefix = "spring.datasource.admin")
        public DataSource getDb1DataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean("db2DataSource")
        @ConfigurationProperties(prefix = "spring.datasource.order")
        public DataSource getDb2DataSource() {
            return DataSourceBuilder.create().build();
        }
    
        /**
         * 动态数据源配置
         */
        @Bean
        @Primary
        public DataSource multipleDataSource(@Qualifier("db1DataSource") DataSource db1DataSource,
                                             @Qualifier("db2DataSource") DataSource db2DataSource) {
            MultipleDataSource multipleDataSource = new MultipleDataSource();
            HashMap<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put("db1", db1DataSource);// sys-admin
            targetDataSources.put("db2", db2DataSource);// sys-order
    
            //    添加数据源
            multipleDataSource.setTargetDataSources(targetDataSources);
            //    设置默认数据源
            multipleDataSource.setDefaultTargetDataSource(db1DataSource);
            return multipleDataSource;
        }
    
        @Bean("sqlSessionFactory")
        public SqlSessionFactory sqlSessionFactory() throws Exception {
            // SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            // 整合mybatisplus时需要采用MybatisSqlSessionFactoryBean
            MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
            sqlSessionFactory.setDataSource(multipleDataSource(getDb1DataSource(), getDb2DataSource()));
            sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
            return sqlSessionFactory.getObject();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    2.6. mapper
    public interface MayiktDictionaryDataMapper extends BaseMapper<MayiktDictionaryData> {}
    
    • 1
    public interface SysOrderMapper extends BaseMapper<SysOrder> { }
    
    • 1
    2.7. 表结构
    CREATE TABLE `mayikt_dictionary_data` (
      `id` int NOT NULL COMMENT '字典ID',
      `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '字典名称',
      `type_id` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '字典类型ID',
      `create_time` datetime DEFAULT NULL COMMENT '创建时间',
      `update_time` datetime DEFAULT NULL COMMENT '更新时间',
      `is_delete` int DEFAULT NULL COMMENT '是否删除',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='字典子表';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    CREATE TABLE `sys_order` (
      `id` int NOT NULL COMMENT '订单ID',
      `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '订单名称',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='订单表';
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    js页面window.onload()=$(function(){}) 和$(docunment).ready(function(){})
    Java代理
    深入理解java并发编程之aqs框架
    微信小程序 java-php大学校园学生社团系统python
    《小狗钱钱》阅读笔记(三)
    Docker快速部署springboot项目
    Java SE 20 新增特性
    CompletableFuture异步编排
    Linux高性能服务器——状态机
    vue路由传参的详解1.命名路由的传参和取出2.query传参和取出3.meta传参和取出4.其他方式5.注意点 代码和注释
  • 原文地址:https://blog.csdn.net/weixin_40816738/article/details/126831550