AOP自定义注解 环绕通知动态切换数据源


前言

提示:这里可以添加本文要记录的大概内容:
Spring Boot实现自定义注解切换多数据源,在前几天项目的升级的过程中,由于业务原因扩展了三个新的数据库,则编写了 一个自定义注解切换数据源.


提示:以下是本篇文章正文内容,下面案例可供参考

一、pom依赖导入

	<!--  专用数据库连接池 引入 -->
		<dependency>
			<groupId>com.zaxxer</groupId>
			<artifactId>HikariCP</artifactId>
		</dependency>
		        <!-- aop依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- mysql-->

二、YML文件配置

hikari:
  master:
    jdbc-url: jdbc:mysql://192.168.1.180:3306/syb?useUnicode=true&characterEncoding=utf8&useSSL=true&allowMultiQueries=true&verifyServerCertificate=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
    username: leader
    password: leader
    maximum-pool-size: 20
    pool-name: master
    connection-timeout: 30000
    idle-timeout: 600000
    max-lifetime: 1765000
  database:
    jdbc-url: jdbc:mysql://192.168.1.211:3306/database_db?useUnicode=true&characterEncoding=utf8&useSSL=true&allowMultiQueries=true&verifyServerCertificate=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
    username: leader
    password: leader
    maximum-pool-size: 80
    pool-name: database
    connection-timeout: 30000
    idle-timeout: 600000
    max-lifetime: 1765000
  qht:
    jdbc-url: jdbc:mysql://192.168.1.11:3306/qihaotong?useUnicode=true&characterEncoding=utf8&useSSL=true&allowMultiQueries=true&verifyServerCertificate=false&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
    username: qihaotong
    password: 123456
    maximum-pool-size: 80
    pool-name: qht
    connection-timeout: 30000
    idle-timeout: 600000
    max-lifetime: 1765000

具体实现

DataSourcesEnum 枚举类

这个类中存放的需要转换的数据源枚举, 列:切换数据源的时候可以直接 DataSourcesEnum.QHT

public enum DataSourcesEnum {
MASTER,DATABASE,QHT;
}

Properties 数据源名称对应的实体类

import com.zaxxer.hikari.HikariDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

 /**
 * 数据源配置的实体类
 * @author zzz11
 * @version 1.0
 * @date 2023/11/20 16:05
 * 博客:<a href="https://blog.csdn.net/qq_49354230?type=blog">...</a>
 */
@Component
@Data
@ConfigurationProperties(prefix = "hikari")
public class Properties {
	private HikariDataSource master;
	private HikariDataSource database;
	private HikariDataSource qht;
}

DataSourceConfig 数据源配置

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.PlatformTransactionManager;

import com.example.publicdemo.Enum.DataSourcesEnum;
import com.example.publicdemo.dbs.DynamicDataSource;

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

/**
 *数据源配置
 * @author zzz11
 * @version 1.0
 * @date 2023/11/20 16:10
 * 博客:<a href="https://blog.csdn.net/qq_49354230?type=blog">...</a>
 */
@Configuration
@EnableScheduling
@Slf4j
public class DataSourceConfig {

	@Autowired
	private Properties properties;

	@Bean(name = "dataSource")
	public DataSource dataSource() {
		//按照目标数据源名称和目标数据源对象的映射存放在Map中
		Map<Object, Object> targetDataSources = new HashMap<>();
		targetDataSources.put(DataSourcesEnum.MASTER, properties.getMaster());
		targetDataSources.put(DataSourcesEnum.DATABASE, properties.getDatabase());
		targetDataSources.put(DataSourcesEnum.QHT, properties.getQht());
		//采用是想AbstractRoutingDataSource的对象包装多数据源
		DynamicDataSource dataSource = new DynamicDataSource();
		dataSource.setTargetDataSources(targetDataSources);
		//设置默认的数据源,当拿不到数据源时,使用此配置
	//指定目标数据源的映射,以查找键为键。映射的值可以是相应的 DataSource 实例,也可以是数据源名称 String(通过 DataSourceLookup进行解析)。
		dataSource.setDefaultTargetDataSource(properties.getMaster());
		log.info("条件默认数据源为主数据源");
		return dataSource;
	}

