天天看点

spring框架详解系列(二)------AOP及spring事务、传播行为相关总结

原创文章,转载请注明出处!!!

Spring框架—面向切面编程(AOP)

1 什么是AOP

  • 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  • AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(简单概括为8个字:纵向重复,横向抽取)

    经典应用:事务管理、性能监视、安全检查、缓存 、日志等

  • Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
  • AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入

例如:

实现AOP思想的有:拦截器(拦截器处理action请求,在每个action方法前或后增减操作,实现功能),过滤器(使用过滤器处理Post乱码请求),动态代理实现事务。

2 AOP实现原理

概念:所谓spring的aop开发其实就是spring封装了代理技术来体现aop思想.

spring框架一共封装了两种代理技术来体现aop思想.

接口 + 实现类 :spring采用 jdk 的动态代理Proxy。注意:动态代理需要有接口才能对类进行代理.是有局限性的.

实现类:spring 采用 cglib字节码增强。cglib代理原理属于继承式代理.如果目标类(被代理对象)可被继承.那么就可以使用cglib代理增强.

注意:cglib代理无法增强被final修饰的类.以为被fanal修饰,无法被继承.

基础知识积累:装饰者模式

动态代理是装饰者模式的一种延伸和封装:

装饰者必备的条件:实现相同的接口;含有(可获取)当前实现类的对象;在(可调用)相应的方法,在该方法前后增减相关功能实现不同的功能。

装饰者实现代码如下

public class UserServiceWarpper implements UserService {
    
    	private UserService target;
    	
    	public UserServiceWarpper(UserService target) {
    		super();
    		this.target = target;
    	}
    
    	@Override
    	public void save() {
    		System.out.println("开启事务!");
    		target.save();
    		System.out.println("事务关闭!");
    		
    	}
    
    	@Override
    	public void delete() {
    		System.out.println("开启事务!");
    		target.delete();
    		System.out.println("事务关闭!");
    	}
    
    	@Override
    	public void update() {
    		System.out.println("开启事务!");
    		target.update();
    		System.out.println("事务关闭!");
    	}
    
    	@Override
    	public void find() {
    		System.out.println("开启事务!");
    		target.find();
    		System.out.println("事务关闭!");
    	}
    }
    
 //测试类
public class Demo {
	@Test
	public void fun1(){
		
		//1 创建目标对象
		UserService target = new UserServiceImpl();
		//2 创建装饰对象
		UserService proxy = new UserServiceWarpper(target);
		//3 测试
		proxy.save();
		proxy.delete();
		
	}
}
           

详细了解访问:https://www.jianshu.com/p/d7f20ae63186

3 AOP术语

AOP联盟提出的AOP领域的专用名词

1.Joinpoint(连接点):所谓连接点是指那些可能被代理(拦截)的方法。

2.PointCut 切入点:已经或即将被代理(增强)的方法(连接点)。例如:save()

3.advice 通知/增强,需要对切点增强代码。例如:after、before

4.target:目标对象,需要被代理的类。例如:UserService

5.proxy 代理对象(类)

6.Weaving(织入):动词,对目标对象中的切点应用(织入)了通知之后形成的对象;

即是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.

7.Aspect(切面): 是切入点pointcut和通知advice的结合

一个切入点和一个通知,组成成一个特殊的面。

如图下图所示
           
spring框架详解系列(二)------AOP及spring事务、传播行为相关总结

4 AOP实现方式

手动实现spring支持的两种动态代理的代码

1.目标类:接口 + 实现类(jdk动态代理)

public interface UserService {
	void save();
	void delete();
	void update();
	void find();
}

 //目标对象(被代理对象)
public class UserServiceImpl implements UserService {
	@Override
	public void save() {
		System.out.println("用户新增!");
		//int i = 1/0;
	}
	@Override
	public void delete() {
		System.out.println("用户删除!");
	}
	@Override
	public void update() {
		System.out.println("用户修改!");
	}
	@Override
	public void find() {
		System.out.println("用户查询!");
	}
}
           

3.工厂类:编写工厂生成代理对象

//演示动态代理实现InvocationHandler
public class UserServiceProxyFactory1 implements InvocationHandler {
	//目标对象
	private UserService target;
	
