SpringBoot实现多数据源(三)【AOP + 自定义注解】

上一篇文章SpringBoot实现多数据源(二)【Mybatis插件】

三、通过 AOP + 自定义注解切换多数据源


适用范围:不同场景下的业务,一般利用AOP,结合自定义注解动态切换数据源

  1. 配置文件application.yml不变,导入依赖
    • pom.xml
<dependencies>
    <!--jdbc-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--aop-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
    </dependency>
    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.8</version>
    </dependency>
    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
    </dependency>

</dependencies>
  1. 自定义读写分离注解
    • @ReadAndWrite
package com.vinjcent.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({
    
    ElementType.METHOD, ElementType.TYPE}) // 可以声明在哪些作用域上
@Retention(RetentionPolicy.RUNTIME)
/**
 * SOURCE、CLASS、RUNTIME:
 * SOURCE: 编译之后不会在target文件的.class中生成,无法通过反射获取
 * CLASS: 会保留在.class中,不会被jvm加载
 * RUNTIME: 运行时生效
 *
 */
public @interface ReadAndWrite {
    
    
    String value() default "write";
}
  1. 配置切面
    • DynamicDataSourceAspect
package com.vinjcent.aspect;

import com.vinjcent.annotation.ReadAndWrite;
import com.vinjcent.config.DynamicDataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 动态数据源切面
 */
@Component
@Aspect
public class DynamicDataSourceAspect {
    
    

    // 前置通知before或环绕通知Around都可以

    // @annotation(rw)为所有ReadAndWrite注解进行增强
    // within(com.vinjcent.service.impl.*) 某个包下的类
    @Before("within(com.vinjcent.service.impl.*) && @annotation(rw)")
    public void before(JoinPoint point, ReadAndWrite rw) {
    
    
        // 获取当前注解中的value值
        String opt = rw.value();
        // 根据opt修改ThreadLocal中name的值,更换动态数据源
        DynamicDataSource.name.set(opt);
        System.out.println(opt);
    }
}

  1. 配置读写分离数据源
    • DataSourceConfiguration
package com.vinjcent.config;

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 javax.sql.DataSource;

@Configuration
public class DataSourceConfiguration {
    
    

    @Bean(name = "readDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.read")
    public DataSource readDatasource() {
    
    
        // 底层会自动拿到spring.datasource中的配置,创建一个DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "writeDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.write")
    public DataSource writeDatasource() {
    
    
        // 底层会自动拿到spring.datasource中的配置,创建一个DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }
}
  1. 动态数据源配置
    • DynamicDataSource
package com.vinjcent.config;

import com.vinjcent.constants.DataSourceConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;


@Component
@Primary    // 将该Bean设置为主要注入Bean
public class DynamicDataSource extends AbstractRoutingDataSource {
    
    

    // 用于存储数据源的标识
    public static ThreadLocal<String> name = new ThreadLocal<>();

    // 写
    private final DataSource writeDataSource;

    // 读
    private final DataSource readDataSource;


    @Autowired
    public DynamicDataSource(@Qualifier("readDatasource") DataSource readDataSource,
                             @Qualifier("writeDatasource") DataSource writeDataSource) {
    
    
        this.readDataSource = readDataSource;
        this.writeDataSource = writeDataSource;
    }

    @Override
    protected Object determineCurrentLookupKey() {
    
    
        return name.get();
    }

    // 初始化完bean之后调用该方法
    @Override
    public void afterPropertiesSet() {
    
    
        // 为targetDataSources 初始化所有数据源
        Map<Object, Object> sources = new HashMap<>();
        sources.put(DataSourceConstants.READ_DATASOURCE, readDataSource);
        sources.put(DataSourceConstants.WRITE_DATASOURCE, writeDataSource);
        super.setTargetDataSources(sources);

        // 为 defaultTargetDataSource 设置默认的数据源
        super.setDefaultTargetDataSource(readDataSource);

        // resolvedDataSources 负责最终切换的数据源map
        super.afterPropertiesSet();
    }
}
  1. Controller 层不变,修改impl中的 Service 层(这里就不再提供entity以及interface,与前面相同
package com.vinjcent.service.impl;

import com.vinjcent.annotation.ReadAndWrite;
import com.vinjcent.mapper.PeopleMapper;
import com.vinjcent.pojo.People;
import com.vinjcent.service.PeopleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class PeopleServiceImpl implements PeopleService {
    
    

    private final PeopleMapper peopleMapper;

    @Autowired
    public PeopleServiceImpl(PeopleMapper peopleMapper) {
    
    
        this.peopleMapper = peopleMapper;
    }

    @ReadAndWrite("read")
    @Override
    public List<People> list() {
    
    
        return peopleMapper.list();
    }

    @ReadAndWrite("write")
    @Override
    public boolean save(People people) {
    
    
        return peopleMapper.save(people);
    }
}
  1. 运行并测试接口

下一篇文章SpringBoot实现多数据源(四)【集成多个 Mybatis 框架】》

猜你喜欢

转载自blog.csdn.net/Wei_Naijia/article/details/128069769
今日推荐