使用注解版AOP解决事务问题

本文介绍了AOP注解版和XML版的区别,阐述了aop:before、aop:after-returning、aop:after-throwing、aop:after四种常用通知类型的作用、属性和执行时间点,分析了它们在XML和注解版中的执行顺序,还提到了相关文件改动,最后指出使用around环绕通知可解决一些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、注解版和xml版的区别

1、 通知的四种常用类型

(1)aop:before

作用: 用于配置前置通知。指定增强的方法在切入点方法之前执行 属性: method:用于指定通知类中的增强方法名称 ponitcut-ref:用于指定切入点的表达式的引用 poinitcut:用于指定切入点表达式 执行时间点: 切入点方法执行之前执行
<aop:before method=“beginPrintLog” pointcut-ref=“pt1”/>

(2)aop:after-returning

作用: 用于配置后置通知 属性: method:指定通知中方法的名称。 pointct:定义切入点表达式 pointcut-ref:指定切入点表达式的引用 执行时间点: 切入点方法正常执行之后。它和异常通知只能有一个执行
<aop:after-returning method=“afterReturningPrintLog” pointcut-ref=“pt1”/>

(3)aop:after-throwing

作用: 用于配置异常通知 属性: method:指定通知中方法的名称。 pointct:定义切入点表达式 pointcut-ref:指定切入点表达式的引用 执行时间点: 切入点方法执行产生异常后执行。它和后置通知只能执行一个
<aop:after-throwing method=“afterThrowingPringLog” pointcut-ref=“pt1”/>

(4) aop:after

作用: 用于配置最终通知 属性: method:指定通知中方法的名称。 pointct:定义切入点表达式 pointcut-ref:指定切入点表达式的引用 执行时间点: 无论切入点方法执行时是否有异常,它都会在其后面执行。
<aop:after method=“afterPringLog” pointcut-ref=“pt1”/>

2、四种常用类型通知的执行顺序

(1)xml的织入顺序是按照xml里的写的顺序进行执行

xml的织入顺序

(2)注解版织入的顺序则和运行时出现的情况进行分两种分析
Ⅰ.正常运行

注解通知正常执行时:
正常执行时会先执行 @before 然后再执行 @After 最后执行@ After-returning
正常执行时执行的顺序
所以正常情况下要在 TransactionManager里的标注事务的状态时对应的通知
如果一开始让@After对应为release()则事务将提前释放资源,造成无法提交,所以要把@After对应为Commit(),这样就可以先提交再释放资源
标注事务的状态时对应的通知

Ⅱ.抛出异常时 After 的执行顺序

如果一开始让@After对应为release()则事务将提前释放资源,造成无法提交,所以要把@After对应为rollBack(),这样就可以先抛异常再释放资源
抛出异常时执行顺序

1.需要改动是文件

1.applicationContext.xml配置文件

注解版

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--开启注解扫描-->
    <context:component-scan base-package="com.william"></context:component-scan>
    <!--创建QueryRunner-->
    <bean id="QueryRunner" class="org.apache.commons.dbutils.QueryRunner">

    </bean>
    <!--创建dataSource-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--引入属性文件-->
  <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
   <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>

2.在TransactionManager进行注解

代码:使用的是四种的执行顺序,没使用around环绕通知
执行顺序按照

package com.william.utils;

import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.SQLException;

/**
 * @author :lijunxuan
 * @date :Created in 2019/5/27  16:50
 * @description :
 * @version: 1.0
 */
@Component
@Aspect
public class TransactionManager {
    @Pointcut("execution(* com.william.service.Impl.*.*(..))")
    public void pc(){}
    @Autowired
    ConnectionUtils connectionUtils;
    @Before("pc()")
    public void beganTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);

            System.out.println(" beganTransaction   "+connectionUtils.getThreadConnection());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    @After("pc()")
    public void Commit(){
        try {
            System.out.println(" Commit   "+connectionUtils.getThreadConnection());
            connectionUtils.getThreadConnection().commit();

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    @AfterThrowing("pc()")
    public void rollBack(){
        try {
            System.out.println(" rollBack   "+connectionUtils.getThreadConnection());
            connectionUtils.getThreadConnection().rollback();

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    @AfterReturning("pc()")
    public void release(){
        try {
            System.out.println(" release   "+connectionUtils.getThreadConnection());
            connectionUtils.getThreadConnection().setAutoCommit(true);
            connectionUtils.getThreadConnection().close();
            connectionUtils.remove();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

二、使用around环绕通知可以解决上面的问题

配置方式:

<aop:config>  <aop:pointcut expression="execution(* com.william.service.impl.*.*(..))" id="pt1"/>   <aop:aspect id="txAdvice" ref="txManager"> 
  <!-- 配置环绕通知 -->   <aop:around method="transactionAround" pointcut-ref="pt1"/> 
 </aop:aspect> </aop:config> 

aop:around: 作用: 用于配置环绕通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
说明: 它是 spring 框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。
注意: 通常情况下,环绕通知都是独立使用的

around环绕通知

package com.william.utils;

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.stereotype.Component;

import java.sql.SQLException;

/**
 * @author :lijunxuan
 * @date :Created in 2019/5/27  16:50
 * @description :
 * @version: 1.0
 */
@Component
@Aspect
public class TransactionManager {

    @Pointcut("execution(* com.william.service.impl.*.*(..))")
    public void pc(){}

    @Around("pc()")
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            beginTransaction();
            //执行原始的方法
            Object result = joinPoint.proceed();
            commit();
            return  result;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            rollback();
        } finally {
            release();
        }
        return  null;
    }

    @Autowired
    ConnectionUtils connectionUtils;

    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public void release(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(true);
            connectionUtils.getThreadConnection().close();
            connectionUtils.remove();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值