SpringBoot AOP 集成多数据源

今日一说:
生活从来没有容易的事,
如果你感觉到轻松,
那一定是有人在为你负重前行。
愿我们最终都能实现自己的梦想!
——小美同学


AOP详细说明:
AOP为Aspect Oriented Programming的缩写,
意为:面向切面编程,
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,
AOP是OOP的延续,是软件开发中的一个热点,
也是Spring框架中的一个重要内容,
是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,
从而使得业务逻辑各部分之间的耦合度降低
提高程序的可重用性,同时提高了开发的效率。

使用AOP编程可以做哪些事情:
Spring AOP面向切面编程,
可以用来配置事务、
做日志、权限验证、
在用户请求时做一些处理等等。
用@Aspect做一个切面,就可以直接实现

使用AOP编程集成多数据源的maven仓库具体有哪些?

	<!-- springboot-aop包,AOP切面注解,aspect等相关注解 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-aop</artifactId>
	</dependency>
		<!-- mysql 依赖 -->
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
	</dependency>

step1、自定义注解接口类,设置默认数据源主库
我本地的包名(com.app.info
创建datasource包,设置数据源主库从库
定义数据源主库
相关注解介绍:

@Aspect:作用是把当前类标识为一个切面供容器读取
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。
方法签名必须是 publicvoid型。
可以将Pointcut中的方法看作是一个被Advice引用的助记符,
因为表达式不直观,因此我们可以通过方法签名的方式为此表达式命名。因此Pointcut中的方法只需要方法签名,
而不需要在方法体内编写实际代码。
@Around:环绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
@After: final增强,不管是抛出异常或者正常退出都会执行

DataSourceContextHolder
具体实现:
1、使用线程局部变量来存放数据源的名称
2、读写数据源、设置数据源,清空数据源

 package com.egc.app.info.datasource;

import lombok.extern.slf4j.Slf4j;

/**
 * 创建一个ThreadLocal类来存放数据源
 * @author ZB
 */
@Slf4j
public class DataSourceContextHolder {

    /**
     * 设置写数据源
     */
    public static final String MASTER_DATABASE = "MASTER_DATABASE";

    /**
     * 设置读数据源
     */
    public static final String SLAVE_DATABASE = "SLAVE_DATABASE";

    /**
     * 设置一个线程局部变量来存放数据源的名称
     */
    private static final ThreadLocal<String> contextLoader = new ThreadLocal<>();

    /**
     * set数据源
     *
     * @param dbName
     */
    public static void setDB(String dbName) {
        contextLoader.set(dbName);
    }
    /**
     * 获取数据源
     *
     * @return
     */
    public static String getDB() {
        return contextLoader.get();
    }
    /**
     * 清空数据源
     */
    public static void clearDB() {
        contextLoader.remove();
    }
}

DS
具体实现:
1、定义自定义注解接口类,设置默认数据源主库
2、进行加载的时候,会第一时间先去加载主库的默认配置

package com.egc.app.info.datasource;

import java.lang.annotation.*;

/**
 * @author ZB
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DS {

    String value() default DataSourceContextHolder.MASTER_DATABASE;
}

step2、读取配置文件默认值,配置主从数据库
创建database包,设置数据源主库从库
database数据库包

application.properties
(备注:此处的用户名、密码、以及ip地址需根据自身情况
进行调整,此处仅做出参考)

#数据源1
spring.datasource.master.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.master.url = jdbc:mysql://192.168.1.152:3306/database1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.master.username = username1
spring.datasource.master.password = password1
#以下为连接池的相关参数配置
spring.datasource.master.max-idle = 10
spring.datasource.master.max-wait = 10000
spring.datasource.master.min-idle = 5
spring.datasource.master.initial-size = 5
spring.datasource.master.validation-query = SELECT 1
spring.datasource.master.test-on-borrow = false
spring.datasource.master.test-while-idle = true
spring.datasource.master.time-between-eviction-runs-millis = 18800
#数据源2
spring.datasource.slave.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.slave.url = jdbc:mysql://192.168.1.152:3306/database2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.slave.password = username2
spring.datasource.slave.username = password2
#以下为连接池的相关参数配置
spring.datasource.slave.max-idle = 10
spring.datasource.slave.max-wait = 10000
spring.datasource.slave.min-idle = 5
spring.datasource.slave.initial-size = 5
spring.datasource.slave.validation-query = SELECT 1
spring.datasource.slave.test-on-borrow = false
spring.datasource.slave.test-while-idle = true
spring.datasource.slave.time-between-eviction-runs-millis = 18800

DataSourceConfig
具体实现:
1、获取配置文件中spring.datasource.master
前缀的主数据库信息
2、获取配置文件中spring.datasource.slave
前缀的从数据库信息
3、设置了动态数据源交给AOP处理
这边使用AOP来管理数据源,设置为默认数据源

package com.egc.app.info.config.database;

import com.egc.app.info.datasource.DataSourceContextHolder;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
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.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *  配置数据源
 * @author ZB
 */
@Configuration
public class DataSourceConfig {

    /**
     * 写数据源
     *
     * @return
     */
    @Bean(DataSourceContextHolder.MASTER_DATABASE)
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDatabase() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 读数据源
     *
     * @return
     */
    @Bean(DataSourceContextHolder.SLAVE_DATABASE)
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDatabase() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 设置了动态数据源交给AOP处理
     * 这边使用AOP来管理数据源,设置为默认数据源
     *
     * @return
     */
    @Bean("dynamicDataSource")
    @Primary
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //设置默认数据源,如果没有设置数据源,采用此数据源
        dynamicDataSource.setDefaultTargetDataSource(masterDatabase());
        //设置多数据源
        Map<Object, Object> map = new ConcurrentHashMap<>();
        map.put(DataSourceContextHolder.MASTER_DATABASE, masterDatabase());
        map.put(DataSourceContextHolder.SLAVE_DATABASE, slaveDatabase());
        //设置目标数据源
        dynamicDataSource.setTargetDataSources(map);
        return dynamicDataSource;
    }

}

