Springboot+AOP实现多数据源事务处理

本篇是通过aop实现多数据源事务的处理,多数据源事务网上页有很多例子,但大多数是运用分布式事务实现的,对于一站式应用总感觉怪怪的,于是总结了一下通过aop实现多数据源事务的例子。

一、引入aop依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

二、添加两个注解,事务和事务管理

1、事务注解 TransactionAnno,用于标注使用事务

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TransactionAnno {
}

2、事务管理注解 TransactionManagerAnno,用于对多个事务管理对象进行管理

import org.springframework.context.annotation.Bean;
import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Bean
public @interface TransactionManagerAnno {
}

三、核心:定义切点和切面

import com.boot.framework.common.config.ApplicationContextHolder;
import com.boot.framework.common.utils.FileUtil;
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.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;

/**
 * Created by wxp on 2019/8/28.
 */
@Aspect
@Component
public class TransactionOperateAspect {

    private Set<String> multiTransactional=new HashSet<>();

    @Pointcut("@annotation(com.boot.framework.common.config.aop.TransactionAnno)")
    public void transactionAnno(){}

    @Pointcut("@annotation(com.boot.framework.common.config.aop.TransactionManagerAnno)")
    public void TransactionManagerAnno(){}

    @Around(value = "transactionAnno()")
    public Object manageTransaction(ProceedingJoinPoint pjp) throws Throwable{
        Object result = null;
        Stack<DataSourceTransactionManager> dataSourceTransactionManagers = new Stack<>();
        Stack<TransactionStatus> transactionStatuses = new Stack<>();

        try {
            if (!openTransaction(dataSourceTransactionManagers, transactionStatuses))
                return result;
            result = pjp.proceed();
            commit(dataSourceTransactionManagers, transactionStatuses);
        }catch (Throwable e){
            rollback(dataSourceTransactionManagers, transactionStatuses);
            throw e;
        }

        return result;
    }

    /**
     * 开启事务
     * @param dataSourceTransactionManagers
     * @param transactionStatuses
     * @return
     */
    private boolean openTransaction(Stack<DataSourceTransactionManager> dataSourceTransactionManagers,
                                    Stack<TransactionStatus> transactionStatuses){

        if (multiTransactional.size() == 0){
            //获取所有数据源文件
            ArrayList<Class<?>> classList = FileUtil.getClasses("com.boot.framework.common.config.datasource");
            for (Class<?> object: classList){
                //获取类中所有方法
                Method[] methods = object.getDeclaredMethods();
                for (Method method : methods){
                    //方法上吐过存在TransactionManagerAnno注解则将方法添加进multiTransactional对象中
                    TransactionManagerAnno managerAnno = method.getAnnotation(TransactionManagerAnno.class);
                    if (managerAnno != null)//
                        multiTransactional.add(method.getName());
                }
            }
            //无数据源事物
            if (multiTransactional.size() == 0)
                return false;
        }

        //将列表中的事务入栈
        for (String transactionManagerName : multiTransactional){
            DataSourceTransactionManager dataSourceTransactionManager = ApplicationContextHolder.getBean(transactionManagerName);
            TransactionStatus status = dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());
            transactionStatuses.push(status);
            dataSourceTransactionManagers.push(dataSourceTransactionManager);
        }

        return true;
    }

    /**
     * 提交栈中事务
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     */
    private void commit(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
                        Stack<TransactionStatus> transactionStatuStack){
        while (!dataSourceTransactionManagerStack.isEmpty()){
            dataSourceTransactionManagerStack.pop().commit(transactionStatuStack.pop());
        }
    }

    /**
     * 回滚栈中事务
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     */
    private void rollback(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
                          Stack<TransactionStatus> transactionStatuStack){
        while (!dataSourceTransactionManagerStack.isEmpty()){
            dataSourceTransactionManagerStack.pop().rollback(transactionStatuStack.pop());
        }
    }
}

四、bean操作方法,在切面上使用到

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

@Service
public class ApplicationContextHolder implements ApplicationContextAware, DisposableBean {

    private static ApplicationContext applicationContext = null;

    private static Logger logger = LoggerFactory.getLogger(ApplicationContextHolder.class);
    
//    private static final Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
    /**
     * 取得存储在静态变量中的ApplicationContext.
     */
    public static ApplicationContext getApplicationContext() {
        assertContextInjected();
        return applicationContext;
    }

    /**
     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        assertContextInjected();
        return (T) applicationContext.getBean(name);
    }

    /**
     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    public static <T> T getBean(Class<T> requiredType) {

        assertContextInjected();
        return applicationContext.getBean(requiredType);
    }

    /**
     * 清除SpringContextHolder中的ApplicationContext为Null.
     */
    public static void clearHolder() {
        logger.debug("清除SpringContextHolder中的ApplicationContext:"
                + applicationContext);
        applicationContext = null;
    }

    /**
     * 实现ApplicationContextAware接口, 注入Context到静态变量中.
     */
    public void setApplicationContext(ApplicationContext applicationContext) {
//      logger.debug("注入ApplicationContext到SpringContextHolder:{}", applicationContext);

        if (ApplicationContextHolder.applicationContext != null) {
            logger.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + ApplicationContextHolder.applicationContext);
        }

        ApplicationContextHolder.applicationContext = applicationContext; // NOSONAR
    }

    /**
     * 实现DisposableBean接口, 在Context关闭时清理静态变量.
     */
    public void destroy() throws Exception {
        ApplicationContextHolder.clearHolder();
    }

    /**
     * 检查ApplicationContext不为空.
     */
    private static void assertContextInjected() {
        Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
    }
}

五、使用

1、将数据源上的事务管理由配置bean改为加上@TransactionManagerAnno,如:

在这里插入图片描述

2、在需要事务的方法上加上事务注解@TransactionAnno即可
注意:不支持嵌套事务

六、原理

当执行标注@TransactionAnno注解的方法时,会将所有的数据源事务管理器名字度存入局部变量集类型的multiTransactional,然后在访问这个方法前通过multiTransactional里面的所有数据源事务管理器名字取到对应的bean,手动开启事务后将的TransactionStatus对象全部放入一个栈中,(使用栈是因为要保障事务管理器出现的顺序,而不会因为顺序问题而报错),然后在方法正常执行完之后提交事务,在捕获到异常后回滚事务,并抛出事务。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章