spring 事务代理的方法

由于 Spring 事务管理是基于接口代理或动态字节码技术,通过 AOP 实施事务增强的。虽然,Spring 还支持 AspectJ LTW 在类加载期实施增强,但这种方法很少使用,所以我们不予关注。

对于基于接口动态代理的 AOP 事务增强来说,由于接口的方法是 public 的,这就要求实现类的实现方法必须是 public 的(不能是 protected,private 等),同时不能使用 static 的修饰符。所以,可以实施接口动态代理的方法只能是使用“public”或“public final”修饰符的方法,其它方法不可能被动态代理,相应的也就不能实施 AOP 增强,也不能进行 Spring 事务增强了。

基于 CGLib 字节码动态代理的方案是通过扩展被增强类,动态创建子类的方式进行 AOP 增强植入的。由于使用 final、static、private 修饰符的方法都不能被子类覆盖,相应的,这些方法将不能被实施 AOP 增强。所以,必须特别注意这些修饰符的使用,以免不小心成为事务管理的漏网之鱼。

下面通过具体的实例说明基于 CGLib 字节码动态代理无法享受 Spring AOP 事务增强的特殊方法。

清单 6.UserService.java:4 个不同修饰符的方法

package user.special;

import org.springframework.stereotype.Service;

@Service("userService")

public class UserService {

//① private方法因访问权限的限制,无法被子类覆盖

private void method1() {

System.out.println("method1");

}

//② final方法无法被子类覆盖

public final void method2() {

System.out.println("method2");

}

//③ static是类级别的方法,无法被子类覆盖

public static void method3() {

System.out.println("method3");

}

//④ public方法可以被子类覆盖,因此可以被动态字节码增强

public void method4() {

System.out.println("method4");

}

}

Spring 通过 CGLib 动态代理技术对 UserService Bean 实施 AOP 事务增强的配置如下所示:

清单 7.applicationContext.xml:对 UserService 用 CGLib 实施事务增强

<?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:p="http://www.springframework.org/schema/p"

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-3.0.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.0.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

<!-- 省略声明数据源及DataSourceTransactionManager事务管理器-->

<aop:config proxy-target-class="true">

<!-- ①显式使用CGLib动态代理 -->

<!-- ②希望对UserService所有方法实施事务增强 -->

<aop:pointcut id="serviceJdbcMethod"

expression="execution(* user.special.UserService.*(..))"/>

<aop:advisor pointcut-ref="serviceJdbcMethod"

advice-ref="jdbcAdvice" order="0"/>

</aop:config>

<tx:advice id="jdbcAdvice" transaction-manager="jdbcManager">

<tx:attributes>

<tx:method name="*"/>

</tx:attributes>

</tx:advice>

</beans>

在 ① 处,我们通过 proxy-target-class="true"显式使用 CGLib 动态代理技术,在 ② 处通过 AspjectJ 切点表达式表达 UserService 所有的方法,希望对 UserService 所有方法都实施 Spring AOP 事务增强。

在 UserService 添加一个可执行的方法,如下所示:

清单 8.UserService.java 添加 main 方法

package user.special;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import org.springframework.stereotype.Service;

@Service("userService")

public class UserService {

public static void main(String[] args) {

ApplicationContext ctx =

new ClassPathXmlApplicationContext("user/special/applicationContext.xml");

UserService service = (UserService) ctx.getBean("userService");

System.out.println("before method1");

service.method1();

System.out.println("after method1");

System.out.println("before method2");

service.method2();

System.out.println("after method2");

System.out.println("before method3");

service.method3();

System.out.println("after method3");

System.out.println("before method4");

service.method4();

System.out.println("after method4");

}

}

在运行 UserService 之前,将 Log4J 日志级别设置为 DEBUG,运行以上代码查看输出日志,如下所示:

17:24:10,953  (AbstractBeanFactory.java:241)

- Returning cached instance of singleton bean ‘userService‘

before method1

method1

after method1

before method2

method2

after method2

before method3

method3

after method3

before method4

17:24:10,953  (AbstractPlatformTransactionManager.java:365)

- Creating new transaction with name [user.special.UserService.method4]:

PROPAGATION_REQUIRED,ISOLATION_DEFAULT

17:24:11,109  (DataSourceTransactionManager.java:205)

- Acquired Connection [[email protected]]

for JDBC transaction

17:24:11,109  (DataSourceTransactionManager.java:265)

- Committing JDBC transaction on Connection

[[email protected]]

17:24:11,125  (DataSourceTransactionManager.java:323)

- Releasing JDBC Connection [[email protected]]

after transaction

17:24:11,125  (DataSourceUtils.java:312)

- Returning JDBC Connection to DataSource

after method4

观察以上输出日志,很容易发现 method1~method3 这 3 个方法都没有被实施 Spring 的事务增强,只有 method4 被实施了事务增强。这个结果刚才验证了我们前面的论述。

我们通过下表描述哪些特殊方法将成为 Spring AOP 事务增强的漏网之鱼:

表 2. 不能被 Spring AOP 事务增强的方法

动态代理策略 不能被事务增强的方法

基于接口的动态代理 除 public 外的其它所有的方法,此外 public static 也不能被增强

基于 CGLib 的动态代理 private、static、final 的方法