DynamicDataSource
具体实现:
1、继承AbstractRoutingDataSource类,来重写数据源
获取方式
2、使用log打印当前数据源

package com.egc.app.info.config.database;

import com.egc.app.info.datasource.DataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
 * 继承AbstractRoutingDataSource类,来重写数据源获取方式
 * @author ZB
 */
@Slf4j
public class DynamicDataSource  extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        //获取当前的数据源
        String db = DataSourceContextHolder.getDB();
        log.info("==>  AbstractRoutingDataSource当前的数据源是:{}",db);
        return db;
    }
}

step3、创建切面类切换数据源
具体实现:
1、加载时需要获取当前的类,数据源是通过method方法获取的
2、切换数据源的时候,我们必须知道需要切换的数据源是多少

package com.egc.app.info.config.aop;

import com.egc.app.info.datasource.DS;
import com.egc.app.info.datasource.DataSourceContextHolder;
import org.aspectj.lang.JoinPoint;
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 lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 创建一个切面来切换数据源
 * @author ZB
 */
@Aspect
@Component
@Slf4j
public class DynamicDataSourceAspect {

    @Pointcut("@annotation(com.egc.app.info.datasource.DS)")
    public void pointCut(){};

    @Before("pointCut()")
    public void beforeSwitchDS(JoinPoint joinPoint) {
        /**
         * 注意:采用倒推思想,前提是对反射原理一定要了解
         *
         * 1-->6.既然想切换数据源,至少我们要知道切换的数据源value是多少
         * 2-->5.获取数据源的value,前提要获取当前的注解DS
         * 3-->4.DS是通过method方法获取的
         * 4-->3.所以先要获取当前的操作method aClass.method("方法名","参数");
         * 5-->2.获取当前的类
         * 6-->1.获取方法名是是什么,当前参数是什么
         *
         * 思路理清楚,开始写代码
         */
        /**
         * 1.获取当前的方法名、获取当前的参数列表
         *
         * 分析思路:
         * joinPoint.getSignature 获取的是 Signature 对象
         * MemberSignature 继承了 Signature对象
         * CodeSignature 继承了 MemberSignature对象
         * MethodSignature 继承了 CodeSignature
         * 将Signature 向下转换为 MethodSignature 去获取 CodeSignature 的属性 getParameterTypes
         * ok,开始
         */
        String methodName = joinPoint.getSignature().getName();
        Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        //2.获取当前的类
        Class<?> aClass = joinPoint.getTarget().getClass();
        //设置默认数据源
        String dataBaseName = DataSourceContextHolder.MASTER_DATABASE;
        try {
            //3.通过aClass.method("方法名","参数");获取当前方法
            Method method = aClass.getMethod(methodName, parameterTypes);
            //判断当前类是不是存在DS注解
            if (method.isAnnotationPresent(DS.class)) {
                //4.存在,获取当前的注解
                DS ds = method.getAnnotation(DS.class);
                //5.获取value值
                dataBaseName = ds.value();

            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        //6.切换数据源
        log.info("==>  AOP切面before:切换数据源,{}", dataBaseName);
        DataSourceContextHolder.setDB(dataBaseName);
    }
    @After("pointCut()")
    public void afterSwitchDS(){
        log.info("==>  AOP切面after:清除数据源");
        DataSourceContextHolder.clearDB();
    }
}

执行效果:
切换数据源结果

常见错误解析:
org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection
如果是阿里巴巴的数据源
数据配置这里需要更改为
spring.datasource.slave.jdbc-url
如果是其他的数据源必须更改为url
两个都可以尝试一下

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot是一个用于简化Spring应用开发的框架,可以方便地实现多数据源动态切换功能。 在Spring Boot中,通过配置多个数据源bean,可以实现多数据源的使用。首先,在application.properties或application.yml文件中配置多个数据源的相关属性,例如数据库连接信息、驱动程序等。然后,在Spring Boot的配置类中,通过@Bean注解将数据源bean配置到Spring容器中。 为了实现动态切换数据源,可以使用ThreadLocal或者AOP进行数据源的切换。首先,可以定义一个DataSourceContextHolder类,使用ThreadLocal来存储当前线程使用的数据源标识。然后,可以在需要切换数据源的地方,通过调用DataSourceContextHolder的setDataSourceKey方法,设置当前线程使用的数据源标识。在数据访问层的代码中,可以通过调用DataSourceContextHolder的getDataSourceKey方法获取当前线程使用的数据源标识,然后根据该标识来获取对应的数据源bean。 除了使用ThreadLocal,还可以使用AOP实现数据源的切换。可以定义一个切面,通过在切面中获取注解或者方法名来确定使用的数据源,然后通过切换数据源的方式来实现动态切换。 通过以上的方式,就可以实现Spring Boot多数据源动态切换功能。不同的数据源可以根据自己的需求和业务场景进行配置和使用,提高系统的扩展性和灵活性。 ### 回答2: Spring Boot提供了方便的配置和集成多数据源,并且支持动态切换数据源。下面将通过一个简单的示例来说明如何在Spring Boot实现多数据源的动态切换。 首先,在pom.xml文件中添加Spring Boot和数据源相关的依赖项,比如Spring Boot Starter、MyBatis、Druid等。然后,在application.properties文件中配置数据源的相关信息,包括数据库的URL、用户名、密码等。 接下来,创建多个数据源的配置类,通过@Configuration注解将其标记为配置类,并使用@Bean注解来创建数据源对象。在创建数据源对象时,可以使用DruidDataSource或者其他适配的数据源类。 在创建数据源配置类时,可以使用@Primary注解来指定一个主数据源,该主数据源将作为默认数据源使用。对于其他数据源,可以使用@Qualifier注解进行标识。 然后,在创建MyBatis的SqlSessionFactoryBean时,使用@MapperScan注解来扫描并加载Mapper接口。在其中配置数据源,可以通过注入的方式获取数据源对象,并将其设置到SqlSessionFactoryBean中。 最后,在需要切换数据源的地方,可以通过使用AOP切面和动态切换数据源的方式来实现。可以创建一个DataSourceAspect切面类,在其中定义切点和通知,通过在方法上添加@DataSource注解来指定要切换的数据源。在通知方法中,通过读取注解上的参数来确定要切换的数据源,并将其设置到ThreadLocal变量中。 总结起来,Spring Boot多数据源动态切换的步骤包括添加依赖项、配置数据源、创建数据源配置类、配置MyBatis的SqlSessionFactoryBean以及使用AOP实现动态切换数据源。通过这些步骤,我们可以在Spring Boot中轻松实现多数据源的动态切换。 ### 回答3: 在Spring Boot实现多数据源动态切换可以通过以下步骤实现: 1. 配置数据源:在application.properties或yml文件中配置多个数据源的连接信息,每个数据源都有独立的数据源配置属性。 2. 创建数据源工厂:使用Spring的Bean注解创建多个数据源对象,并分别设置其相关属性。 3. 创建数据源路由器:创建一个数据源路由器类,该类包含一个ThreadLocal变量,用于保存当前线程所使用的数据源标识。 4. 创建数据源切换注解:使用自定义注解方式,通过在Service层的方法上加上注解,来选择对应的数据源。 5. 创建切面:使用AOP的方式,在方法执行前获取选择的数据源标识,并存入数据源路由器的ThreadLocal变量中。 6. 创建数据源切换切入点:在切面中设置一个切入点,用于匹配加上数据源切换注解的方法。 7. 配置数据源切面:使用Spring的Bean注解配置切面类。 8. 启动类中配置数据源切换:在启动类中添加@EnableAspectJAutoProxy注解来开启AOP,同时使用@Import注解引入切面类。 9. 使用数据源切换注解:在Service层的方法上加上数据源切换注解,指定使用哪个数据源。 通过以上步骤,就可以在使用Spring Boot实现多数据源动态切换。在需要切换数据源的地方,只需要使用自定义的注解来指定数据源,切换的过程由切面和数据源路由器来完成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值