spring-boot配置多数据源(动态数据源,基于注解和切面方式)

本文详细介绍了如何在Spring Boot应用中使用自定义注解配置多个数据源,包括DS注解、properties文件的配置、DataSourceConstants常量定义、DynamicDataSource的实现以及AOP切面处理。通过@DS注解标记方法,可以根据需要灵活切换数据源的操作。
摘要由CSDN通过智能技术生成

一、自定义DB注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {
    /**
     * 数据源名称
     */
    String value();

}

二、配置properties数据源

文件名:jdbc.properties

# slave-authserver
spring.datasource.slave.authserver.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.authserver.jdbc-url=jdbc:mysql://localhost:3306/smart-authserver?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.datasource.slave.authserver.username=root
spring.datasource.slave.authserver.password=123456

# slave-data
spring.datasource.slave.data.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.data.jdbc-url=jdbc:mysql://localhost:3306/smart-data?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.datasource.slave.data.username=root
spring.datasource.slave.data.password=123456

# slave-kpi
spring.datasource.slave.kpi.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.kpi.jdbc-url=jdbc:mysql://localhost:3306/smart-kpi?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.datasource.slave.kpi.username=root
spring.datasource.slave.kpi.password=123456

# slave-logistics
spring.datasource.slave.logistics.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.logistics.jdbc-url=jdbc:mysql://localhost:3306/smart-logistics?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.datasource.slave.logistics.username=root
spring.datasource.slave.logistics.password=123456

三、设置各数据源常量

public interface DataSourceConstants {

    /**
     * 从数据库-数据中心
     */
    String DS_KEY_SLAVE_DATA = "DS_KEY_SLAVE_DATA";

    /**
     * 从数据库-单点系统
     */
    String DS_KEY_SLAVE_AUTH = "DS_KEY_SLAVE_AUTH";

    /**
     * 从数据库-后勤系统
     */
    String DS_KEY_SLAVE_LOGISTICS = "DS_KEY_SLAVE_LOGISTICS";

    /**
     * 从数据库-考核系统
     */
    String DS_KEY_SLAVE_KPI = "DS_KEY_SLAVE_KPI";

}

四、Spring配置多数据源

import com.jnx.smart.authserver.db.DynamicDataSource;
import com.jnx.smart.base.constant.DataSourceConstants;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
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.context.annotation.PropertySource;

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

@Configuration
@PropertySource("classpath:jdbc.properties")
@MapperScan(basePackages = {
        "com.jnx.smart.authserver.business.repository.dao",
        "com.jnx.smart.data.business.repository.dao",
        "com.jnx.smart.assess.business.repository.dao",
        "com.jnx.smart.logistics.business.repository.dao"})
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class DynamicDataSourceConfig {

    @Bean
    @Primary
    public DataSource dynamicDataSource() {
        Map<Object, Object> dataSourceMap = new HashMap<>(2);
        dataSourceMap.put(DataSourceConstants.DS_KEY_SLAVE_AUTH, slaveDataSourceAuth());
        dataSourceMap.put(DataSourceConstants.DS_KEY_SLAVE_DATA, slaveDataSourceData());
        dataSourceMap.put(DataSourceConstants.DS_KEY_SLAVE_KPI, slaveDataSourceKpi());
        dataSourceMap.put(DataSourceConstants.DS_KEY_SLAVE_LOGISTICS, slaveDataSourceLogistics());
        //设置动态数据源
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        //单点登录系统,设置默认数据源为点点登陆数据库
        dynamicDataSource.setDefaultTargetDataSource(slaveDataSourceAuth());

        return dynamicDataSource;
    }

    /**
     * 主数据源-auth
     *
     * @return 后勤管理数据源bean
     */
    @Bean("DS_KEY_SLAVE_AUTH")
    @ConfigurationProperties(prefix = "spring.datasource.slave.authserver")
    public DataSource slaveDataSourceAuth() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 从数据源-data
     *
     * @return 文件系统数据源bean
     */
    @Bean("DS_KEY_SLAVE_DATA")
    @ConfigurationProperties(prefix = "spring.datasource.slave.data")
    public DataSource slaveDataSourceData() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 从数据源-kpi
     *
     * @return 文件系统数据源bean
     */
    @Bean("DS_KEY_SLAVE_KPI")
    @ConfigurationProperties(prefix = "spring.datasource.slave.kpi")
    public DataSource slaveDataSourceKpi() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 从数据源-logistics
     *
     * @return 文件系统数据源bean
     */
    @Bean("DS_KEY_SLAVE_LOGISTICS")
    @ConfigurationProperties(prefix = "spring.datasource.slave.logistics")
    public DataSource slaveDataSourceLogistics() {
        return DataSourceBuilder.create().build();
    }
}

五、数据源的上下文配置

数据源上下文设置

import com.jnx.smart.base.constant.DataSourceConstants;

/**
 * 动态数据源数据存储
 *
 * @author Joi
 */
public class DynamicDataSourceContextHolder {

    /**
     * 动态数据源名称上下文
     */
    private static final ThreadLocal<String> DATASOURCE_CONTEXT_KEY_HOLDER = new ThreadLocal<>();

    /**
     * 设置/切换数据源
     */
    public static void setContextKey(String key) {
        DATASOURCE_CONTEXT_KEY_HOLDER.set(key);
    }

    /**
     * 获取数据源名称
     */
    public static String getContextKey() {
        String key = DATASOURCE_CONTEXT_KEY_HOLDER.get();
        return key == null ? DataSourceConstants.DS_KEY_SLAVE_AUTH : key;
    }

    /**
     * 删除当前数据源名称
     */
    public static void removeContextKey() {
        DATASOURCE_CONTEXT_KEY_HOLDER.remove();
    }
}

获取上下文的数据源

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 动态数据源类
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    
    @Override
    protected Object determineCurrentLookupKey() {
        // 当设置数据源 key 到上下文,则从上下文中得到此数据源 key
        return DynamicDataSourceContextHolder.getContextKey();
    }
    
}

AOP切面处理

import com.jnx.smart.base.annotation.DS;
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.springframework.stereotype.Component;

import java.util.Objects;

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

    @Pointcut("@annotation(com.jnx.smart.base.annotation.DS)")
    public void dataSourcePointCut() {

    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String dsKey = getDSAnnotation(joinPoint).value();
        DynamicDataSourceContextHolder.setContextKey(dsKey);
        try {
            return joinPoint.proceed();
        } finally {
            DynamicDataSourceContextHolder.removeContextKey();
        }
    }

    /**
     * 根据类或方法获取数据源注解
     */
    private DS getDSAnnotation(ProceedingJoinPoint joinPoint) {
        Class<?> targetClass = joinPoint.getTarget().getClass();
        DS dsAnnotation = targetClass.getAnnotation(DS.class);
        // 先判断类的注解,再判断方法注解
        if (Objects.nonNull(dsAnnotation)) {
            return dsAnnotation;
        } else {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            return methodSignature.getMethod().getAnnotation(DS.class);
        }
    }

}

六、使用

在完成以上配置后,直接在需要使用的service方法上,打上注解任选一个需要的数据源@DB(value=DataSourceConstants.DS_KEY_SLAVE_DATA)
,程序执行时就会从指定的数据源进行操作。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值