	public UserServiceProxyFactory1(UserService target) {
		super();
		this.target = target;
		
		if(this.target == null){
			throw new RuntimeException("目标对象不能为空!");
		 }
	}
	
	//创建动态代理对象并返回
	public UserService getUserServiceProxy(){
		//参数1:类的加载器=> 加载动态代理生成的类
		//参数2:代理类需要实现的接口(们)
		//参数3:
		return (UserService) Proxy.newProxyInstance(this.getClass().getClassLoader(),
									  new Class[]{UserService.class},this);
	}
	@Override
	//参数1: 代表代理对象
	//参数2: 代表当前代理的业务方法
	//参数3: 方法执行参数
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("开启事务");
		//调用业务方法
		Object result = method.invoke(target, args);
		System.out.println("关闭事务");
		return result;
	}	
}
//测试类
public class Demo {
	@Test
	public void fun1(){
		
		//1 创建目标对象
		UserService target = new UserServiceImpl();
		//2 创建代理工厂
		UserServiceProxyFactory1 factory = new UserServiceProxyFactory1(target);
		//3 创建代理
		UserService us = factory.getUserServiceProxy();
		//4 测试
		us.save();
		
		System.out.println(us instanceof UserServiceImpl);
	}
}
           

导入相关的包Spring-core…jar

//演示CGLIB代理
public class UserServiceProxyFactory2 implements MethodInterceptor {
	//创建cglib代理对象并返回
	public UserService getUserServiceProxy(){
		Enhancer en = new Enhancer();
		//指定谁是目标对象 => cglib好生成子类(代理对象)
		en.setSuperclass(UserServiceImpl.class);
		//指定代理类中的方法如何实现 (类似于动态代理方法Proxy.newProxyInstance中的第三个参数)
		en.setCallback(this);
		return (UserService) en.create();
	}
	@Override
	//参数1: 代理对象
	//参数2: 当前代理的业务方法
	//参数3: 方法参数
	//参数4: 代理方法对象
	public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy methodProxy) throws Throwable {
		System.out.println("开启事务");
		//调用业务方法 => 调用父类方法
		Object result = methodProxy.invokeSuper(arg0, arg2);
		System.out.println("关闭事务");
		return result;
	}
}
//测试类
public class Demo2 {
	@Test
	public void fun1(){
		
		//2 创建代理工厂
		UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
		//3 创建代理
		UserService us = factory.getUserServiceProxy();
		//4 测试
		us.save();
		
		System.out.println(us instanceof UserServiceImpl);
	}
}
           

springAOP开发流程(xml)

1.导入相关的jar包:

4+2(context,beans、core、expression+common、log4j)

spring-aop 、spring-aspects

aop联盟 aspectsJ-weaver

junit测试包

2.准备目标类

如前文书写UserService

3.准备通知

/通知类
public class MyAdvice {
	public void MYPC(){}
	/*
	 * 前置通知: 在切点执行之前执行的代码 环绕通知: 在切点执行之前和之后执行的代码 后置通知:
	 * 在切点执行之后执行的代码.如果切点方法执行出现异常就不执行. 后置通知: 在切点执行之后执行的代码.如果切点方法执行出现异常仍然执行. 异常通知:
	 * 出现异常后才执行通知代码.
	 */
	public void before() {
		System.out.println("前置通知!");
	}
	
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("环绕通知!-前面部分");
		// 执行切点(业务)方法
		Object result = pjp.proceed();
		System.out.println("环绕通知!-后面部分");
		return result;
	}

	public void afterReturning() {
		System.out.println("后置通知!如果切点方法执行出现异常就不执行.");
	}


	public void after() {
		System.out.println("后置通知!如果切点方法执行出现异常仍然执行.");
	}


	public void afterThrowing() {
		System.out.println("异常通知!");
	}
}

           