	@Bean
	public PlatformTransactionManager txManager() {
		return new DataSourceTransactionManager(dataSource());
	}

MyDataSource 注解类


import com.example.publicdemo.Enum.DataSourcesEnum;

import java.lang.annotation.*;

/**
 * @author zzz11
 * @version 1.0
 * @date 2023/11/20 16:21
 * 博客:<a href="https://blog.csdn.net/qq_49354230?type=blog">...</a>
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyDataSource {

   //要切换的动态数据库名称
    DataSourcesEnum value();
}

MyDataSourceAspect 环绕通知切换数据源

package com.example.publicdemo.dbproperties;

import com.example.publicdemo.Enum.DataSourcesEnum;
import com.example.publicdemo.annottation.MyDataSource;
import com.example.publicdemo.dbs.DynamicDataSourceHolder;
import lombok.extern.slf4j.Slf4j;
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.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Date;

/**
 * @author zzz11
 * @version 1.0
 * @date 2023/11/20 16:22
 * 博客:<a href="https://blog.csdn.net/qq_49354230?type=blog">...</a>
 */
@Slf4j
@Component
@Aspect
public class MyDataSourceAspect {


    /**
     * 定义连接点
     *
     * @within 用于匹配标注有特定注解的类的目标对象
     * @annotation 用于匹配目标方法上标注有特定注解的方法
     */
    @Pointcut("@annotation(com.example.publicdemo.annottation.MyDataSource) || @within(com.example.publicdemo.annottation.MyDataSource)")
    public void myPointcut() {
    }

    @Around(value = "myPointcut()")
    public void myAspect(ProceedingJoinPoint joinPoint) {
        DataSourcesEnum value = null;
        try {

            //通过 getClass() 方法获取目标对象的类,返回的是一个 Class 对象,该对象包含了目标对象的类信息,例如类的名称、方法、字段
            Class<?> aClass = joinPoint.getTarget().getClass();
            //判断这个是否存在MyDataSource注解 如果存在返回true 否则返回false
            //检查类上是否有MyDataSource注解
            if (aClass.isAnnotationPresent(MyDataSource.class)) {
                MyDataSource annotation = aClass.getAnnotation(MyDataSource.class);
                value = annotation.value();
            }
            //用于获取该类中所有声明的方法,不包括继承自父类的方法。返回的是一个 Method 对象数组,每个元素代表一个类中声明的方法
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                //检查方法上是否存在MyDataSoruce注解 如果存在就切换数据源
                if (method.isAnnotationPresent(MyDataSource.class)) {
                    MyDataSource annotation = method.getAnnotation(MyDataSource.class);
                    value = annotation.value();
                }
            }
            //切换数据源
            log.info("切换数据源,目标数据源-------------"+value+"-------------");
            DynamicDataSourceHolder.putDataSource(value);
            joinPoint.proceed();
        } catch (Throwable e) {
            log.error("当前线程 " + Thread.currentThread().getName() + "向ThreadLocal添加数据发生异常", e);
            e.printStackTrace();
        }  finally {
            log.info("切换会默认数据源"+"-----时间:"+new Date()+"-----");
            log.info("切换会默认数据源"+"-----"+DataSourcesEnum.MASTER+"-----");
            DynamicDataSourceHolder.putDataSource(DataSourcesEnum.MASTER);

        }
    }
}

具体调用

@MyDataSource(DataSourcesEnum.QHT)
    public void timingPullTittle() throws Exception {
        List<DouYinLittleProcedure> douYinLittleProcedures = procedureMapper.selectTittleInfo();
    }
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值