SpringAOP事务管理

先了解AOP的相关术语:

1.通知(Advice):

通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。

2.连接点(Joinpoint):

程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。

3.切入点(Pointcut)

通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,spring中允许我们方便的用正则表达式来指定

4.切面(Aspect)

通知和切入点共同组成了切面:时间、地点和要发生的“故事”

5.引入(Introduction)

引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)

6.目标(Target)

即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事)

7.代理(proxy)

应用通知的对象,详细内容参见设计模式里面的代理模式

8.织入(Weaving)

把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:

(1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器

(2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码

(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术

Spring提供了4种实现AOP的方式:

1.经典的基于代理的AOP

[email protected]注解驱动的切面

3.纯POJO切面

4.注入式AspectJ切面

Spring支持五种类型的通知:

Before(前)  org.apringframework.aop.MethodBeforeAdvice

After-returning(返回后) org.springframework.aop.AfterReturningAdvice

After-throwing(抛出后) org.springframework.aop.ThrowsAdvice

Arround(周围) org.aopaliance.intercept.MethodInterceptor

Introduction(引入) org.springframework.aop.IntroductionInterceptor

1、使用基于注解的AOP事务管理 

<tx:annotation-driven transaction-manager="transactionManager"/>

<aop:aspectj-autoproxy />

探索tx:annotation-driven标签:

<tx:annotation-driven/>标签是注解驱动的事务管理支持的核心。

<tx:annotation-driven/>标签的属性:

transaction-manager:指定到现有的PlatformTransactionManager bean的引用,通知会使用该引用。default="transactionManager"

mode:指定Spring事务管理框架创建通知bean的方式。可用的值有proxy和aspectj。前者是默认值,表示通知对象是个JDK代理;后者表示Spring AOP会使用AspectJ创建代理。

order:指定创建的切面的顺序。只要目标对象有多个通知就可以使用该属性。

proxy-target-class:该属性如果为true就表示你想要代理目标类而不是bean所实现的所有接口。default="false"

探索@Transactional注解:

你可以指定传播、隔离级别、超时以及允许和不允许的异常。

@Transactional注解的属性:

propagation:指定事务定义中使用的传播

isolation:设定事务的隔离级别

timeout:指定事务的超市(秒)

readOnly:指定事务的超时

noRollbackFor:目标方法可抛出的异常所构成的数组,但通知仍会提交事务

rollbackFor:异常所构成的数组,如果目标方法抛出了这些异常,通知就会回滚事务

基于注解的事务管理小结:

如果定义在类上,那么所有的方法都使用相同的方式,有些read就会抱怨给太多的东西了。

如果在每个方法上都定义注解,那么就会很麻烦。

(可以使用XML AOP事务管理能更好的处理这种情况)

2、使用XML AOP事务管理 

<tx:advice/>标签,该标签会创建一个事务处理通知。

view plaincopy
to clipboard
print?

  1. <tx:advice id="txAdvice" transaction-manager="transactionManager">
  2. <tx:attributes>
  3. <tx:method name="bulk*" propagation="REQUIRED" isolation="DEFAULT" />
  4. <tx:method name="load*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>
  5. </tx:attributes>
  6. </tx:advice>
  7. <aop:config>
  8. <aop:advisor pointcut="execution(* *..*Service*.*(..))" advice-ref="txAdvice" />
  9. </aop:config>
  10. <aop:config>
  11. <aop:pointcut id="allServiceMethods"
  12. expression="execution(* com.apress.prospring2.ch16.services.*.*(..))"/>
  13. <aop:advisor advice-ref="defaultTransactionAdvice"
  14. pointcut-ref="allServiceMethods"/>
  15. </aop:config>
  16. <tx:advice id="defaultTransactionAdvice" transaction-manager="transactionManager">
  17. <tx:attributes>
  18. <tx:method
  19. name="*"
  20. isolation="DEFAULT"
  21. propagation="REQUIRED"
  22. no-rollback-for="java.lang.RuntimeException"
  23. timeout="100"/>
  24. <tx:method
  25. name="get*"
  26. read-only="true"/>
  27. </tx:attributes>
  28. </tx:advice>

3、tx:advice标签简介 

id是该advice bean的标识,而transaction-manager则必须引用一个PlatformTransactionManager bean。

还可以通过<tx:attributes>标签定制<tx:advice>标签所创建的通知的行为。

<tx:method/>标签的属性:

name:方法名的匹配模式,通知根据该模式寻找匹配的方法。

propagation:设定事务定义所用的传播级别。

isolation:设置事务的隔离级别。

timeout:指定事务的超时(秒)。

read-only:该属性为true指示事务是只读的

no-rollback-for:以逗号分隔的异常类的列表,目标方法可以跑出这些异常而不会导致通知执行回滚

rollback-for:以逗号分隔的异常类的列表,当目标方法跑出这些异常时会导致通知执行回滚。默认情况下,该列表为空,因此不在no-rollback-for列表中的任何运行时异常都会导致回滚。

<tx:method>中isolation(隔离)和propagation(传播)参数的含义:

getIsolationLevel:他对其他事务所看到的数据变化进行控制。

事务隔离级别:

隔离级别 说明

ISOLATION_DEFAULT 默认级别(对大多数数据库来说就是ISOLATION_READ_COMMITTED)

ISOLATION_READ_UNCOMMITTED 最低的隔离级别。事实上我们不应该隔离级别,因为在事务完成前,其他事务可以看到该事务所修改的数据。而在其他事务提交前,该事务也可以看到其他事务所做的修改。

ISOLATION_READ_COMMITTED
大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。遗憾的是,在该事务提交后,你就可以查看其他事务插入活更新的数据。这意味着在事务的不同点上,如果其他事务修改数据,你会看到不同的数据。

ISOLATION_REPEATABLE_READ 该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。

ISOLATION_SERIALIZABLE 代价最大、可靠性最高的隔离级别,所有的事务都是俺顺序一个接一个的执行。

getPropagationBehavior:指定了当代码请求一个新的事务时Spring所做的事情。

传播行为指:

传播行为 说明

PROPAGATION_REQUIRED 当前如果有事务,Spring就会使用该事务;否则会开始一个新事务。

PROPAGATION_SUPPORTS 当前如果有事务,Spring就会使用该事务;否则不会开启一个新事务。

PROPAGATION_MANDATORY 当前如果有事务,Spring就会使用该事务;否则会抛出异常。

PROPAGATION_REQUIRES_NEW Spring总会开始一个新事务。如果当前有事务,则该事务挂起。

PROPAGATION_NOT_SUPPORTED Spring不会执行事务中的代码。代码总是在非事务环境下执行,如果当期有事务,则该事务挂起。

PROPAGATION_NEVER 即使当前有事务,Spring也会在飞事务环境下执行。如果当前有事务,则抛出异常。

PROPAGATION_NESTED 如果当前有事务,则在嵌套事务中执行。如果没有,那么执行情况与PROPAGATION_REQUIRED一样。

平时看一篇大牛写的案例给你们举个例子

具体做法。。。大晚上的就举个睡觉的例子吧:

首先写一个接口叫Sleepable,这是一个牛X的接口,所有具有睡觉能力的东西都可以实现该接口(不光生物,包括关机选项里面的休眠)

package test.spring.aop.bean

public interface Sleepable{

void sleep();

}

然后写一个Human类,他实现了这个接口

package test.spring.aop.bean

public Human implements Sleepable{

/*这人莫非跟寡人差不多?

*除了睡觉睡的比较好之外其余的什么也不会做?*/

public void sleep(){

System.out.println("睡觉了!梦中自有颜如玉!");

}

}

好了,这是主角,不过睡觉前后要做些辅助工作的,最基本的是脱穿衣服,失眠的人还要吃安眠药什么的,但是这些动作与纯粹的睡觉这一“业务逻辑”是不相干的,如果把

这些代码全部加入到sleep方法中,是不是有违单一职责呢?,这时候我们就需要AOP了。

编写一个SleepHelper类,它里面包含了睡觉的辅助工作,用AOP术语来说它就应该是通知了,我们需要实现上面的接口

package test.spring.aop.bean;

import Java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

import org.springframework.aop.MethodBeforeAdvice;

public class SleepHelper implements MethodBeforeAdvice,AfterReturningAdvice{

public void before(Method mtd, Object[] arg1, Object arg2)

throws Throwable {

System.out.println("通常情况下睡觉之前要脱衣服!");

}

public void afterReturning(Object arg0, Method arg1, Object[] arg2,

Object arg3) throws Throwable {

System.out.println("起床后要先穿衣服!");

}

}

然后在spring配置文件中进行配置:

<bean id="sleepHelper" class="test.spring.aop.bean.SleepHelper">

</bean>

OK!现在创建通知的工作就完成了.

第二步是进行配置,这是很令人蛋疼的操作,尤其是这么热的天,Spring又把东西的名字起的见鬼的长!它为啥不能像usr这种风格呢?

首先要做的是配置一个切点,据说切点的表示方式在Spring中有好几种,但是常用的只有两种:1.使用正则表达式 2.使用AspectJ表达式 AspectJ我不是很熟悉(我也是熟悉

党 or 精通党?),我还是习惯用正则表达式

Spring使用org.springframework.aop.support.JdkRegexpMethodPointcut来定义正则表达式切点

<bean id="spleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">

<property name="pattern" value=".*sleep"/>

</bean>

pattern属性指定了正则表达式,它匹配所有的sleep方法

切点仅仅是定义了故事发生的地点,还有故事发生的时间以及最重要的故事的内容,就是通知了,我们需要把通知跟切点结合起来,我们要使用的通知者是:

org.springframework.aop.support.DefaultPointcutAdvisor

<bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">

<property name="advice" ref="sleepHelper"/>

<property name="pointcut" ref="sleepPointcut"/>

</bean>

切入点和通知都配置完成,接下来该调用ProxyFactoryBean产生代理对象了

<bean id="humanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="target" ref="human"/>

<property name="interceptorNames" value="sleepHelperAdvisor" />

<property name="proxyInterfaces" value="test.spring.aop.bean.Sleepable" />

</bean>

ProxyFactoryBean是一个代理,我们可以把它转换为proxyInterfaces中指定的实现该interface的代理对象:

import org.springframework.aop.framework.ProxyFactoryBean;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import test.spring.aop.bean.Sleepable;

public class Test {

public static void main(String[] args){

ApplicationContext appCtx = new ClassPathXmlApplicationContext("applicationContext.xml");

Sleepable sleeper = (Sleepable)appCtx.getBean("humanProxy");

sleeper.sleep();

}

}

程序运行产生结果:

通常情况下睡觉之前要脱衣服!

睡觉啦~梦中自有颜如玉!

起床后要先穿衣服!

OK!这是我们想要的结果,但是上面这个过程貌似有点复杂,尤其是配置切点跟通知,Spring提供了一种自动代理的功能,能让切点跟通知自动进行匹配,修改配置文件如下:

<bean id="sleepHelper" class="test.spring.aop.bean.SleepHelper">

</bean>

<bean id="sleepAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

<property name="advice" ref="sleepHelper"/>

<property name="pattern" value=".*sleep"/>

</bean>

<bean id="human" class="test.spring.aop.bean.Human">

</bean>

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

执行程序:

public class Test {

public static void main(String[] args){

ApplicationContext appCtx = new ClassPathXmlApplicationContext("applicationContext.xml");

Sleepable sleeper = (Sleepable)appCtx.getBean("human");

sleeper.sleep();

}

}

成功输出结果跟前面一样!

只要我们声明了org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator(我勒个去的,名太长了)就能为方法匹配的bean自动创建代理!

但是这样还是要有很多工作要做,有更简单的方式吗?有!

一种方式是使用AspectJ提供的注解:

package test.mine.spring.bean;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

@Aspect

public class SleepHelper {

public SleepHelper(){

}

@Pointcut("execution(* *.sleep())")

public void sleeppoint(){}

@Before("sleeppoint()")

public void beforeSleep(){

System.out.println("睡觉前要脱衣服!");

}

@AfterReturning("sleeppoint()")

public void afterSleep(){

System.out.println("睡醒了要穿衣服!");

}

}

用@Aspect的注解来标识切面,注意不要把它漏了,否则Spring创建代理的时候会找不到它,@Pointcut注解指定了切点,@Before和@AfterReturning指定了运行时的通知,注

意的是要在注解中传入切点的名称

然后我们在Spring配置文件上下点功夫,首先是增加AOP的XML命名空间和声明相关schema

命名空间:

xmlns:aop="http://www.springframework.org/schema/aop"

schema声明:

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

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

然后加上这个标签:

<aop:aspectj-autoproxy/> 有了这个Spring就能够自动扫描被@Aspect标注的切面了

最后是运行,很简单方便了:

public class Test {

public static void main(String[] args){

ApplicationContext appCtx = new ClassPathXmlApplicationContext("applicationContext.xml");

Sleepable human = (Sleepable)appCtx.getBean("human");

human.sleep();

}

}

下面我们来看最后一种常用的实现AOP的方式:使用Spring来定义纯粹的POJO切面

前面我们用到了<aop:aspectj-autoproxy/>标签,Spring在aop的命名空间里面还提供了其他的配置元素:

<aop:advisor> 定义一个AOP通知者

<aop:after> 后通知

<aop:after-returning> 返回后通知

<aop:after-throwing> 抛出后通知

<aop:around> 周围通知

<aop:aspect>定义一个切面

<aop:before>前通知

<aop:config>顶级配置元素,类似于<beans>这种东西

<aop:pointcut>定义一个切点

我们用AOP标签来实现睡觉这个过程:

代码不变,只是修改配置文件,加入AOP配置即可:

<aop:config>

<aop:aspect ref="sleepHelper">

<aop:before method="beforeSleep" pointcut="execution(* *.sleep(..))"/>

<aop:after method="afterSleep" pointcut="execution(* *.sleep(..))"/>

</aop:aspect>

</aop:config>

主要平时项目中的话。看着这些注解代码就别奇怪了。应该明白他们是干嘛用的。

时间: 2024-08-28 20:26:18

SpringAOP事务管理的相关文章

Spring事务管理 与 SpringAOP

1,Spring事务的核心接口 Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略.  Spring事务管理涉及的接口的联系如下: 1.1 事务管理器 Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现. Spring事务管理器的接口是org.springframework.tra

使用SpringAOP实现事务(声明式事务管理、零配置)

前言: 声明式事务管理建立在AOP之上的.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务.声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中. 声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持. Sprin

10.Spring事务管理【TX】

转账案例环境搭建 1.引入JAR包 IOC的6个包 AOP的4个包 C3P0的1个包 MySQL的1个驱动包 JDBC的2个目标包 整合JUnit测试1个包 2.引入配置文件 log4j.properties+applicationContext.xml ### direct log messages to stdout ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=

注解方式实现声明式事务管理

使用注解实现Spring的声明式事务管理,更加简单! 步骤: 1) 必须引入Aop相关的jar文件 2) bean.xml中指定注解方式实现声明式事务管理以及应用的事务管理器类 3)在需要添加事务控制的地方,写上: @Transactional @Transactional注解: 1)应用事务的注解 2)定义到方法上: 当前方法应用spring的声明式事务 3)定义到类上:   当前类的所有的方法都应用Spring声明式事务管理; 4)定义到父类上: 当执行父类的方法时候应用事务. 修改bean