4.引入Aop约束,配置切面

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:aop="http://www.springframework.org/schema/aop" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
	<!-- 注册目标对象 -->
	<bean name="userService" class="com.huawei.service.UserServiceImpl" ></bean>
	<!-- 注册通知对象 -->
	<bean name="myAdvice" class="com.huawei.spring_aop.MyAdvice" ></bean>
	<!-- 配置切面=>切点+通知 -->
	<!-- 切点表达式
			execution(表达式)
			public void com.huawei.service.UserServiceImpl.save()
			void com.huawei.service.UserServiceImpl.save() 默认就是public
			* com.huawei.service.UserServiceImpl.save() 返回值任意
			* com.huawei.service.*ServiceImpl.save() 指定包中所有ServiceImpl结尾的类
			* com.huawei.service.*ServiceImpl.*() 方法名任意
			* com.huawei.service.*ServiceImpl.*(..) 参数列表任意
			* com.huawei.service..*ServiceImpl.*(..) 当前包以及后代包
	 -->
	<aop:config>
		<!-- 定义切点 -->
		<aop:pointcut id="myAdvice" expression="execution(*com.huawei.service.*ServiceImpl.*(..))" id="MyPC"/>
		<!-- 定义切面 -->
		<aop:aspect ref="myAdvice" >
			<!-- 前置通知 
				将myAdvice对象中的before方法作为通知织入到MyPC指定的切点(方法)
			-->
			<aop:before method="before" pointcut-ref="MyPC"/>
			<!-- 环绕通知 -->
			<aop:around method="around" pointcut-ref="MyPC"/>
			<!-- 后置通知 -->
			<aop:after-returning method="afterReturning" pointcut-ref="MyPC"/>
			<!-- 后置通知 -->
			<aop:after method="after" pointcut-ref="MyPC"/>
			<!-- 异常通知 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref="MyPC"/>
		</aop:aspect>
	</aop:config>
</beans>
           

5.测试类

//创建spring容器 
@RunWith(SpringJUnit4ClassRunner.class)
//指定spring容器配置
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
	@Autowired
	private UserService us;
	
	@Test
	public void fun1() throws Exception{
		us.save();
	}
}
           

springAOP开发流程(注解版)

/通知类
//@Aspect表示在当前类中配置切面
@Aspect
public class MyAdvice {
	//抽取切点表达式
	@Pointcut("execution(* com.huawei.service.*ServiceImpl.*(..))")
	public void MYPC(){}
	
	/*
	 * 前置通知: 在切点执行之前执行的代码 环绕通知: 在切点执行之前和之后执行的代码 后置通知:
	 * 在切点执行之后执行的代码.如果切点方法执行出现异常就不执行. 后置通知: 在切点执行之后执行的代码.如果切点方法执行出现异常仍然执行. 异常通知:
	 * 出现异常后才执行通知代码.
	 */
	// 前置通知
	//前置切面 => 通知+切点
	@Before("MyAdvice.MYPC()")
	public void before() {
		System.out.println("前置通知!");
	}
	// 环绕通知
	@Around("MyAdvice.MYPC()")
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("环绕通知!-前面部分");
		// 执行切点(业务)方法
		Object result = pjp.proceed();
		System.out.println("环绕通知!-后面部分");
		return result;
	}
	// 后置通知
	@AfterReturning("execution(* com.huawei.service.*ServiceImpl.*(..))")
	public void afterReturning() {
		System.out.println("后置通知!如果切点方法执行出现异常就不执行.");
	}
	// 后置通知
	@After("execution* com.huawei.service.*ServiceImpl.*(..))")
	public void after() {
		System.out.println("后置通知!如果切点方法执行出现异常仍然执行.");
	}
	// 异常通知
	@AfterThrowing("execution(* com.huawei.service.*ServiceImpl.*(..))")
	public void afterThrowing() {
		System.out.println("异常通知!");
	}
}

           

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:aop="http://www.springframework.org/schema/aop" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
	<!-- 注册目标对象 -->
	<bean name="userService" class="com.huawei.service.UserServiceImpl" ></bean>
	<!-- 注册通知对象 -->
	<bean name="myAdvice" class="com.huawei.spring_aop.MyAdvice" ></bean>
	<!-- 开启注解管理实务开关 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
           

测试类

//创建spring容器 
@RunWith(SpringJUnit4ClassRunner.class)
//指定spring容器配置
@ContextConfiguration("classpath:applicationContext2.xml")
public class Demo2 {
	@Autowired
	private UserService us;
	
	@Test
	public void fun1() throws Exception{
		us.save();
	}

}
           

Spring框架—事务处理及传播行为

  • spring中事务控制位于分层开发照片那个的业务层,是为业务层事务处理提供解决方案
  • spring框架为我们提供了一组事务控制的接口,需要导入spring-tx.xx.jar
  • spring事务控制都是基于aop思想的,既可以通过编程的方式实现也可以通过spring配置的方式实现

