SpringAOP——事务

参考官网 + 百度翻译...

一、简单介绍

官网介绍Spring框架提供的事务管理有以下优点:

  • 跨不同事务API(例如Java事务API(JTA),JDBC,Hibernate和Java Persistence API(JPA))的一致编程模型。
  • 支持声明式事务
  • 支持代码式事务API相对简单,不像JTA等API复杂
  • 与Spring Data Access的出色集成。

1、一致编程模型

两种事务模型:

全局事务:可以使用多个事务资源,通常是关系数据库和消息队列。应用服务器通过JTA管理全局事务,而JTA是一个繁琐的API(部分是由于其异常模型)。此外,UserTransaction通常需要从JNDI 派生JTA ,这意味着还需要使用JNDI才能使用JTA。全局事务的使用限制了应用程序代码的任何潜在重用,因为JTA通常仅在应用程序服务器环境中可用。

局部事务:本地事务是特定于资源的,例如与JDBC连接关联的事务。本地事务可能更易于使用,但有一个明显的缺点:它们不能跨多个事务资源工作。例如,使用JDBC连接管理事务的代码不能在全局JTA事务中运行。因为应用程序服务器不参与事务管理,所以它无法帮助确保多个资源之间的正确性。(值得注意的是,大多数应用程序使用单个事务资源。)另一个缺点是本地事务侵入了编程模型。

spring的一致编程模型:Spring解决了全局和本地交易的弊端。它使应用程序开发人员可以在任何环境中使用一致的编程模型。您只需编写一次代码,它就可以从不同环境中的不同事务管理策略中受益。Spring框架提供了声明式和程序化事务管理。官方建议使用声明式事务管理。通过程序化事务管理,开发人员可以使用Spring Framework事务抽象,该抽象可以在任何基础事务基础架构上运行。使用首选的声明性模型,开发人员通常只需编写很少或没有编写与事务管理相关的代码,因此,它们不依赖于Spring Framework事务API或任何其他事务API。

 2、声明式事务

Spring面向方面的编程(AOP)使Spring框架的声明式事务管理成为可能。但是,由于事务方面的代码随Spring Framework发行版一起提供并且可以以样板方式使用,因此通常不必理解AOP概念即可有效地使用此代码。声明式事务的特点:

  • 可在任何环境中工作。通过调整配置文件,它可以使用JDBC,JPA或Hibernate来处理JTA事务或本地事务。

  • 您可以将Spring Framework声明式事务管理应用于任何类,而不仅限于EJB之类的特殊类。

  • Spring框架提供了声明性回退规则。提供了对回滚规则的编程和声明性支持。

  • Spring Framework允许您使用AOP自定义事务行为。例如,在事务回滚的情况下,您可以插入自定义行为。您还可以添加任意建议以及事务建议。

  • Spring框架不支持跨远程调用传播事务上下文。如果需要此功能,建议您使用EJB。但是,在使用这种功能之前,请仔细考虑,因为通常情况下,您不希望事务跨越远程调用。

事务方法调用视图:

 

 

 3、代码式事务

Spring框架通过使用以下两种方式提供程序化事务管理的方法:

  • TransactionTemplate

  • PlatformTransactionManager。

Spring团队通常建议TransactionTemplate针对程序化事务管理。第二种方法类似于使用JTA UserTransactionAPI,尽管异常处理不那么麻烦。

TransactionTemplate实现

public class SimpleService implements Service {

    private final TransactionTemplate transactionTemplate;

    public SimpleService(PlatformTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    public Object someServiceMethod() {
        return transactionTemplate.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus status) {
                updateOperation1();
                return resultOfUpdateOperation2();
            }
        });
    }
}

PlatformTransactionManager实现:

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = txManager.getTransaction(def);
try {
    // execute your business logic here
}
catch (MyException ex) {
    txManager.rollback(status);
    throw ex;
}
txManager.commit(status);

声明式事务与代码式事务的选择:

代码式事务:执行少量事务操作时,或者不想依靠Spring事务使用其他事务技术。代码式事务,需要编码所以代码侵入性。

