精华内容
下载资源
问答
  • 本次课程将带领大家去深入了解什么式声明式事物,它能做什么,它能有什么用处,最后再带领大家去剖析底层源码,提高大家对声明式事物的理解。 还是那句老话,对视频中有什么不懂的地方记得及时来询问老师我。
  • Spring中的声明式事物

    2019-03-16 01:19:32
    NULL 博文链接:https://762626559-qq-com.iteye.com/blog/399127
  • 自定义@Service、@Autowired、@Transactional注解类,完成基于注解的IOC容器(Bean对象创建及依赖注入维护)和声明式事务控制,写到转账工程中,并且可以实现转账成功和转账异常时事务回滚 首先对注解类进行开发: ...

    自定义@Service、@Autowired、@Transactional注解类,完成基于注解的IOC容器(Bean对象创建及依赖注入维护)和声明式事务控制,写到转账工程中,并且可以实现转账成功和转账异常时事务回滚

    1. 首先对注解类进行开发:

    写自定义注解的代码,在写之前需要先了解以下两个元注解:

    @Target

    @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。

    作用:被描述注解的使用范围:

    取值(ElementType)有:

    1.CONSTRUCTOR:用于描述构造器

    2.FIELD:用于描述域

    3.LOCAL_VARIABLE:用于描述局部变量

    4.METHOD:用于描述方法

    5.PACKAGE:用于描述包

    6.PARAMETER:用于描述参数

    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

     

    @Retention

    @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

      作用:用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

    取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)

    2.CLASS:在class文件中有效(即class保留)

    3.RUNTIME:在运行时有效(即运行时保留)

    Retention meta-annotation类型有唯一的value作为成员,它的取值来自Java.lang.annotation.RetentionPolicy的枚举类型值。

        以下是本项目对注解类进行开发:

        注解的声明周期采用的都是:RUNTIME,运行时有效

        @Autowired的注解使用范围是FIELD:用于描述域

        @Service和@Transactional的注解使用范围是TYPE:用于描述类

    1. 将xml中声明的bean转为注解的形式

    比如TransferServiceImpl:

    再来解释以下注解反射实现部分:

    总共分三步:

    1. 扫描包下所有的@service,通过反射完成对象实例化
    2. 根据@Autowired完成注解的依赖关系
    3. 针对@Transactional,修改对象为对应的代理对象

    /**
     *
    声明静态方法,一开始就加载
     
    * 方法一: 扫描注解解析
     
    */
    static {
       
    // 任务一:扫描包,通过反射技术实例化对象并且存储待用(map集合)
       
    try{
           
    //扫描获取反射对象集合
           
    Reflections reflections = new Reflections("com.lagou.edu");
           
    Set<Class<?>> servecesTypesAnnotatedWith = reflections.getTypesAnnotatedWith(Service.class);
            for
    (Class<?> c : servecesTypesAnnotatedWith) {
               
    // 通过反射技术实例化对象
               
    Object bean = c.newInstance();
               
    Service annotation = c.getAnnotation(Service.class);

               
    //对象IDservice注解有value时用value,没有时用类名
               
    if(StringUtils.isEmpty(annotation.value())){
                   
    //由于getName获取的是全限定类名,所以要分割去掉前面包名部分
                   
    String[] names = c.getName().split("\\.");
                   
    map.put(names[names.length-1], bean);
               
    }else{
                   
    map.put(annotation.value(), bean);
               
    }
            }
           
    // 实例化完成之后维护对象的依赖关系Autowired,检查哪些对象需要传值进入,
           
    for(Map.Entry<String, Object> a: map.entrySet()){
                Object o = a.getValue()
    ;
               
    Class c = o.getClass();
               
    //获取所有的变量
               
    Field[] fields = c.getDeclaredFields();
               
    //遍历属性,若持有Autowired注解则注入
               
    for (Field field : fields) {
                   
    //判断是否是使用注解的参数
                   
    if (field.isAnnotationPresent(Autowired.class)
                            &&field.getAnnotation(
    Autowired.class).required()) {
                        String[] names = field.getType().getName().split(
    "\\.");
                       
    String name = names[names.length-1];
                       
    //Autowired注解的位置需要set方法,方便c.getMethods()获取
                       
    Method[] methods = c.getMethods();
                        for
    (int j = 0; j < methods.length; j++) {
                            Method method = methods[j]
    ;
                            if
    (method.getName().equalsIgnoreCase("set" + name)) {  // 该方法就是 setAccountDao(AccountDao accountDao)
                               
    method.invoke(o,map.get(name));
                           
    }
                        }
                    }
                }
               
    //判断对象类是否持有Transactional注解,若有则修改对象为代理对象
               
    if(c.isAnnotationPresent(Transactional.class)){
                   
    //获取代理工厂
                   
    ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
                   
    Class[] face = c.getInterfaces();//获取类c实现的所有接口
                   
    //判断对象是否实现接口
                   
    if(face!=null&&face.length>0){
                       
    //实现使用JDK
                       
    o = proxyFactory.getJdkProxy(o);
                   
    }else{
                       
    //没实现使用CGLIB
                       
    o = proxyFactory.getCglibProxy(o);
                   
    }
                }

               
    // 把处理之后的object重新放到map
               
    map.put(a.getKey(),o);
           
    }
        }
    catch (IllegalAccessException e) {
            e.printStackTrace()
    ;
       
    } catch (InstantiationException e) {
            e.printStackTrace()
    ;
       
    } catch (InvocationTargetException e) {
            e.printStackTrace()
    ;
       
    }

    }

     

    最后演示下注解成果:

    1. 演示前先看下表中数据:

    1. 李向韩划款1000,操作成功

    数据库显示

    1. 李向韩划款1000,操作失败

    先调整代码:

    操作失败:

    数据库显示

    说明转账异常时事务回滚成功。

    展开全文
  • 注意:大多数Spring框架使用者采用声明式事物管理。

    注意:大多数Spring框架使用者采用声明式事物管理。这样的选择对于应用程序代码有最少的影响,因此是非侵入式轻量级容器的不二选择。


    Spring框架的声明式事物管理使得Spring的 AOP可用,虽然Spring框架中自带了事物方面代码并以样板文件方式使用,AOP概念一般不必充分利用这样的代码。


    Spring框架的声明式事物管理与EJB CMT相似,其中你可以为每个方法指定事物行为。如果必要的话,可能在事物上下文中进行一个setRollbackOnly()调用。两类事物管理的不同之处是:

    • 不像EJB CMT ,与JTA关联,Spring框架的事物管理可以再任何环境中运行。其通过简单地修改配置文件可以使用JTA事物或者使用JDBC,JPA,Hibernate或JDO的本地事物。
    • 可以在任何类中使用Spring框架的事物管理,而不像EJBs只能在指定的类中使用。
    • Spring框架提供声明回滚机制,EJB没有这个功能。并支持编程式和声明式事物回滚
    • Spring框架使你通过AOP可以自定义事物行为。例如,可以在事物回滚中插入自定义行为。你也可以与事物建议一起,添加任意建议。而EJB CMT,除非使用setRollbackOnly(),否则你不能影响容器的事物管理
    • Spring框架在远程调用之间不支持事物上下文传递,犹如高端应用服务器做的那样。如果需要这个功能,建议使用EJB。然而,在使用这个功能前倾慎重考虑,因为一般地,人们不希望事物跨越远程调用。

    回滚规则概念是很重要的:它们使得你能够指定哪个异常应该引起回滚。在配置中指定这个申明,而不是用Java代码。所以,虽然你可以仍旧在TransactionStatus对象上调用setRollbackOnly()回滚当前事物,但是一般地,你可以指定一个规则,MyApplicationException必须总是回滚。这种选择的显著好处是业务对象不依赖事物底层。例如,它不需要导入Spring事物API或其他Spring API。


    虽然EJB容器默认行为在系统异常时自动回滚事物(一般是运行期异常),但是EJB CMT在应用异常时(检查型异常)不自动回滚异常。然而Spring默认的声明式事物管理的行为延续了EJB的优点(在非检查型异常上,回滚是自动的),这对于自定义事物很有用。




    11.5.1 理解Spring框架的声明式事物实现

    仅仅用@Transactional注解你的类是不够的,将@EnableTransactionManagement添加到你的配置中,并稍后期望你能理解其如何工作的。这不得分描述了在事物关联问题中Spring框架的声明式事物底层的内部工作机理。

    最重要的概念,除了Spring框架的声明式事物支持外还有借助AOP代理的支持,并且事物建议通过元数据驱动(目前是基于XML或者注解)。结合了事物元数据的AOP借助AOP代理使用一个TransactionInterceptor,其与一个适当的PlatformTransactionManager实现关联,来驱动方法调用周围的事物。


    概念上讲,调用事物代理上的方法是像这样的:


    11.5.2 声明式事物实现的例子


    考虑下面的接口和旁边的实现。这个例子使用Foo和Bar类作为占位符,这样了你可以关注事物的使用而不是一个指定的领域模型。这个例子的目的是,DefaultFooService类在每个实现方法体重抛出UnsupportedOperationException实例,是很好的。允许你看见创建的事物并在响应UnsupportedOperationException实例后回滚。


    // the service interface that we want to make transactional
    
    package x.y.service;
    
    public interface FooService {
    
        Foo getFoo(String fooName);
    
        Foo getFoo(String fooName, String barName);
    
        void insertFoo(Foo foo);
    
        void updateFoo(Foo foo);
    
    }

    // an implementation of the above interface
    
    package x.y.service;
    
    public class DefaultFooService implements FooService {
    
        public Foo getFoo(String fooName) {
            throw new UnsupportedOperationException();
        }
    
        public Foo getFoo(String fooName, String barName) {
            throw new UnsupportedOperationException();
        }
    
        public void insertFoo(Foo foo) {
            throw new UnsupportedOperationException();
        }
    
        public void updateFoo(Foo foo) {
            throw new UnsupportedOperationException();
        }
    
    }

    假设FooService接口的前两个方法,getFoo(String)getFoo(String, String),必须在只读语义的事物上下文中执行,并且其他的方法,insertFoo(Foo)updateFoo(Foo),必须在只写语义的事物上下文中执行。下面的配置将在下一章节详细解释


    <!-- from the file context.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: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/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- this is the service object that we want to make transactional -->
        <bean id="fooService" class="x.y.service.DefaultFooService"/>
    
        <!-- the transactional advice (what happens; see the <aop:advisor/> bean below) -->
        <tx:advice id="txAdvice" transaction-manager="txManager">
            <!-- the transactional semantics... -->
            <tx:attributes>
                <!-- all methods starting with get are read-only -->
                <tx:method name="get*" read-only="true"/>
                <!-- other methods use the default transaction settings (see below) -->
                <tx:method name=""/>
            </tx:attributes>
        </tx:advice>
    
        <!-- ensure that the above transactional advice runs for any execution
            of an operation defined by the FooService interface -->
        <aop:config>
            <aop:pointcut id="fooServiceOperation" expression="execution( x.y.service.FooService.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
        </aop:config>
    
        <!-- don't forget the DataSource -->
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
            <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
            <property name="username" value="scott"/>
            <property name="password" value="tiger"/>
        </bean>
    
        <!-- similarly, don't forget the PlatformTransactionManager -->
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!-- other <bean/> definitions here -->
    
    </beans>


    检查前面的配置。你想要 一个业务对象,即fooService bean,事务性的。事物语法在<tx:advice/>中定义。<tx:advice/>定义以“所有以get为前缀的方法在只读事物的上下文中执行”,并且所有其他方法以默认的事物语法执行。<tx:advice/>transaction-manager属性设置了PlatformTransactionManager bean的名字,用于驱动事物,这里是txmanager bean。

    注意:如果你想封装的PlatformTransactionManager的名字有transactionManager命名,你可以忽略事物建议<tx:advice/>中的transaction-manager属性。如果你要封装的PlatformTransactionManager bean有其他名字,那么必须直接使用transaction-manager属性,如前面例子所示的。


    <aop:config/>定义确保由txAdvice bean定义的事物建议在程序中在合适的时候执行。首先定义一个切点匹配FooService接口(fooServiceOperation)中定义的任何操作的执行。之后使用一个advisor与txAdvice关联这个切点。结果指示在fooServiceOperation执行点上,由txAdvice定义的建议将运行。


    使用<aop:pointcut/>元素定义的表达式是一个方面切点表达式;


    一般要求是针对所有的业务层事物。最好的方式是仅改变切点表达式,来匹配你的业务层任何操作。例如:

    <aop:config>
        <aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service..(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
    </aop:config>

    注意:在这个例子中,假设你的业务接口定义在 x.y.service包中。


    现在我们分析了配置文件,那这些配置是做什么的?


    上面的配置用于创建对象(由fooService bean定义创建)事物代理。这个代理将由事物建议配置,这样当在代理上执行一个合适的方法时,开启事物,挂起,标示为只读,等等,这取决于与这个方法关联的事物配置。考虑下面的程序测试驱动上述的配置。


    public final class Boot {
    
        public static void main(final String[] args) throws Exception {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);
            FooService fooService = (FooService) ctx.getBean("fooService");
            fooService.insertFoo (new Foo());
        }
    }

    运行上面的测试代码将打印出下面的日志。(Log4j输出和由insertFoo(...)抛出的UnsupportedOperationException的栈轨迹为了清晰就省略了)


    <!-- the Spring container is starting up... -->
    [AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy for bean fooService with 0 common interceptors and 1 specific interceptors
    
    <!-- the DefaultFooService is actually proxied -->
    [JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService]
    
    <!-- ... the insertFoo(..) method is now being invoked on the proxy -->
    [TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo
    
    <!-- the transactional advice kicks in here... -->
    [DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo]
    [DataSourceTransactionManager] - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction
    
    <!-- the insertFoo(..) method from DefaultFooService throws an exception... -->
    [RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationException
    [TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo due to throwable [java.lang.UnsupportedOperationException]
    
    <!-- and the transaction is rolled back (by default, RuntimeException instances cause rollback) -->
    [DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@a53de4]
    [DataSourceTransactionManager] - Releasing JDBC Connection after transaction
    [DataSourceUtils] - Returning JDBC Connection to DataSource
    
    Exception in thread "main" java.lang.UnsupportedOperationException at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)
    <!-- AOP infrastructure stack trace elements removed for clarity -->
    at $Proxy0.insertFoo(Unknown Source)
    at Boot.main(Boot.java:11)

    11.5.3 回滚一个声明式事物

    前面部门概括了如何为类(在你的应用程序中一般是声明的业务层类)指定事物设置。这节讲述如何在一个简单的声明的例子中控制事物的回滚。


    指向Spring框架事物底层(回滚事物)的推荐方式是从某个事物上下文当前执行的代码中抛出异常。Spring框架的事物底层代码将抓取任何未处理的异常,当其从栈中冒出来时,并作出决定是否标示为回滚事物。


    你可以配置哪个异常类型标示为回滚事物,包括未检查型异常。下面的XML片段演示了如何为一个未检查的,应用程序指定的异常类型配置回滚事物。


    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
        <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
        <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    你也可以指定非回滚事物,如果当抛出异常时,不想回滚事物。下面的例子告诉Spring框架的事物底层提交伴随的事物,即使面对一个未处理的 InstrumentNotFoundException异常。

    <tx:advice id="txAdvice">
        <tx:attributes>
        <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
        <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    当Spring框架的事物底层抓取了一个异常并根据配置的回滚规则决定是否标示为回滚事物,就使用匹配的规则。所以在接下来的配置例子中,任何异常除了 InstrumentNotFoundException导致了伴随的事物回滚。


    <tx:advice id="txAdvice">
        <tx:attributes>
        <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
        </tx:attributes>
    </tx:advice>

    你也可以程序化编程的方式指定必须的回滚。虽然非常简单,这个处理很具有侵略性,并且将代码紧密耦合入Spring框架的事物底层。


    public void resolvePosition() {
        try {
            // some business logic...
        } catch (NoProductInStockException ex) {
            // trigger rollback programmatically
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

    强烈建议尽可能的使用声明式事物方式回滚。如果确实不得已,也可以使用程序化回滚,但是了这样使用面对了一个纯净的基于POJO结构的窘境。


    11.5.4 为不同的bean配置不同的事物语法

    考虑这样的情景,你有大量的业务层对象,并且你想对每个对象应用一个完全不同的事物配置。通过定义明显的<aop:advisor/>元素并使用不同的pointcutadvice-ref属性值可以达到这个要求。


    为方便比较,首先假设你的业务层的所有类定义在一个根x.y.service包中。为使得那个包和子包中定义的类的实例和以Service命名结尾的类的实例有默认的事物配置,你应该按照如下方式写:


    <?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: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/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <aop:config>
    
            <aop:pointcut id="serviceOperation"
                    expression="execution(* x.y.service..Service.(..))"/>
    
            <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>
    
        </aop:config>
    
        <!-- these two beans will be transactional... -->
        <bean id="fooService" class="x.y.service.DefaultFooService"/>
        <bean id="barService" class="x.y.service.extras.SimpleBarService"/>
    
        <!-- ... and these two beans won't -->
        <bean id="anotherService" class="org.xyz.SomeService"/> <!-- (not in the right package) -->
        <bean id="barManager" class="x.y.service.SimpleBarManager"/> <!-- (doesn't end in Service) -->
    
        <tx:advice id="txAdvice">
            <tx:attributes>
                <tx:method name="get*" read-only="true"/>
                <tx:method name="*"/>
            </tx:attributes>
        </tx:advice>
    
        <!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->
    
    </beans>

    下面例子显示了如何使用完全不同的事物设置配置两个不同的beans


    <?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: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/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <aop:config>
    
            <aop:pointcut id="defaultServiceOperation"
                    expression="execution(* x.y.service.Service.(..))"/>
    
            <aop:pointcut id="noTxServiceOperation"
                    expression="execution(* x.y.service.ddl.DefaultDdlManager.(..))"/>
    
            <aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
    
            <aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>
    
        </aop:config>
    
        <!-- this bean will be transactional (see the defaultServiceOperation pointcut) -->
        <bean id="fooService" class="x.y.service.DefaultFooService"/>
    
        <!-- this bean will also be transactional, but with totally different transactional settings -->
        <bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>
    
        <tx:advice id="defaultTxAdvice">
            <tx:attributes>
                <tx:method name="get" read-only="true"/>
                <tx:method name=""/>
            </tx:attributes>
        </tx:advice>
    
        <tx:advice id="noTxAdvice">
            <tx:attributes>
                <tx:method name="" propagation="NEVER"/>
            </tx:attributes>
        </tx:advice>
    
        <!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->
    
    </beans>






    展开全文
  • 1.引 通过前面的分析,已经成功的创建了事物,但是不要...处理声明式事物 if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with g...
    1.引

    前面的分析中已经成功的创建了事物,但是方法仍在方法拦截器链中。回顾一下代码片段:

    // 2.处理声明式事物
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 2.2 创建事物
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        System.out.println("==创建了名为:["+joinpointIdentification+"]的事物");
        Object retVal = null;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            // 2.3 继续调用方法拦截器链,这里一般将会调用目标类的方法,如:com.lyc.cn.v2.day09.AccountServiceImpl.save方法
            retVal = invocation.proceedWithInvocation();
        } catch (Throwable ex) {
            // target invocation exception
            // 2.4 如果目标类方法抛出异常,则在此处理,例如:事物回滚
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        } finally {
            // 2.5 清除上一步创建的事物信息
            cleanupTransactionInfo(txInfo);
        }
        // 2.6 调用成功完成后执行,但不是在异常被处理后执行。如果我们不创建事务,就什么也不做。
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    

    关于拦截器链的调用、目标方法的调用等前面都有介绍。

    接下来的处理会分为两种情况(假如当前一定存在事物):有异常,事物即有可能会被回滚、也有可能会被提交;无异常,事物会被提交,接下来对两种情况逐个分析。因为catch方法的代码比较靠前,先分析有异常的情况。细心的同学可能会发现,无论是否抛出异常都会调用completeTransactionAfterThrowing方法去完成事物,只是该方法被重载了,传递的参数不同而已,当然其内部处理细节肯定是不同的。

    2.回滚判断
    /**
     * 处理异常以完成事物,基于配置该事物可能回滚也可能提交
     * Handle a throwable, completing the transaction.
     * We may commit or roll back, depending on the configuration.
     * @param txInfo information about the current transaction
     * @param ex     throwable encountered
     */
    protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.getTransactionStatus() != null) {
            if (logger.isTraceEnabled()) {
                logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);
            }
            // 1.回滚
            /**
             * txInfo.transactionAttribute.rollbackOn(ex)判断回滚的条件:
             *
             * 1. 如果自定了RollbackRuleAttribute列表,如果当前异常匹配到了RollbackRuleAttribute其中的条目,则回滚
             *    例如:可以通过rollbackFor指定触发回滚的异常@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
             *
             * 2. 否则如果异常是RuntimeException或者Error的类型,则回滚
             *
             * 3. 其他的异常是不会回滚的,这里要注意一下...
             */
            if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                    // 执行回滚
                    System.out.println("==准备回滚"+txInfo.getJoinpointIdentification());
                    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                } catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                } catch (RuntimeException | Error ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    throw ex2;
                }
            }
            // 2.如果未能满足回滚条件,则有可能会提交事物,也有可能会回滚事物
            // 注意:如果TransactionStatus.isRollbackOnly()为true,则仍然会执行回滚
            else {
                // We don't roll back on this exception.
                // Will still roll back if TransactionStatus.isRollbackOnly() is true.
                try {
                    System.out.println("==准备提交"+txInfo.getJoinpointIdentification());
                    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
                } catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by commit exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                } catch (RuntimeException | Error ex2) {
                    logger.error("Application exception overridden by commit exception", ex);
                    throw ex2;
                }
            }
        }
    }
    

    txInfo.transactionAttribute.rollbackOn(ex)这段代码是对当前异常是否满足回滚条件的判断。Spring事物中默认只有RuntimeException和Error两种类型的被捕获之后,事物才会回滚。因为在DefaultTransactionAttribute类中有:

    public boolean rollbackOn(Throwable ex) {
    	return (ex instanceof RuntimeException || ex instanceof Error);
    }
    

    但是在实际应用当中,可能会有很多自定义异常,如果想要自定义异常发生时也回滚事物,怎么办呢?这时候就要用到rollbackFor属性,例如:@Transactional(propagation = Propagation.REQUIRED,rollbackFor = MyException.class),Spring会将我们自定义的异常注册到RuleBasedTransactionAttribute类的RollbackRuleAttribute属性集合中。总结如下

    1. 匹配到自定义异常,回滚。
    2. 如果异常是RuntimeException或者Error的类型,回滚。
    3. 其他异常,不回滚。

    在completeTransactionAfterThrowing方法的else分支中通过commit方法对当前事物进行了提交操作,但是,commit方法并不一定会真正的提交事物,也有可能会回滚事物,这一点留在事物提交分析中讲解,接下来看事物是如何回滚的。

    3.回滚
    // 执行回滚前检查事物状态
    public final void rollback(TransactionStatus status) throws TransactionException {
        if (status.isCompleted()) {
            throw new IllegalTransactionStateException(
                    "Transaction is already completed - do not call commit or rollback more than once per transaction");
        }
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        processRollback(defStatus, false);
    }
    
    // 执行回滚
    private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
        try {
            boolean unexpectedRollback = unexpected;
            try {
                // 1.事物回滚前调用事物同步接口
                triggerBeforeCompletion(status);
                // 2.如果有保存点,则回滚到保存点
                if (status.hasSavepoint()) {
                    if (status.isDebug()) {
                        logger.debug("Rolling back transaction to savepoint");
                    }
                    status.rollbackToHeldSavepoint();
                }
                // 3.如果当前事物是一个新的事物,则调用doRollback执行给定事物的回滚
                else if (status.isNewTransaction()) {
                    if (status.isDebug()) {
                        logger.debug("Initiating transaction rollback");
                    }
                    doRollback(status);
                }
                else {
                    // Participating in larger transaction
                    // 4.如果当前事物并非独立事物,则将当前事物的rollbackOnly属性标记为true,等到事物链完成之后,一起执行回滚
    
                    // 如果当前存在事物,但是事物的rollbackOnly属性已经被标记为true
                    // 或者globalRollbackOnParticipationFailure(返回是否仅在参与事务失败后才将现有事务全局标记为回滚)为true
                    if (status.hasTransaction()) {
                        if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                            if (status.isDebug()) {
                                logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                            }
                            System.out.println("==当前事物并非独立事物,且RollbackOnly为true\n");
                            // 则将ConnectionHolder中的rollbackOnly标记为true
                            doSetRollbackOnly(status);
                        }
                        else {
                            if (status.isDebug()) {
                                logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                            }
                        }
                    }
                    // 5.如果当前不存在事物,则不执行任何操作
                    else {
                        logger.debug("Should roll back transaction but cannot - no transaction available");
                    }
                    // Unexpected rollback only matters here if we're asked to fail early
                    if (!isFailEarlyOnGlobalRollbackOnly()) {
                        unexpectedRollback = false;
                    }
                }
            }
            catch (RuntimeException | Error ex) {
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
                throw ex;
            }
            // 6.事物回滚后调用事物同步接口
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
            // Raise UnexpectedRollbackException if we had a global rollback-only marker
            if (unexpectedRollback) {
                throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
            }
        }
        finally {
            // 7.事物完成后清理资源
            cleanupAfterCompletion(status);
        }
    }
    

    说明:第2、4点涉及到了嵌套事物回滚,留在后面章节分析。而且针对其中的嵌套事物处理,如回滚到保存点等操作,我们留在后面的章节讲解,因为涉及到的东西太多了,在一个小节里实在很难讲解。

    1. 事物完成前、完成后触发器的调用,例如:我们在上一节的业务方法里注册了TransactionSynchronizationAdapter接口,那么在这里会分别执行我们实现了接口的方法,我们可以通过该接口来做一些额外的功能扩展。
    2. 如果有保存点,则调用rollbackToHeldSavepoint回滚到保存点(后面嵌套事物章节分析)
    3. 如果当前事物是一个新的事物,则调用doRollback执行给定事物的回滚
    4. 如果当前事物并非独立事物,则将当前事物的rollbackOnly属性标记为true,等到事物链完成之后,一起执行回滚
      大概的流程就是这样了,因为我们分析的是单service下的单个业务方法调用,所以这里我们还是只分析最简单的doRollback正常回滚调用(后面嵌套事物章节分析)

    不同的事物管理器对doRollback回滚有不同的实现,这里我们只看DataSourceTransactionManager的回滚方法:

    @Override
    protected void doRollback(DefaultTransactionStatus status) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        if (status.isDebug()) {
            logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
        }
        try {
            con.rollback();
        }
        catch (SQLException ex) {
            throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
        }
    }
    

    从ConnectionHolder中拿到连接并执行回滚。这里会涉及到一些数据库底层的东西了,感兴趣的同学可以自己深入研究。

    到这里事物的回滚操作就完成了,但是不要忘了,这里一定要释放掉事物相关的资源。以免造成数据库连接泄露、内存泄露等错误。

    private void cleanupAfterCompletion(DefaultTransactionStatus status) {
        // 1.将当前事物状态标记为已完成
        status.setCompleted();
        // 2.清除synchronization
        if (status.isNewSynchronization()) {
            TransactionSynchronizationManager.clear();
        }
        // 3.事务完成后清理资源。
        if (status.isNewTransaction()) {
            doCleanupAfterCompletion(status.getTransaction());
        }
        // 4.从嵌套事物中恢复被挂起的资源
        if (status.getSuspendedResources() != null) {
            if (status.isDebug()) {
                logger.debug("Resuming suspended transaction after completion of inner transaction");
            }
            Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
            resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
        }
    }
    

    第1、2很简单,第4又涉及到了事物嵌套,我们只有分析一下第3步了:

    protected void doCleanupAfterCompletion(Object transaction) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    
        // Remove the connection holder from the thread, if exposed.
        // 解绑ConnectionHolder
        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.unbindResource(obtainDataSource());
        }
    
        // Reset connection.
        // 重置连接
        Connection con = txObject.getConnectionHolder().getConnection();
        try {
            if (txObject.isMustRestoreAutoCommit()) {
                con.setAutoCommit(true);
            }
            DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
        }
        catch (Throwable ex) {
            logger.debug("Could not reset JDBC Connection after transaction", ex);
        }
    
        // 释放连接
        if (txObject.isNewConnectionHolder()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
            }
            DataSourceUtils.releaseConnection(con, this.dataSource);
        }
    
        txObject.getConnectionHolder().clear();
    }
    

    等到事物资源清理完成之后,事物就彻底的结束了。

    4.提交
    public final void commit(TransactionStatus status) throws TransactionException {
        // 如果当前事物已经被标记为完成,抛出异常
        if (status.isCompleted()) {
            throw new IllegalTransactionStateException(
                    "Transaction is already completed - do not call commit or rollback more than once per transaction");
        }
    
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    
        // 如果rollbackOnly为true,则回滚
        if (defStatus.isLocalRollbackOnly()) {
            if (defStatus.isDebug()) {
                logger.debug("Transactional code has requested rollback");
            }
            processRollback(defStatus, false);
            return;
        }
    
        // shouldCommitOnGlobalRollbackOnly --> 返回是否对标记为仅以全局方式回滚的事务调用
        // defStatus.isGlobalRollbackOnly() --> 实现了SmartTransactionObject并且事物的rollbackOnly被标记为true
        if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
            if (defStatus.isDebug()) {
                logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
            }
            processRollback(defStatus, true);
            return;
        }
    
        // 提交事物
        processCommit(defStatus);
    }
    

    关于回滚和提交的判断条件注释都写的很清晰了,对于rollbackOnly这里不做太多的介绍,留在后面的章节分析。大家只要记住,事物到了这里,可能会回滚也可能会提交即可。

    展开全文
  • 大多数网站上都让用注解来实现事物控制,其实比较麻烦,还容易漏掉,于是开始研究如何实现声明式事物,其实要搞也简单一个类搞定。 @Configuration @AutoConfigureAfter(E6DynamicDataSourceConfiguration.class) ...

    大多数网站上都让用注解来实现事物控制,其实比较麻烦,还容易漏掉,于是开始研究如何实现声明式事物,其实要搞也简单一个类搞定。

    @Configuration
    @AutoConfigureAfter(E6DynamicDataSourceConfiguration.class)
    public class TransacationConfig {
    
        private ApplicationContext applicationContext;
    
        public TransacationConfig(ApplicationContext applicationContext)
        {
            this.applicationContext = applicationContext;
        }
    
    
    
        /**
         * 事物拦截器 千万不要声明一个datasourcetransacationmanager 因为jpa带一个了
    
         */
        public TransactionInterceptor getAdvisor() {
            Properties properties = new Properties();
            properties.setProperty("get*", "PROPAGATION_REQUIRED,-Exception,readOnly");
            properties.setProperty("find*", "PROPAGATION_REQUIRED,-Exception,readOnly");
            properties.setProperty("select*", "PROPAGATION_REQUIRED,-Exception,readOnly");
            properties.setProperty("insert*", "PROPAGATION_REQUIRED,-Exception");
            properties.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");
            properties.setProperty("save*", "PROPAGATION_REQUIRED,-Exception");
            properties.setProperty("create*", "PROPAGATION_REQUIRED,-Exception");
            properties.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");
            properties.setProperty("del*", "PROPAGATION_REQUIRED,-Exception");
            TransactionInterceptor result = new TransactionInterceptor(null, properties);
            result.setBeanFactory(this.applicationContext);
            return result;
        }
    
        private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.e6yun.project..service.impl.*.*(..)) && !execution (* com.e6yun.project.common.module.base.user.service.impl.*.*(..)) ";
    
    
        @Bean
        public Advisor txAdviceAdvisor() {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
            return new DefaultPointcutAdvisor(pointcut, getAdvisor());
        }
    }

     

    展开全文
  • spring.net 声明式事物管理小示例,有完整的配置,以及测试
  • Spring(IOC,APD,声明式事物的例子)
  • hiberante使用Spring声明式事物  配置数据源  配置sessionFactory
  • Spring(六)——声明式事物控制

    千次阅读 2021-10-23 10:13:08
    基于XML的声明式事务控制 声明式事务控制的实现 使用注解配置声明式事务控制 编程式事务控制相关对象 编程式事务控制三大对象 1、PlatformTransactionManager PlatformTransactionManager接口是spring 的事务...
  • 声明式事物

    2013-06-06 21:15:50
    声明式事物的方式  1.在Spring的配置文件的头部添加 xmlns:tx="http://www.springframework.org/schema/tx"  http://www.springframework.org/schema/tx  ...2.在Spr
  • Spring声明式事物配置

    2017-11-01 10:58:38
    事物配置: <aop:pointcut id="transactionPointcut" expression="execution(* s.s.m.service..*Impl.*(..))" />...
  • 1.配置事物 applicationContext-tran.xml &lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;beans xmlns="http://www.springframework.org/schema/beans" xmlns:p=...
  • Spring的声明式事务底层原理

    千次阅读 2018-11-17 19:54:16
    文章目录声明式事务的概述声明式事务的初探声明式事务的源码分析 声明式事务的概述 Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一...
  • •Spring 同时支持编程式事务管理和声明式的事务管理. •编程式事务管理: 将事务管理代码嵌入到业务方法中来控制事务的提交和回滚.在编程式管理事务时,必须在每个事务操作中包含额外的事务管理代码. 声明式事务...
  • spring声明式事物的两种配置方式

    千次阅读 2017-01-27 23:36:54
    目的:在使用事务的过程中法,发现在在通配符配置的形势下,想开启一个新的...spring声明式事物的配置方式有两种:1,通配符配置,2注解式配置 区别,通配符方式方便,但是不灵活,注解式相反但是需要在每个需要注解
  • 使用Spring为Hibernate配置声明式事物

    千次阅读 热门讨论 2016-01-04 21:28:41
    从AOP的角度看,事物跟日志一样,都是跟业务逻辑无关的东西,这两个东西通过切入方式,放到系统中,是非常合适的。下面,将Hibernate中的事物配置到Spring中,使我们在编程的时候,无需考虑事物的存在,专心的放到...
  • 上一节我们说了spring编程式事物管理,但是总感觉还是要在每个service层的方法内嵌或者是外套事物管理代码,造成业务逻辑与事物耦合在一起了,还好有spring aop为我们管理了事物。下面我们来看下怎么实现: 业务层...
  • springmvc使用声明式事物简单配置步骤
  • Spring 声明式事物配置文件解析

    千次阅读 2014-10-30 11:02:31
    在Java源代码中声明式语义使得声明与受影响的代码更近。不当的结合没有太多的危险,因为用于事物的代码将总是以任何方式部署。 下面的例子是使用@Transaction注解的例子。考虑下面的类定义: /...
  • 我配置spring声明式事物为*Server 我的登陆方法为login没有在事物的范围呢,但里面的getUserInfo在事物范围内, 那么只有get*的话就没有被绑定到线程中,只有配置login才行。为什么? *"> PROPAGATION_...
  • 各位前辈帮我看一下我的声明式事物管理那里出错了,为什么不起作用 <property name="mappingResources"> <value>com/transaction/pojo/Role.hbm.xml</value> </list> ...
  • SSM之Service层基于注解的声明式事物

    千次阅读 2016-05-31 11:20:17
    接口的具体实现,这里采用注解来实现依赖注入,以及基于注解的声明式事物 @Service public class UserServiceImpl implements UserService { @Autowired //注入DAO private UserDao ...
  • Spring声明式事物

    2016-01-06 17:44:45
     Spring声明式事物   声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。  aop方式: ...
  • 声明式事务和编程式事务的区别

    万次阅读 2020-02-02 14:26:12
    声明式事务:通过AOP(面向切面)方式在方法前使用编程式事务的方法开启事务,在方法后提交或回滚。用配置文件的方法或注解方法(如:@Transactional)控制事务。 编程式事务:手动开启、提交、回滚事务。 通俗地...
  • SpringBoot 声明式事物配置

    千次阅读 2017-03-16 23:26:21
    // 事物管理器 transactionInterceptor .setTransactionManager(platformTransactionManager); Properties transactionAttributes = new Properties(); // 新增 transactionAttributes.setProperty( "insert*...
  • 通过事务使某个方法里面的操作要么全成功要么全失败 第一步,引入所需的jar包 增加事务的命名空间 ...当作用于类上时,该类的所有public方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来...
  • Spring AOP声明式事务

    千次阅读 2018-09-19 23:43:08
    Spring AOP声明式事务Spring AOP声明式事务传统spring配置SpringBoot配置总结 Spring AOP声明式事务 Spring AOP声明式事务可以帮我们自动管理事务,在业务操作前后加入事务开始和提交,根据约定的命名规则,进行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,963
精华内容 15,985
关键字:

声明式事物