上一节我们使用xml的AOP完成了事务管理案例的改造,重点在于xml文件的配置,其他代码到没有什么改变,这篇文章主要讲解基于注解AOP的事务管理案例改造。需要改的代码比较多。

1.改造xml

首先要使用xml文件配置Spring创建容器时要扫描的包:

<!--配置Spring创建容器时要扫描的包-->
<context:component-scan base-package="com.eastnotes"></context:component-scan>

将能够用注解完成的bean对象的配置删除,最终结果如下:

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置Spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.eastnotes"></context:component-scan>

    <!--配置QueryRunner对象-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
    </bean>

    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--使用set注入连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>

可以看出,queryRunner和dataSource依然使用xml配置,等下一节将纯注解配置的时候,会将这些也解决掉。

2.改造bean对象

这一步,我们要使用注解的形式,将bean对象放入容器。

  • 使用@Repository注解将DAO对象装入容器,使用@Autowired注解runner和connectionUtils进行依赖注入
  • 使用@Service注解将AccountService对象装入容器,使用@Autowired注解对accountDao进行依赖注入
  • 使用@Component注解将ConnectionUtils对象装入容器,使用@Autowired对dataSource进行依赖注入

对于transactionManager类的改造,代码如下:

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

/**
 * 和事物管理相关的工具类
 * 包含了开启事务、提交事务、回滚事务和释放连接
 */
@Component("transactionManager")
@Aspect
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    @Pointcut("execution(* com.eastnotes.service.impl.*.*(..))")
    public void pt1(){

    }

    /**
     * 开启事务
     */
    @Before("pt1()")
    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    @AfterReturning("pt1()")
    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 回滚事务
     */
    @AfterThrowing("pt1()")
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
            connectionUtils.removeConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 释放连接
     */
    @After("pt1()")
    public void release(){
        try {
            connectionUtils.getThreadConnection().close();  //将连接还回连接池中
            connectionUtils.removeConnection();             //解绑
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

但是这样改造完成后,由于我们使用四种通知的方式进行的配置,在执行时,最终通知优于异常通知运行,因此事务管理时会失败,所以我们还得使用环绕通知来改造,环绕通知的代码如下:

/**
 * 和事物管理相关的工具类
 * 包含了开启事务、提交事务、回滚事务和释放连接
 */
@Component("transactionManager")
@Aspect
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    @Pointcut("execution(* com.eastnotes.service.impl.*.*(..))")
    public void pt1(){

    }

    /**
     * 开启事务
     */
    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();
            connectionUtils.removeConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 释放连接
     */
    public void release(){
        try {
            connectionUtils.getThreadConnection().close();  //将连接还回连接池中
            connectionUtils.removeConnection();             //解绑
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Around("pt1()")
    public Object aroundAdvice(ProceedingJoinPoint pjp){
        Object rtValue  = null;

        try{
            Object [] args = pjp.getArgs(); //得到方法执行所需要的参数
            this.beginTransaction();
            rtValue = pjp.proceed(args);    //明确调用业务层方法(切入点方法)
            this.commit();
            return rtValue;
        }catch (Throwable t){
            this.rollback();
            throw new RuntimeException(t);
        }finally {
           this.release();
        }
    }
}