Spring与Hibernate整合,实现Hibernate事务管理

1.所需的jar包 连接池/数据库驱动包 Hibernate相关jar Spring 核心包(5个) Spring aop 包(4个) spring-orm-3.2.5.RELEASE.jar                 [spring对hibernate的支持] spring-tx-3.2.5.RELEASE.jar                     [事务相关] 2.配置文件 Product.hbm.xml <?xml version="1.0" encoding=

Spring中的Jdbc事务管理

Spring提供了对事务的声明式事务管理,只需要在配置文件中做一些配置,即可把操作纳入到事务管理当中,解除了和代码的耦合. Spring声明式事务管理,核心实现就是基于Aop. Spring声明式事务管理是粗粒度的事务控制,只能给整个方法应用事务,不可以对方法的某几行应用事务. Spring声明式事务管理器类: Jdbc技术:DataSourceTransactionManager Hibernate技术:HibernateTransactionManager 1.xml方式声明事务 引入tx名

Spring学习8-Spring事务管理(AOP/声明式式事务管理)

一.基础知识普及 声明式事务的事务属性: 一:传播行为 二:隔离级别 三:只读提示 四:事务超时间隔 五:异常:指定除去RuntimeException其他回滚异常.  传播行为: 所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为. spring的事务传播规则: 传播行为 意义 PROPAGATION_REQUIRED 如果当前存在事务,则加入该事务:如果当前没有事务,则创建一个新的事务. PROPAGATION_REQUIR

Spring声明式事务管理(基于注解方式实现)

----------------------siwuxie095 Spring 声明式事务管理(基于注解方式实现) 以转账为例 1.导入相关 jar 包(共 10 个包) (1)导入核心 jar 包和日志相关的 jar 包 (2)导入 JdbcTemplate 的 jar 包 (3)导入 MySQL 的 JDBC 驱动包 mysql-connector-java 下载链接: https://dev.mysql.com/downloads/connector/j/ (4)导入 AOP 的 jar

Spring的事务管理

事务 事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性(ACID) 原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 持久性:一旦结束,数据就永久的保存到数据库 如果不考虑隔离性 脏读:一个事务读到另一个事务未提交数据 不可重复读:一个事务读到另一个事务已经提交数据(update)导致一个事务多次查询结果不一致 虚读:一个事务读到另一个事务已经提交数据(insert)导致一个事务多次查询结果不一致 事务的隔离级别