spring中事务管理的API介绍

常用的操作事务的方法为PlatFormTransactionManager接口的方法:

获取事务

TransactionStatus getTransaction(TransactionDefinition definition)

提交事务

void commit(TransactionStatus status)

回滚事务

void rollback(TransactionStatus status)

TransactionDefinition 类时事务定义信息对象的

方法有:

String getName() 获取事务对象的名称

Int getIsolationLevel() 获取事务的隔开级别

Int getTimeOut() 获取超时时间

Int getpropagationbehavior() 获取事务的传播行为

开发中我们使用它实现类:

DataSourceTransactionManager 使用springJDBC或mybatis实现持久化数据库

HitbernateTransactionManager 使用Hitbernate实现持久化数据库

JpaTransactionManager 使用JPA实现持久化数据库

事务的隔离级别

理解事务的4种隔离级别

数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。下面通过事例一一阐述它们的概念与联系。

Read uncommitted

读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。

事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。

分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。

那怎么解决脏读呢?Read committed!读提交,能解决脏读问题。

Read committed

读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。

事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…

分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。

那怎么解决可能的不可重复读问题?Repeatable read !

Repeatable read

重复读,就是在开始读取数据(事务开启)时,不再允许修改操作

事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。

分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。

什么时候会出现幻读?

事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。

那怎么解决幻读问题?Serializable!

Serializable 序列化

Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

值得一提的是:大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是Repeatable read。

现在来看看MySQL数据库为我们提供的四种隔离级别:

① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

② Repeatable read (可重复读):可避免脏读、不可重复读的发生。

③ Read committed (读已提交):可避免脏读的发生。

④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。

  

存在的问题:

l 脏读:一个线程中的事务读到了另外一个线程中未提交的数据。

l 不可重复读:一个线程中的事务读到了另外一个线程中已经提交的update的数据。

l 虚读:一个线程中的事务读到了另外一个线程中已经提交的insert的数据。

解决:

要想避免以上现象,通过更改事务的隔离级别来避免:

l READ UNCOMMITTED 脏读、不可重复读、虚读有可能发生。

l READ COMMITTED 避免脏读的发生,不可重复读、虚读有可能发生。

l REPEATABLE READ 避免脏读、不可重复读的发生,虚读有可能发生。

l SERIALIZABLE 避免脏读、不可重复读、虚读的发生。

拓展:

级别依次升高,效率依次降低。

MySQL:默认REPEATABLE READ

ORACLE:默认READ COMMITTED

MySQL:select @@tx_isolation;//查看当前的隔离级别

set transaction isolation level 级别;// 设置当前的事务隔离级别

详细访问:https://blog.csdn.net/hl93hnrz/article/details/55269893

Spring事务的传播行为

传播行为是指业务方法平行调用时,决定被调用方法的事务应该如何处理的问题

传播行为的常见类型和说明如下:

事务传播行为类型 说明

PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。

PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

详细访问:https://segmentfault.com/a/1190000013341344

事务在spring中的使用

准备服务类及接口

//该方法是注解版的,不加注解则需在xml中配置相关的事务
public interface BankService {
	//转账
	void transfer(Integer from,Integer to,Double money);
}


//为Service中的所有业务方法都织入事务通知
@Transactional(readOnly=true,propagation=Propagation.REQUIRED,isolation=Isolation.REPEATABLE_READ)
public class BankServiceImpl implements BankService {

	
	private BankDao bd;
	
	@Override
	//转账方法
	//注解加在方法上回覆盖类上的配置
	@Transactional(readOnly=false,propagation=Propagation.REQUIRED,isolation=Isolation.REPEATABLE_READ)
	public void transfer(Integer from, Integer to, Double money) {
		
		bd.decreaseMoney(from, money);
		//int i = 1/0;
		bd.increaseMoney(to, money);
		
	}

	public void setBd(BankDao bd) {
		this.bd = bd;
	}
}

           

准备数据库访问类Dao接口和实现类(此处省略接口,只提供实现类)

