手写Spring注解事务(利用AOP技术 + 注解 + Spring编程式事务)

1.参考下面的文章搭建一个无事务管理的SSM操作数据库的框架

      Spring 使用Druid数据源 整合 Mybatis

2.AOP技术参考

      AOP技术应用实现

3.第一步首先实现Spring编程式事务

       1) 创建事务管理类工具,即手动开启事务,手动提交事务,手动回滚事务的方法

package com.roger.core.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Component
@Scope("prototype")//设置成原型状态,避免线程安全问题
public class TransactionUtil {

    private TransactionStatus transactionStatus = null;

    @Autowired
    private DataSourceTransactionManager transactionManager;


    public void begin(){
        System.out.println("方法上有事务注解,使用手动的方式开启事务...");
        transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
    }

    public void commit(){
        if(transactionStatus != null){
            System.out.println("提交事务...");
            transactionManager.commit(transactionStatus);
        }
    }

    public void rollback(){
        if(transactionStatus != null) {
            System.out.println("回滚事务...");
            transactionManager.rollback(transactionStatus);
        }
    }
}

         2) 创建编程式事务管理的切面类

package com.roger.core.aspect;

import com.roger.core.utils.TransactionUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
//一定要把切面交给Spring管理
//在测试声明式事务的时候,需要从Spring的容器中收回编程式事务的切面
//@Component
@Aspect
public class ProgramTransactionAspect {

    @Autowired(required = false)
    private TransactionUtil transactionUtil;

    @Pointcut("execution(* com.roger.biz.service.impl..*.*(..))")
    public void addTransaction(){

    }

    //异常通知:给添加事务的方法回滚事务,当方法抛出异常时
    @AfterThrowing("addTransaction()")
    public void rollbackTransaction(){
        //获取当前事务,然后回滚
        transactionUtil.rollback();
    }

    //环绕通知:给需要添加事务的方法,手动开启事务和提交事务
    @Around("addTransaction()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable{
        transactionUtil.begin();
        joinPoint.proceed();
        transactionUtil.commit();
    }
}

     3).创建测试接口以及测试接口实现类

package com.roger.biz.service;

import com.roger.core.model.User;

public interface UserService {

    void save(User user);
}
package com.roger.biz.service.impl;

import com.roger.biz.dao.UserDao;
import com.roger.biz.service.UserService;
import com.roger.core.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired(required = false)
    private UserDao userDao;

    @Override
    public void save(User user) {
        userDao.insert(user);
        //利用最简单的构造异常来验证事务的回滚操作
        //通过回滚操作来和事务提交作对比
       // int i = 1 / 0 ;
    }
}

    4.创建junit4测试类 --测试基类

package com.roger;

import com.roger.core.config.DataSourceConfig;
import com.roger.core.config.SpringConfig;
import com.roger.core.config.mybatis.SqlSessionConfig;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DataSourceConfig.class, SqlSessionConfig.class,SpringConfig.class})
public class SpringBaseTestSuit {

}

    5.具体测试接口的junit4测试类

package com.roger.biz.service.impl;

import com.roger.SpringBaseTestSuit;
import com.roger.biz.service.UserService;
import com.roger.core.model.User;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import static org.junit.Assert.*;

public class UserServiceImplTest extends SpringBaseTestSuit {

    @Autowired(required = false)
    private UserService userService;

    @Test
    public void save() {
        User user = new User();
        user.setUserName("Jackson");
        user.setAge(38);
        user.setPhone("15498756489");
        userService.save(user);
    }
}

      6.通过观察数据库操作表的记录来观察结果,这里没有写太多的测试方法,来直接用Junit验证测试结果

4.自定义事务注解-非常简单的,只使用于方法级别的

package com.roger.core.annotaion;

import java.lang.annotation.*;

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

    String value() default "";
}

5.改造编程式事务的切面类-使其适应注解方式

package com.roger.core.aspect;

import com.roger.core.annotaion.CustomTransactional;
import com.roger.core.utils.TransactionUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

////在测试编程式事务的时候,需要从Spring的容器中收回声明式事务的切面
@Component//一定要把切面交给Spring管理
@Aspect
public class DeclareTransactionAspect {

    @Autowired(required = false)
    private TransactionUtil transactionUtil;

    @Pointcut("execution(* com.roger.biz.service.impl..*.*(..))")
    public void addTransaction() {

    }

    //异常通知:给添加事务的方法回滚事务,当方法抛出异常时
    @AfterThrowing("addTransaction()")
    public void rollbackTransaction() {
        transactionUtil.rollback();
    }

    //环绕通知:给需要添加事务的方法,手动开启事务和提交事务
    @Around("addTransaction()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        Class<?> targetCls = joinPoint.getTarget().getClass();
        //获取即将要执行方法
        Method method = getInvokedMethod(targetCls, joinPoint);
        if (method == null) {
            joinPoint.proceed();
            return;
        }
        //判断执行方法是否有事务注解
        Annotation customTransAnno = method.getAnnotation(CustomTransactional.class);
        if (customTransAnno == null) {
            System.out.println("方法上没有事务注解,直接开始执行方法...");
            joinPoint.proceed();
            return;
        }

        transactionUtil.begin();
        joinPoint.proceed();

        transactionUtil.commit();
    }

    private Method getInvokedMethod(Class targetCls, ProceedingJoinPoint pJoinPoint) {
        List<Class<? extends Object>> clazzList = new ArrayList<>();
        Object[] args = pJoinPoint.getArgs();
        for (Object arg : args) {
            clazzList.add(arg.getClass());
        }

        Class[] argsCls = (Class[]) clazzList.toArray(new Class[0]);

        String methodName = pJoinPoint.getSignature().getName();
        Method method = null;
        try {
            method = targetCls.getMethod(methodName, argsCls);
        } catch (NoSuchMethodException e) {
            //不做任何处理,这个切面只处理事务相关逻辑
            //其他任何异常不在这个切面的考虑范围
        }
        return method;
    }
}

6.github代码下载

猜你喜欢

转载自blog.csdn.net/lihongtai/article/details/84746598