代码已上传到GitHub,有兴趣的同学可以下载来看看:https://github.com/ylw-github/Java-CodeAnalysis-Demo
手写Spring 事务框架之前,我们先来熟悉一下Spring事务的两种写法,即“编程式事务”和“声明式事务”。
1. Spring 事务的两种常规写法
1.1 编程事务实现
编程式事务 指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理。管理使用TransactionTemplate
或者直接使用底层的PlatformTransactionManager
。对于编程式事务管理,Spring推荐使用TransactionTemplate
。下面我们使用编程式事务实现手动事务,使用编程事务实现,手动事务begin
、commit
、rollback
:
1. 需要添加的依赖:
<!-- 1.Spring核心依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.7.RELEASE</version> </dependency> <!-- 2.Spring dao依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.3.7.RELEASE</version> </dependency> <!-- AOP --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.1</version> </dependency> <!-- 数据库 --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.25</version> </dependency> <!-- Spring单元测试 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.2.5.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
2. 事务工具类TransactionUtils
package com.ylw.transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.DefaultTransactionAttribute; @Component public class TransactionUtils { @Autowired private DataSourceTransactionManager dataSourceTransactionManager; // 开启事务 public TransactionStatus begin() { TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute()); return transaction; } // 提交事务 public void commit(TransactionStatus transactionStatus) { dataSourceTransactionManager.commit(transactionStatus); } // 回滚事务 public void rollback(TransactionStatus transactionStatus) { dataSourceTransactionManager.rollback(transactionStatus); } }
3. AOP 技术封装手动事务AOPTransaction
package com.ylw.transaction; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.TransactionAspectSupport; @Component @Aspect public class AopTransaction { @Autowired private TransactionUtils transactionUtils; // // 异常通知 @AfterThrowing("execution(* com.ylw.transaction.UserService.add(..))") public void afterThrowing() { System.out.println("程序已经回滚"); // 获取程序当前事务 进行回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } // 环绕通知 @Around("execution(* com.ylw.transaction.UserService.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("开启事务"); TransactionStatus begin = transactionUtils.begin(); proceedingJoinPoint.proceed(); transactionUtils.commit(begin); System.out.println("提交事务"); } }
4. 数据库操作UserDao
package com.ylw.transaction; import org.springframework.stereotype.Component; @Component public class UserDao { public void add(String name, int age) { System.out.println("模拟插入数据库 name->" + name + " age->" + age); } }
5. 业务类UserService
package com.ylw.transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; @Service public class UserService { @Autowired private UserDao userDao; @Autowired private TransactionUtils transactionUtils; public void add() { TransactionStatus transactionStatus = null; try { transactionStatus = transactionUtils.begin(); userDao.add("张三", 27); int i = 1 / 0; //模拟抛出异常 System.out.println("我是add方法"); userDao.add("李四", 16); transactionUtils.commit(transactionStatus); } catch (Exception e) { e.printStackTrace(); } finally { if (transactionStatus != null) { transactionStatus.rollbackToSavepoint(transactionStatus); } } } }
6.单元测试工具类
import com.ylw.transaction.UserService; import com.ylw.transaction.UserService2; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class TransactionTest { @Autowired UserService userService; @Test public void test(){ userService.add(); } }
运行后,发现数据库并没有插入数据:
注意:
事务是程序运行如果没有错误,会自动提交事物,如果程序运行发生异常,则会自动回滚,如果使用了try捕获异常时.一定要在catch里面手动回滚。
事务手动回滚代码:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
1.2 声明事务实现
管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional
注解的方式),便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。
声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。下面我们举一个声明式事务的例子:
1. 注解版声明:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 开启注解 --> <context:component-scan base-package="com.ylw"></context:component-scan> <!-- 1. 数据源对象: C3P0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test"></property> <property name="user" value="root"></property> <property name="password" value="123456"></property> </bean> <!-- 2. JdbcTemplate工具类实例 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事物 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 开启注解事物 --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager" /> </beans>
2. 业务类使用:
package com.ylw.transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; @Service public class UserService2 { @Autowired private UserDao userDao; @Transactional public void add() { userDao.add("张三", 27); int i = 1 / 0; //模拟抛出异常 System.out.println("我是add方法"); userDao.add("李四", 16); } }
3. 单元测试(test1方法):
import com.ylw.transaction.UserService; import com.ylw.transaction.UserService2; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class TransactionTest { @Autowired UserService userService; @Autowired UserService2 userService2; @Test public void test(){ userService.add(); } @Test public void test1(){ userService2.add(); } }
运行后,发现数据库并没有插入数据(和上面的一样):
2.手写Spring注解版本事务
2.1 注解
注解是Jdk1.5新增新技术,很多框架为了简化代码,都会提供有些注解。可以理解为插件,是代码级别的插件,在类的方法上写:@XXX
,就是在代码上插入了一个插件。
注解特点: 不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。
注解分类:
- 内置注解(也成为元注解 jdk 自带注解)
- 自定义注解(Spring框架)
2.1.1 内置注解
内置注解一般为JDK自带的注解,比如我们常用到的"@SuppressWarnings
"、"@Deprecated
"、"@Overricle
":
1)@SuppressWarnings 在程序前面加上可以在javac编译中去除警告–阶段是SOURCE
2) @Deprecated 带有标记的包,方法,字段说明其过时----阶段是SOURCE
3)@Overricle 打上这个标记说明该方法是将父类的方法重写–阶段是SOURCE
1. @Overricle 案例演示:
2. @Deprecated案例演示:
3. @SuppressWarnings 案例演示:
2.1.2 自定义注解
首先要明确前面说的,注解的作用是用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。
下面介绍常用的的几个注解:
1. @Target
@Target 说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
CONSTRUCTOR: 用于描述构造器 FIELD: 用于描述域 LOCAL_VARIABLE: 用于描述局部变量 METHOD: 用于描述方法 PACKAGE: 用于描述包 PARAMETER: 用于描述参数 TYPE: 用于描述类、接口(包括注解类型) 或enum声明
2. @Retention
表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)。
3. @Document
描述 Annotation 应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。被例如javadoc此类的工具文档。
4. @Inherited
这也是一个标记注解。@Inherited 阐述了某个被标注的类型是被继承的。被标注过的 class 的子类所继承。类并不从它所实现的接口继承 Annotation,方法并不从它所重载的方法继承 Annotation。
当使用@Inherited类型标注的 Annotation 的 Retention 是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
5.例子
1)定义注解
package com.ylw.transaction.annonation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = { ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface AddAnnotation { int userId() default 0; String userName() default "默认名称"; String[]description(); }
2)使用注解
package com.ylw.transaction.annonation; public class User { @AddAnnotation(userName = "张三", userId = 1, description = {"高大", "英俊", "肥胖"}) public void add() { } }
3)测试注解
package com.ylw.transaction.annonation; import java.lang.reflect.Method; public class Test1 { public static void main(String[] args) throws ClassNotFoundException { Class classInfo = Class.forName("com.ylw.transaction.annonation.User"); // 获取到所有方法 Method[] methods = classInfo.getDeclaredMethods(); for (Method method : methods) { System.out.println(method); AddAnnotation declaredAnnotation = method.getDeclaredAnnotation(AddAnnotation.class); if (declaredAnnotation == null) { // 结束本次循环 continue; } // 获取userId int userId = declaredAnnotation.userId(); System.out.println("userId:" + userId); // 获取userName String userName = declaredAnnotation.userName(); System.out.println("userName:" + userName); // 获取arrays String[] description = declaredAnnotation.description(); for (String str : description) { System.out.println("desc:" + str); } } } }
运行结果:
2.2 自定义事务注解
1.TransactionUtils:
package com.ylw.transaction.spring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.DefaultTransactionAttribute; @Component public class TransactionUtils { @Autowired private DataSourceTransactionManager dataSourceTransactionManager; private TransactionStatus transaction; // 开启事务 public TransactionStatus begin() { transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute()); return transaction; } // 提交事务 public void commit(TransactionStatus transactionStatus) { dataSourceTransactionManager.commit(transactionStatus); } // 回滚事务 public void rollback() { dataSourceTransactionManager.rollback(transaction); } }
2.注解类ExtTransaction:
package com.ylw.transaction.spring.div; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //自定义事务注解 @Target(value = { ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface ExtTransaction { }
3.定义切面类
package com.ylw.transaction.spring.div; import com.ylw.transaction.spring.TransactionUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.TransactionAspectSupport; import java.lang.reflect.Method; @Component @Aspect public class AopExtTransaction { @Autowired private TransactionUtils transactionUtils; @AfterThrowing("execution(* com.ylw.transaction.spring.*.*.*(..))") public void afterThrowing() throws NoSuchMethodException, SecurityException { // isRollback(proceedingJoinPoint); System.out.println("程序发生异常"); // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // TransactionStatus currentTransactionStatus = // TransactionAspectSupport.currentTransactionStatus(); // System.out.println("currentTransactionStatus:" + // currentTransactionStatus); transactionUtils.rollback(); } // // 环绕通知 在方法之前和之后处理事情 @Around("execution(* com.ylw.transaction.spring.*.*.*(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 调用方法之前执行 TransactionStatus transactionStatus = begin(proceedingJoinPoint); proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出异常不会执行后面代码 // 调用方法之后执行 commit(transactionStatus); } public TransactionStatus begin(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException { // // 判断是否有自定义事务注解 ExtTransaction declaredAnnotation = getExtTransaction(pjp); if (declaredAnnotation == null) { return null; } // 如果有自定义事务注解,开启事务 System.out.println("开启事务"); TransactionStatus transactionStatu = transactionUtils.begin(); return transactionStatu; } public void commit(TransactionStatus transactionStatu) { if (transactionStatu != null) { // 提交事务 System.out.println("提交事务"); transactionUtils.commit(transactionStatu); } } public ExtTransaction getExtTransaction(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException { // 获取方法名称 String methodName = pjp.getSignature().getName(); // 获取目标对象 Class<?> classTarget = pjp.getTarget().getClass(); // 获取目标对象类型 Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes(); // 获取目标对象方法 Method objMethod = classTarget.getMethod(methodName, par); // // 判断是否有自定义事务注解 ExtTransaction declaredAnnotation = objMethod.getDeclaredAnnotation(ExtTransaction.class); if (declaredAnnotation == null) { System.out.println("您的方法上,没有加入注解!"); return null; } return declaredAnnotation; } // 回滚事务 public void isRollback(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException { // // 判断是否有自定义事务注解 ExtTransaction declaredAnnotation = getExtTransaction(pjp); if (declaredAnnotation != null) { System.out.println("已经开始回滚事务"); // 获取当前事务 直接回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return; } } }
4.业务类使用:
package com.ylw.transaction.spring; import com.ylw.transaction.spring.div.ExtTransaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService3 { @Autowired private UserDao userDao; @ExtTransaction public void add() { userDao.add("张三", 27); int i = 1 / 0; //模拟抛出异常 System.out.println("我是add方法"); userDao.add("李四", 16); } }
测试类(test2方法):
import com.ylw.transaction.spring.UserService; import com.ylw.transaction.spring.UserService2; import com.ylw.transaction.spring.UserService3; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class TransactionTest { @Autowired UserService userService; @Autowired UserService2 userService2; @Autowired UserService3 userService3; @Test public void test(){ userService.add(); } @Test public void test1(){ userService2.add(); } @Test public void test2(){ userService3.add(); } }
运行结果后,数据库没有改变:
4.Spring事务传播行为
Spring中事务的定义:
Propagation(key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。)有以下选项可供使用:
- PROPAGATION_REQUIRED–支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- PROPAGATION_SUPPORTS–支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY–支持当前事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW–新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED–以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER–以非事务方式执行,如果当前存在事务,则抛出异常。
总结