import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class BankDao extends JdbcDaoSupport {

	
	//给指定账户加钱
	public void increaseMoney(Integer to , Double money){
		
		String sql = "update t_bank set money = money+? where id = ?";
		//获取jdbc模板对象操作数据库
		getJdbcTemplate().update(sql, money,to);
	}
	//给指定账户扣钱
	public void decreaseMoney(Integer from , Double money){
		
		String sql = "update t_bank set money = money-? where id = ?";
		
		getJdbcTemplate().update(sql, money,from);
	}
	
}

           

配置xml文件天剑事务管理器和数据源

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:tx="http://www.springframework.org/schema/tx" 
xmlns:aop="http://www.springframework.org/schema/aop" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd 
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
 http://www.springframework.org/schema/aop 
 http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
    
     
    <!-- 读取db.properties -->
    <context:property-placeholder location="classpath:db.properties" />
    
    <!-- c3p0连接池 -->
    <!-- ${jdbc.jdbcUrl} => 根据键引用properties中对应的值 -->
    <bean name="dataSouce" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
    	<property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property>
    	<property name="driverClass" value="${jdbc.driverClass}" ></property>
    	<property name="user" value="${jdbc.user}" ></property>
    	<property name="password" value="${jdbc.password}" ></property>
    </bean>
    <!-- 配置事务管理器 -->
    <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
    	<!-- 引入数据源,关联数据源 -->
    	<property name="dataSource" ref="dataSouce" ></property>
    </bean>
    <!-- 一、配置注解事务管理 -->
	    <!-- 开启注解事务开关 -->
	    <tx:annotation-driven transaction-manager="transactionManager" />
    <!-- 二、配置事务通知和切面,若使用注解则可不配置 -->
      <!-- 配置事务通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager" >
    	<tx:attributes>
    		<!-- 为方法指定事务属性 
    				name:方法名
    					save* 所有save开头的方法
    				read-only:是否只读
    				isolation:隔离级别
    				propagation:传播行为
    		-->
    		<tx:method name="save*"  read-only="false" isolation="REPEATABLE_READ" propagation="REQUIRED" />
    		<tx:method name="delete*"  read-only="false" isolation="REPEATABLE_READ" propagation="REQUIRED" />
    		<tx:method name="update*"  read-only="false" isolation="REPEATABLE_READ" propagation="REQUIRED" />
    		<tx:method name="find*"  read-only="true" isolation="REPEATABLE_READ" propagation="REQUIRED" />
    		<tx:method name="*"  read-only="false" isolation="REPEATABLE_READ" propagation="REQUIRED" />
    	</tx:attributes>
    </tx:advice>
    <!-- 配置事务切面 -->
    <aop:config>
    	<!-- 切点 -->
    	<aop:pointcut expression="execution(* com.huawei.service.*ServiceImpl.*(..))" id="myPC"/>
    	<!-- 切面 -->
    	<aop:advisor advice-ref="txAdvice" pointcut-ref="myPC" />
    	<aop:aspect>
    	</aop:aspect>
    </aop:config>
    <!-- 配置Dao -->
     <bean name="bd" class="com.huawei.dao.BankDao" >
     	<property name="dataSource" ref="dataSouce" ></property>
    </bean>
    
    <!-- 配置Service  -->
    <bean name="bs" class="com.huawei.service.BankServiceImpl" >
    	<property name="bd" ref="bd" ></property>
    </bean>
</beans>
           

db.properties文件

jdbc.jdbcUrl=jdbc:mysql:///hibernate_test
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=1234
           

以上配置事务时配置的事务属性和事务的传播行为。

事务属性如下

readonly:只读,取值true/false(查询时可设该属性为true)

isolation:隔离级别

有四个取值1 读未提交 2 读已提交 4可重复读 8串行化

propergation 传播行为 有7个值

timeout 超时时间

rollback-for 为xxx异常回滚 , 配置完整类名

no-rollback-for 不为xxx异常回滚, 配置完整类名

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:com/huawei/spring_aop_tx/applicationContext.xml")
public class Demo {
	@Autowired
	private BankService bs;
	
	@Test
 public void fun2() throws Exception{
		bs.transfer(1, 2, 100d);
	}
}
注解版的服务如上实现类BankServiceImpl所示
           

参考文章:

事务传播行为详解:https://segmentfault.com/a/1190000013341344

事务理解:https://www.cnblogs.com/xiarongjin/p/8405510.html