不过,需要特别指出的是,这些不能被 Spring 事务增强的特殊方法并非就不工作在事务环境下。只要它们被外层的事务方法调用了,由于 Spring 的事务管理的传播特殊,内部方法也可以工作在外部方法所启动的事务上下文中。我们说,这些方法不能被 Spring 进行 AOP 事务增强,是指这些方法不能启动事务,但是外层方法的事务上下文依就可以顺利地传播到这些方法中。

这些不能被 Spring 事务增强的方法和可被 Spring 事务增强的方法唯一的区别在 “是否可以主动启动一个新事务”:前者不能而后者可以。对于事务传播行为来说,二者是完全相同的,前者也和后者一样不会造成数据连接的泄漏问题。换句话说,如果这些“特殊方法”被无事务上下文的方法调用,则它们就工作在无事务上下文中;反之,如果被具有事务上下文的方法调用,则它们就工作在事务上下文中。

对于 private 的方法,由于最终都会被 public 方法封装后再开放给外部调用,而 public 方法是可以被事务增强的,所以基本上没有什么问题。在实际开发中,最容易造成隐患的是基于 CGLib 的动态代理时的“public static”和“public final”这两种特殊方法。原因是它们本身是 public 的,所以可以直接被外部类(如 Web 层的 Controller 类)调用,只要调用者没有事务上下文,这些特殊方法也就以无事务的方式运作。

时间: 2024-08-27 03:17:33

spring 事务代理的方法的相关文章

spring事务管理-摘抄

原著网址 http://gcq04552015.iteye.com/blog/1666570 Spring是以代理的方式实现对事务的管理.我们在Action中所使用的Service对象,其实是代理对象的实例,并不是我们所写的Service 对象实例.既然是两个不同的对象,那为什么我们在Action中可以象使用Service对象一样的使用代理对象呢?为了说明问题,假设有个 Service类叫AService,它的Spring事务代理类为AProxyService,AService实现了一个接口 I

2015第24周一Spring事务

1. Spring事务管理简介 (1)Spring为多种不同类型的事务管理机制提供统一编程模型,这些事务管理模型包括JTA.JDBC.Hibernate.JPA和JDO. (2)Spring支持声明式事务管理(使用XML文档配置(或者Annotation)结合AOP实现的事务管理). (3)为代码嵌入式(programmatic)的事务管理提供API接口,与复杂的JTA接口相比要简单的多. (4)能够与Spring的数据抽象访问完美结合. 2. Spring事务管理的优点 一般认为,JavaEE

Spring 事务管理高级应用难点剖析--转

第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN&en=utf&hpp=20&dws=cndw&lo=zh 概述 Spring 最成功,最吸引人的地方莫过于轻量级的声明式事务管理,仅此一点,它就宣告了重量级 EJB 容器的覆灭.Spring 声明式事务管理将开发者从繁复的事务管理代码中解脱出来,专注于业务逻辑的开发上,这是一件

详细介绍Spring事务管理

在学习spring事务管理时,我忍不住要问,spring为什么进行事务管理,spring怎么进行的事务管理?首先,为什么要进行事务,接下来说说spring是怎样进行事务管理的. 我们都知道spring提供两种管理事务的方式,一种是声明式事务,一种是编程式事务. Spring的声明式事务管理,基于Spring的AOP,不再需要不停地写commit,rollback,(但Spring仍然没有放弃编程式的事务管理策略). Spring的编程式事务管理,为我们提供了一个TransactionTempla

Spring事务传播特性的浅析——事务方法嵌套调用的迷茫

Spring事务传播机制回顾 Spring事务一个被讹传很广说法是:一个事务方法不应该调用另一个事务方法,否则将产生两个事务.结果造成开发人员在设计事务方法时束手束脚,生怕一不小心就踩到地雷. 其实这是不认识Spring事务传播机制而造成的误解,Spring对事务控制的支持统一在TransactionDefinition类中描述,该类有以下几个重要的接口方法: int getPropagationBehavior():事务的传播行为 int getIsolationLevel():事务的隔离级别

Spring,为内部方法新起一个事务,此处应有坑。

事务的作用,使我们操作能够连贯起来.而spring则是提供了一个更简单的方法,只要使用 @Transactional 一个注解,就可以保证操作的连贯性了. 普通用法,稍后再说,这里要说的是: 在最外面的方法中,有一个@Transactional 的注解,当有抛出异常时,则进行回滚操作: @Transactional(readOnly = false, rollbackFor = Throwable.class, isolation = Isolation.REPEATABLE_READ) 原本这

Spring事务配置的五种方法

总结如下: Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource.TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分. DataSource.TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager.具体

Spring事务内方法调用自身事务 增强的三种方式

ServiceA.java文件: 查看Spring Tx的相关日志:  可以看到只创建了一个事物ServiceA.service方法的事务,但是callSelf方法却没有被事务增强; 分析原因:Spring事务生成的对象也是被Cglib或JDK代理的对象,就区别于该对象本身了, 代理的对象执行方法之前会走拦截器链,就不能同this方法. 解决方案: 使用Autowired注解将自身注入,然后调用注入属性的方法; 验证输出结果:  确实初始化了callSelf的事务; 2.之前Aop可以将代理对象

spring配置代理事务管理配置

方式一 <!-- 用代理类对 TransactionManager进行组合切面事务管理 --> <tx:advice id="advice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="find*" propagation="REQUIRED" read-only="tr