通过AOP环绕通知如何实现事务控制

 更新时间:2021年09月07日 08:40:56   作者:怪咖软妹@  
这篇文章主要介绍了通过AOP环绕通知如何实现事务控制的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

通过AOP环绕通知实现事务控制

1、导入相关的依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
         <groupId>c3p0</groupId>
         <artifactId>c3p0</artifactId>
         <version>0.9.1.2</version>
     </dependency>
     <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.8.7</version>
      </dependency>
</dependencies>

2、配置连接池和开启AOP注解

以下采用的是xml配置方式,当然也可以使用纯注解配置

<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!--连接数据库的必备信息-->
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
    <property name="user" value="root"></property>
    <property name="password" value="root"></property>
</bean>
<!--开启spring对注解AOP的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

2、创建链接工具类

package com.gzl.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
/**
 * 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定
 */
@Component("connectionUtils")
public class ConnectionUtils {
    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    @Autowired
    private DataSource dataSource;
    /**
     * 获取当前线程上的连接
     * @return
     */
    public Connection getThreadConnection() {
        try{
            //1.先从ThreadLocal上获取
            Connection conn = tl.get();
            //2.判断当前线程上是否有连接
            if (conn == null) {
                //3.从数据源中获取一个连接,并且存入ThreadLocal中
                conn = dataSource.getConnection();
                tl.set(conn);
            }
            //4.返回当前线程上的连接
            return conn;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
    /**
     * 把连接和线程解绑
     */
    public void removeConnection(){
        tl.remove();
    }
}

3、AOP环绕事务类

package com.gzl.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
 */
@Component("txManager")
@Aspect
public class TransactionManager {
    @Autowired
    private ConnectionUtils connectionUtils;
  /**
     * 需要进行事务控制的类或者方法,EL表达式配置
     */
    @Pointcut("execution(* com.gzl.service.impl.*.*(..))")
    private void pt1(){}
    /**
     * 开启事务
     */
    public  void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 提交事务
     */
    public  void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 回滚事务
     */
    public  void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 释放连接
     */
    public  void release(){
        try {
            connectionUtils.getThreadConnection().close();//还回连接池中
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Around("pt1()")
    public Object aroundAdvice(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try {
            //1.获取参数
            Object[] args = pjp.getArgs();
            //2.开启事务
            this.beginTransaction();
            //3.执行方法
            rtValue = pjp.proceed(args);
            //4.提交事务
            this.commit();
            //返回结果
            return  rtValue;
        }catch (Throwable e){
            //5.回滚事务
            this.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.释放资源
            this.release();
        }
    }
}

spring AOP 环绕通知的思路

环绕通知Around Advice就是在指定的程序前后均执行相关的服务,设计思路如下:

1、设计一个接口

package com.spring.service;
public interface IComponent { 
public void bussiness1();
public void bussiness2();
public void bussiness3();
}

2、编写这个接口的实现

package com.spring.service;
public class Component implements IComponent{
 @Override
 public void bussiness1() {
  // TODO Auto-generated method stub
  System.out.println("这是业务1");
 }
 @Override
 public void bussiness2() {
  // TODO Auto-generated method stub
  System.out.println("这是业务2");
 }
 @Override
 public void bussiness3() {
  // TODO Auto-generated method stub
  System.out.println("这是业务3");
 }
}

3、编写前置通知的逻辑代码

该代码必须实现org.aopalliance.intercept.Method Interceptor接口,需要的服务都写在这里。

4、编写XML配置文件

通过代理来实现AOP的环绕通知,看一下org.aopalliance.intercept.MethodInterceptor接口的源代码。该接口不是Spring内部的接口,而是AOP Alliance标准所指定的,不过Spring对这个接口有一个具体的实现过程,同时该接口相融所有遵守AOP Alliance标准的所有AOP框架。

环绕通知相当于前置通知和后置通知的结合,不同的是在MethodInterceptor的invoke()方法中,可以自由地使用MethodInvocation提供的proceed()方法来执行目标对象的方法,同时proceed()方法将会返回目标方法执行后的返回结果,在invoke方法结束前还可以修改该结果,下面还是以上面的那个例子来示范一下环绕通知的应用。

编写一个环绕通知的类,该类实现MethodInterceptor接口。这里调用了MethodInvocation的proceed()方法,也就是说,调用了目标对象Component中的business1等方法,在这个方法的前后分别增加了验证和通知执行,接着修改一下配置文件,去掉前置通知和后置通知的配置,只需要将这个环绕通知添加进去就可以了,具体代码如下:

这里只需要配置一个环绕通知的Bean,并且将这个Bean配置到interceptorNames中就完成了所有的工作,测试代码与前面的相同,可以看到结果也与前面的相同。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java经典排序算法之归并排序实现代码

    Java经典排序算法之归并排序实现代码

    这篇文章主要介绍了Java经典排序算法之归并排序实现代码,归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用,将已有序的子序列合并,得到完全有序的序列,需要的朋友可以参考下
    2023-10-10
  • Java8中方便又实用的Map函数总结

    Java8中方便又实用的Map函数总结

    java8之后,常用的Map接口中添加了一些非常实用的函数,可以大大简化一些特定场景的代码编写,提升代码可读性,快跟随小编一起来看看吧
    2022-11-11
  • Java线程池实现带返回值的方式方法

    Java线程池实现带返回值的方式方法

    在Java中,线程池是一种重要的多线程处理方式,可以有效管理和重用线程,提高程序的性能和效率,有时候我们需要在多线程处理中获取线程的返回值,本文将介绍如何使用线程池实现带返回值的方式方法,需要的朋友可以参考下
    2024-09-09
  • Spring中的依赖注入DI详解

    Spring中的依赖注入DI详解

    这篇文章主要介绍了Spring中的依赖注入DI详解,组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将依赖关系注入到组件之中,依赖注入的目的并非为软件系统带来更多功能,是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台,需要的朋友可以参考下
    2024-01-01
  • Java面向对象编程的三大特征

    Java面向对象编程的三大特征

    这篇文章主要介绍了Java面向对象编程的三大特征,封装、继承和多态是面向对象编程的三大特征,下面文章详细的介绍,需要的小伙伴可以参考一下
    2022-07-07
  • 详解Java如何优雅地书写if-else

    详解Java如何优雅地书写if-else

    在日常开发中我们常常遇到有多个if else的情况,之间书写显得代码冗余难看,对于追求更高质量代码的同学,就会思考如何优雅地处理这种代码。本文我们就来探讨下几种优化if else的方法
    2022-08-08
  • Java大数运算BigInteger与进制转换详解

    Java大数运算BigInteger与进制转换详解

    这篇文章主要介绍了Java大数运算BigInteger与进制转换详解,Java 提供了 BigInteger(大整数)类和 BigDecimal(大浮点数)类用于大数运算,这两个类都继承自 Number 类(抽象类),由于 BigInteger 在大数运算中更常见,需要的朋友可以参考下
    2023-09-09
  • java实现人工智能化屏幕监控窗口

    java实现人工智能化屏幕监控窗口

    这篇文章主要为大家详细介绍了java实现人工智能化屏幕监控窗口,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-09-09
  • java后端如何调用第三方接口(往header和body中的参数传参)

    java后端如何调用第三方接口(往header和body中的参数传参)

    这篇文章主要介绍了java后端如何调用第三方接口(往header和body中的参数传参),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • 深入了解MyBatis分页机制

    深入了解MyBatis分页机制

    在企业项目的数据库操作中,分页查询是一个常见需求,尤其当数据量庞大时,MyBatis作为我们Java开发者的持久层框架,为分页提供了灵活的支持,本篇文章我们将深入探讨MyBatis的分页机制,使我们在实际开发项目中运用自如,需要的朋友可以参考下
    2023-12-12

最新评论