声明式事务:具有许多事务操作时。声明式事务管理脱离业务逻辑,使用SpringAOP服务,配置简单,代码侵入性不强。

二、声明式事务的抽象

1、PlatformTransactionManager:定义transaction流程接口

public interface PlatformTransactionManager extends TransactionManager {
    //开启事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
            throws TransactionException;

    //事务提交
    void commit(TransactionStatus status) throws TransactionException;
        
    //事务回滚
    void rollback(TransactionStatus status) throws TransactionException;

}    

2、TransactionDefinition:定义事务属性接口

① spring7个传播行为

② spring5个事务隔离级别(实际还是4个数据库系统事务隔离级别)

③ 事务超时

④ 只读事务

public interface TransactionDefinition {

    /**
     * Spring定义了7个传播行为
     */
    int PROPAGATION_REQUIRED = 0;//如果已存在事务,加入到当前事务中;如果不存在,创建新事务 (默认传播行为)(实际一个事务,外层内层异常,都会事务回滚)
    int PROPAGATION_SUPPORTS = 1;//如果已存在事务,加入到当前事务中;如果不存在,不创建新事务,以非事务方式执行
    int PROPAGATION_MANDATORY = 2;//如果已存在事务,加入到当前事务中;如果不存在,抛出异常
    int PROPAGATION_REQUIRES_NEW = 3;//如果已存在事务,暂停当前事务,创建新事务;如果不存在,创建新事务(实际两个事务,外层挂起,等内层执行完,后执行,回退没有联系)
    int PROPAGATION_NOT_SUPPORTED = 4;//如果已存在事务,暂停当前事务,不创建事务,按非事务方式执行;如果不存在,不创建事务,(实际一个事务,内层异常,会引发回滚)
    int PROPAGATION_NEVER = 5;//如果已存在事务,抛出异常;如果不存在,不创建新事务,以非事务方式执行。
    int PROPAGATION_NESTED = 6;//如果已存在事务,则在嵌套事务中执行,如果不存在,创建新事务(实际两个事务,外层异常,都回滚,内层异常,内层回滚,外层不需要回滚)


    /**
     * spring5个事务隔离级别
     * @see java.sql.Connection
     */
    int ISOLATION_DEFAULT = -1;//默认使用数据库系统存储引擎的隔离级别(MySQL默认隔离级别:可重复读)
    int ISOLATION_READ_UNCOMMITTED = 1;  //未提交读 same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
    int ISOLATION_READ_COMMITTED = 2;  //提交读 same as java.sql.Connection.TRANSACTION_READ_COMMITTED;
    int ISOLATION_REPEATABLE_READ = 4;  //可重复读 same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;
    int ISOLATION_SERIALIZABLE = 8;  //串行 same as java.sql.Connection.TRANSACTION_SERIALIZABLE;
  

/** * 超时 * @see java.sql.Connection */   int TIMEOUT_DEFAULT = -1;//默认使用数据库系统的事务超时时间 //返回传播行为:默认PROPAGATION_REQUIRED default int getPropagationBehavior() { return PROPAGATION_REQUIRED; }    //返回隔离级别:默认返回数据库默认的隔离级别 default int getIsolationLevel() { return ISOLATION_DEFAULT; }    //返回事务超时时间:默认为数据库系统的事务超时时间 default int getTimeout() { return TIMEOUT_DEFAULT; } //判断事务是否只读:默认为非只读事务 default boolean isReadOnly() { return false; } @Nullable default String getName() { return null; }   //创建一个final实例 static TransactionDefinition withDefaults() { return StaticTransactionDefinition.INSTANCE; } }

3、TransactionStatus:定义事务的状态接口

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {

    /**
      * extends TransactionExecution
      */

    /**
     * 是否新事务
     */
    boolean isNewTransaction();

    /**
     * 仅设置事务回滚,说明事务唯一可能结构是回滚,而不是异常引发的回滚
     */
    void setRollbackOnly();

    /**
     * 判断事务是否仅回滚
     */
    boolean isRollbackOnly();

    /**
     * 判断事务是否已完成(已提交或者已回滚)
     */
    boolean isCompleted();
    
    /**
     * extends TransactionExecution
     */

    /**
     * 创建一个保存点,
     * 可通过rollbackToSavepoint,回滚到这个保存点
     * 可通过releaseSavepoint,释放这个保存点
     */
    Object createSavepoint() throws TransactionException;

    /**
     * 回滚到事务保存点*/
    void rollbackToSavepoint(Object savepoint) throws TransactionException;

    /**
     * 释放(删除)事务保存点,大多数事务管理器会自动释放*/
    void releaseSavepoint(Object savepoint) throws TransactionException;

    /**
     * 判断事务内部是否有保存点
     */
    boolean hasSavepoint();

    /**
     * 将底层会话刷新到数据存储
     */
    @Override
    void flush();

}

三、声明式事务的简单实现

spring5+mybatis

目录结构:

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

