SpringAop的基本使用------进阶版

代码地址
SpringAop
觉得博主还可以给个Star

前面我们进行了基础版本的简介,主要是五个通知。
接下来我们应用到实际场景(事务管理):
pom.xml

<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.14</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

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

MsgMapper.java

package com.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface MsgMapper {
    
    

    @Select("INSERT INTO msg VALUES(NULL,'long',1);")
    void insert();
}

MsgService.java

package com.service;

import com.mapper.MsgMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MsgService {
    
    

    @Autowired
    MsgMapper mapper;

    public void insert(){
    
    
        System.out.println("方法执行中");
        mapper.insert();
    }
}

MsgController.java

package com.web;

import com.service.MsgService;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MsgController {
    
    

    @Autowired
    MsgService msgService;

    @RequestMapping(value = "/test",method = RequestMethod.GET)
    public String test( int j){
    
    
        msgService.insert();
        return "success";
    }
}

Application.java

package com;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(Application.class);
    }
}

我们利用最基础的ssm框架来进行模拟事务管理的场景
我们都知道一个叫做事务回滚。
事务回滚:事务是一组组合成逻辑工作单元的操作,虽然系统中可能会出错,但事务将控制和维护事务中每个操作的一致性和完整性。一旦事务中某个逻辑出错了,我们将会撤回之前所有做的一切。
ssm可以说是很好的代表,在我们进行数据库操作的过程中,假如第一步进行数据存入,而第二步将第一步的数据做一定处理,然后再交给第三步进行数据库存储,而第一步和第三步必要有对应关系。在这一种情况之下,我们就必须保证第二步的处理不能出错,一旦出错,就没有了第三步,对应关系也没有了。所以我们要把这三步包在一个事务内,一旦某个步骤出错了,我们可以回滚,就避免了没有对应关系的问题。
好了,说了这么多,用代码来解释吧。
先运行以上代码。
运行成功,访问http://127.0.0.1:8080/test?j=1
数据库数据增加了一条
在这里插入图片描述
好了,这是正常现象,那么我在MsgService里面的方法加上一个会报错的运算。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210224200739952.png

这么一看,我们知道在运算的时候一定会报错,但是数据还是会插入到数据库
重新运行,并访问http://127.0.0.1:8080/test?j=1
在这里插入图片描述
我们发现数据还是插入进去了,但是这个数据在理论逻辑是没有插入的,这时候我们就需要用到事务回滚了。

  1. @Transactional
    修改MsgService.java
package com.service;

import com.mapper.MsgMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MsgService {
    
    

    @Autowired
    MsgMapper mapper;

    @Transactional
    public void insert(int j){
    
    
        System.out.println("方法执行中");
        mapper.insert();
        int i = j / 0;
    }
}

我们直接加上注解,重新运行,并访问http://127.0.0.1:8080/test?j=1
在这里插入图片描述
数据库中没有增加数据。显然,我们达到了事务回滚的效果。

  1. DataSourceTransactionManager
    DataSourceTransactionManager这种实现方法需要手写,我们来手写这个事务吧
    创建TransactionUtil.java
package com.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

@Component
public class TransactionUtil {
    
    

    /**
     * 获取当前事务管理器
     */
    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;

    public TransactionStatus begin(){
    
    
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
        return transaction;
    }

    /**
     * 提交事务
     * @param transactionStatus
     */
    public void commit(TransactionStatus transactionStatus){
    
    
        dataSourceTransactionManager.commit(transactionStatus);
    }

    /**
     * 回滚事务
     */
    public void rollback(TransactionStatus transactionStatus){
    
    
        dataSourceTransactionManager.rollback(transactionStatus);
    }
}

MsgService.java代码再次修改

package com.service;

import com.mapper.MsgMapper;
import com.util.TransactionUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

@Service
public class MsgService {
    
    

    @Autowired
    MsgMapper mapper;
    
    @Autowired
    TransactionUtil transactionUtil;
    
    public void insert(int j){
    
    
        TransactionStatus transactionStatus = transactionUtil.begin();
        try{
    
    
            System.out.println("方法执行中");
            mapper.insert();
            int i = j / 0;
            transactionUtil.commit(transactionStatus);
        }catch (Exception e){
    
    
            transactionUtil.rollback(transactionStatus);
        }
    }
}

一样可以实现。但是问题来了,我们以上两种都是针对于一个方法,那如果方法多了,不可能我们每次都加注解或者手写一次,不太现实,我们回到基础版说的,减少冗余代码。对,我们还可以利用aop来完成事务回滚
3. AOP实现事务回滚
创建 TransactionAop.java

扫描二维码关注公众号,回复: 13044309 查看本文章
package com.aop;

import com.util.TransactionUtil;
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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;

@Aspect
@Component
@EnableAspectJAutoProxy
public class TransactionAop{
    
    

    @Autowired
    TransactionUtil transactionUtil;

    @Pointcut("execution(* com.service..*.*(..))")
    public void transactionAop(){
    
    

    }

    @Around("transactionAop()")
    public void doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        TransactionStatus transactionStatus = transactionUtil.begin();
        try{
    
    
            joinPoint.proceed();
            transactionUtil.commit(transactionStatus);
        }catch (Exception e){
    
    
            transactionUtil.rollback(transactionStatus);
        }
    }
}

这样就实现了,针对所有的方法,但是。我们知道bean的创建,默认是单例。如果是多个方法使用一个单例,那么就会产生阻塞,那么效率会变得很慢。所以我们需要把TransactionAop变成多例,加上注解@Scope(“prototype”)即可。但还有疑问,那为什么不加在TransactionUtil上,把TransactionUtil变成多例的?因为TransactionUtil在TransactionAop中,一个单例中存在多例,那么这个多例会失效,实质上还是单例。

还有着一个界面问题,我们现在会发现,运算异常的存在,导致我们的界面一直500,处理方法,更改TransactionAop的doAround方法。如下

@Around("transactionAop()")
    public Integer doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        TransactionStatus transactionStatus = transactionUtil.begin();
        try{
    
    
            joinPoint.proceed();
            transactionUtil.commit(transactionStatus);
            return 1;
        }catch (Exception e){
    
    
            transactionUtil.rollback(transactionStatus);
            return 0;
        }
    }

还有一种情况,不想交给AOP处理,我们可以利用手动回滚事务
修改MsgService.java中的insert方法

public void insert(int j){
    
    
        try{
    
    
            System.out.println("方法执行中");
            mapper.insert();
            int i = j / 0;
        }catch (Exception e){
    
    
            // 手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

这里要注意,在此方法中捕捉了异常,那么在TransactionAop是捕捉不到异常的,所以TransactionAop中的回滚并未执行。

猜你喜欢

转载自blog.csdn.net/weixin_43911969/article/details/114037810
今日推荐