通过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中就完成了所有的工作,测试代码与前面的相同,可以看到结果也与前面的相同。

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

相关文章

  • Spring Boot如何集成模板引擎FreeMarker

    Spring Boot如何集成模板引擎FreeMarker

    这篇文章主要介绍了Spring Boot如何集成模板引擎FreeMarker,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • 详解Java中的Reflection反射和暴力反射

    详解Java中的Reflection反射和暴力反射

    本文主要介绍了详解Java中的Reflection反射和暴力反射,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • 详解Spring Cloud Hystrix断路器实现容错和降级

    详解Spring Cloud Hystrix断路器实现容错和降级

    本篇文章主要介绍了详解Spring Cloud Hystrix断路器实现容错和降级,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • 详解ArrayBlockQueue源码解析

    详解ArrayBlockQueue源码解析

    这篇文章主要介绍了ArrayBlockQueue源码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Java java.sql.Timestamp时间戳案例详解

    Java java.sql.Timestamp时间戳案例详解

    这篇文章主要介绍了Java java.sql.Timestamp时间戳案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • 详解java8在Collection中新增加的方法removeIf

    详解java8在Collection中新增加的方法removeIf

    这篇文章主要介绍了详解java8在Collection中新增加的方法removeIf的相关资料,需要的朋友可以参考下
    2018-01-01
  • Java实现克鲁斯卡尔算法的示例代码

    Java实现克鲁斯卡尔算法的示例代码

    克鲁斯卡尔算法是一种用于求解最小生成树问题的贪心算法。这篇文章主要为大家详细介绍了Java实现克鲁斯卡尔算法的方法,需要的可以参考一下
    2023-04-04
  • 关于java.util.Random的实现原理详解

    关于java.util.Random的实现原理详解

    Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法,下面这篇文章主要给大家介绍了关于java.util.Random实现原理的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下。
    2017-08-08
  • java中File类应用遍历文件夹下所有文件

    java中File类应用遍历文件夹下所有文件

    这篇文章主要为大家详细介绍了java中File类应用遍历文件夹下所有文件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • 详解java解决分布式环境中高并发环境下数据插入重复问题

    详解java解决分布式环境中高并发环境下数据插入重复问题

    这篇文章主要介绍了java解决并发数据重复问题 ,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03

最新评论