    <!-- IOC容器自动扫描package -->
    <context:component-scan base-package="com.app.*"/>
    <!-- 开启AOP支持 -->
    <aop:aspectj-autoproxy/>
    <!-- 属性文件 -->
    <context:property-placeholder location="db.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${mysql.driver}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
        <property name="initialSize" value="${mysql.initialSize}"/>
        <property name="minIdle" value="${mysql.minIdle}"/>
        <property name="maxActive" value="${mysql.maxActive}"/>
        <property name="maxWait" value="${mysql.maxWait}"/>
     </bean>
    <!-- mybatis的SQLSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="mapper/UserMapper.xml"/>
    </bean>

    <!-- mybatis的mapper接口映射器 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.app.aop.transactional.mapper"/>
    </bean>

    <!-- 事务管理 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 启用声明式事务管理 支持注解@Transaction-->
    <tx:annotation-driven proxy-target-class="false" transaction-manager="transactionManager"/>
</beans>

UserServiceImp.java

@Service("userService")
public class UserServiceImp implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public void insert(User user) {
        userMapper.insert(user);
    }

    @Override
    public User getUserByUserId(Long userId) {
        return userMapper.selectByPrimaryKey(userId);
    }

    @Override
    @Transactional
    public void transactionalTest(User user,boolean flag){
        userMapper.insert(user);
        if(flag){
            throw new NullPointerException();
        }
        userMapper.insert(user);
    }
}

测试类:TransactionalTests.java

public class TransactionalTests {

    @Test
    public void trancationalTest(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("mybatis-config.xml");

        UserService userService = (UserService) context.getBean("userService");
        User user = userService.getUserByUserId(1L);
        user.setUserId(2011221104210100L);
        user.setUserName("rollback");
        try {
            //flag == true ,throw Exception
            userService.transactionalTest(user,true);
        } catch (Exception e) {
            e.printStackTrace();
        }
        user.setUserId(2011221104210200L);
        user.setUserName("commit");
        //flag == false,successs
        userService.transactionalTest(user,false);
        System.out.println(userService.getUserByUserId(1L));
    }

}

四、声明式事务源码跟踪

 1、声明式事务支持

    <!-- 启用声明式事务管理 支持注解@Transaction-->
    <tx:annotation-driven proxy-target-class="false" transaction-manager="transactionManager"/>

如果用的idea直接点击标签看看,或者直接打开spring-tx.xsd;

<xsd:schema xmlns="http://www.springframework.org/schema/tx"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:tool="http://www.springframework.org/schema/tool"
        targetNamespace="http://www.springframework.org/schema/tx" 
        elementFormDefault="qualified"
        attributeFormDefault="unqualified">
<xsd:element name="annotation-driven">
        <xsd:complexType>
            <xsd:annotation>
                <xsd:documentation source="java:org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"><![CDATA[
          <!-- 简化说明:开启Spring @Transactional -->
                ]]></xsd:documentation>
             <!-- 省去标签属性说明 -->

            </xsd:annotation>
    </xsd:element>

</xsd:schema>

 标签命名空间targetNamespace="http://www.springframework.org/schema/tx"在spring.handers中找到了标签命名空间TxNamespaceHandle

在TxNamespaceHandle中找到了<tx:annotiation-driven>的解析类AnnotationDrivenBeanDefinitionParser

//spring-tx/resourcesMETA-INF/spring.handlers中找到了命名空间解析
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
/* org.springframework.transaction.config.TxNamespaceHandler */
    public void init() {
        registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
    }

AnnotationDrivenBeanDefinitionParser解析

/* org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser#parse */
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        //注册一个事务事件监听器的RootBeandefinition
        //beanClass = TransactionalEventListenerFactory.class
        //beanName = org.springframework.transaction.config.internalTransactionalEventListenerFactory
        registerTransactionalEventListenerFactory(parserContext);
        String mode = element.getAttribute("mode");
        //aspectj实现AOP
        if ("aspectj".equals(mode)) {
            // mode="aspectj"
            registerTransactionAspect(element, parserContext);
            if (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader())) {
                registerJtaTransactionAspect(element, parserContext);
            }
        }
        else {
            //SpringAOP实现AOP
            // mode="proxy"
            AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
        }
        return null;
    }

/* org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer */
private static class AopAutoProxyConfigurer {

        public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
            //若未注册,先注册SpringAOP相关Beandefinition
            AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);

            String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
            if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
                Object eleSource = parserContext.extractSource(element);
//注册AnnotationTransactionAttributeSource的RootBeanDefinition
                //beanName = org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0
                RootBeanDefinition sourceDef = new RootBeanDefinition(
                        "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
                sourceDef.setSource(eleSource);
                sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

                //注册TransactionInterceptor的RootBeanDefinition到beanfactory中
                //beanName=org.springframework.transaction.interceptor.TransactionInterceptor#0
                RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
                interceptorDef.setSource(eleSource);
                interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                registerTransactionManager(element, interceptorDef);
                interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
                String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

                //注册BeanFactoryTransactionAttributeSourceAdvisor到beanfactory中
                //beanName = org.springframework.transaction.config.internalTransactionAdvisor
                RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
                advisorDef.setSource(eleSource);
                advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
                advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
                if (element.hasAttribute("order")) {
                    advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
                }
                parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);

                CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
                compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
                compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
                compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
                parserContext.registerComponent(compositeDef);
            }
        }
    }

总结:<tx:annotiation-driven>向beanfactory中注册的4个RootBeanDefinition

internalTransactionalEventListenerFactory
AnnotationTransactionAttributeSource
TransactionInterceptor//SpringAOP方法执行时的责任链拦截器
BeanFactoryTransactionAttributeSourceAdvisor//直接用bean创建的Advisor

pointCut:切点:@transactional注解

/* org.springframework.transaction.annotation.SpringTransactionAnnotationParser#isCandidateClass */
    public boolean isCandidateClass(Class<?> targetClass) {
        return AnnotationUtils.isCandidateClass(targetClass, Transactional.class);
    }

Advisor:TransactionInterceptor

/* org.springframework.transaction.interceptor.TransactionInterceptor#invoke */
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // Work out the target class: may be {@code null}.
        // The TransactionAttributeSource should be passed the target class
        // as well as the method, which may be from an interface.
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

        // Adapt to TransactionAspectSupport's invokeWithinTransaction...
        return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
    }

/* org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction */
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
            final InvocationCallback invocation) throws Throwable {
     ...if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
            //开启事务
            TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

            Object retVal;
            try {
                // This is an around advice: Invoke the next interceptor in the chain.
                // This will normally result in a target object being invoked.
                //执行方法
                retVal = invocation.proceedWithInvocation();
            }
            catch (Throwable ex) {
                // target invocation exception
                //报错回滚
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                cleanupTransactionInfo(txInfo);
            }
       ...
    }

 

 

posted on 2020-03-03 19:24  FFStayF  阅读(345)  评论(0编辑  收藏  举报