spring事务注解@Transactional的实现原理(转)
出处:
Transactional是spring中定义的事务注解,在⽅法或类上加该注解开启事务。主要是通过反射获取bean的注解信息,利⽤AOP对编程式事务进⾏封装实现。AOP对事务的封装可以看。
我们先写个demo,感受它的加载过程。
spring事务注解:
1. ⾃定义⼀个注解
/**
* @Target  作⽤域(作⽤在⽅法上,类上,或是属性上)
* @Retention 运⾏周期
* @interface 定义注解
*/
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
//⾃定义注解的属性
int id() default 0;
String name() default "默认名称";
String[]arrays();
String title() default "默认标题";
}
2. 测试
import flect.Method;
public class User {
@MyAnnotation(name="吴磊",arrays = {"2","3"})
public  void aMethod () {}
public  void bMethod () {}
public static void main(String[] args) throws ClassNotFoundException {
//1. 反射获取到类信息
Class<?> forName = Class.forName("com.demo.User");
//2. 获取到当前类(不包含继承)所有的⽅法
Method[] declaredMethods = DeclaredMethods();
//3. 遍历每个⽅法的信息
for (Method method : declaredMethods) {
System.out.println("⽅法名称:" + Name());
//4. 获取⽅法上⾯的注解
MyAnnotation annotation = DeclaredAnnotation(MyAnnotation.class);
if(annotation == null) {
System.out.println("该⽅法上没有加注解....");
}else {
System.out.println("Id:" + annotation.id());
System.out.println("Name:" + annotation.name());
System.out.println("Arrays:" + annotation.arrays());
System.out.println("Title:" + annotation.title());
}
System.out.println("--------------------------");
}
}
}
=============================
【控制台输出】
⽅法名称:main
该⽅法上没有加注解....
--------------------------
⽅法名称:aMethod
Id:0
Name:吴磊
Arrays:[Ljava.lang.String;@74a14482
Title:默认标题
--------------------------
⽅法名称:bMethod
该⽅法上没有加注解....
--------------------------
总结:通过上⾯这么⼀个⼩demo我们就能发现,反射获取到每⼀个⽅法的注解信息然后进⾏判断,如果这是@Transactional注解,spring就会开启事务。接下来我们可以按照这种思路基于上⼀篇博客的编程式事务⾃⼰实现⼀个事务注解。
⼿写注解事务:
1. 导包
<dependencies>
<!-- 引⼊Spring-AOP等相关Jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
<!-- mvnrepository/hange/c3p0 -->
<dependency>
<groupId&hange</groupId>
spring roll怎么读<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- mvnrepository/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
</dependencies>
2. 配置l⽂件
<beans xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance" xmlns:p="/schema/p"    xmlns:context="/schema/context"
xmlns:aop="/schema/aop" xmlns:tx="/s
chema/tx"    xsi:schemaLocation="/schema/beans
/schema/beans/spring-beans.xsd
/schema/context
/schema/context/spring-context.xsd
/schema/aop
/schema/aop/spring-aop.xsd
/schema/tx
/schema/tx/spring-tx.xsd">
<!-- 扫描指定路劲 -->
<context:component-scan base-package="com.wulei"/>
<!-- 开启切⾯代理 -->
<aop:aspectj-autoproxy />
<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="hange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="sql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 2. JdbcTemplate⼯具类实例 -->
<bean id="jdbcTemplate" class="org.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3. 配置事务 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
3.1 ⾃定义事务注解 (通过反射解析⽅法上的注解,如果有这个注解就执⾏事务逻辑)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//@Transaction可以作⽤在类和⽅法上, 我们这⾥只作⽤在⽅法上。
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
/**
* ⾃定义事务注解
*/
public @interface MyAnnotation {
}
3.2 封装编程式事务
import org.springframework.beans.factory.annotation.Autowired;
import t.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import ansaction.TransactionStatus;
import ansaction.interceptor.DefaultTransactionAttribute;
@Component
@Scope("prototype") // 申明为多例,解决线程安全问题。
/**
* ⼿写编程式事务
*/
public class TransactionUtil {
/
/ 全局接受事务状态
private TransactionStatus transactionStatus;
// 获取事务源
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
// 开启事务
public TransactionStatus begin() {
System.out.println("开启事务");
transactionStatus = Transaction(new DefaultTransactionAttribute());        return transactionStatus;
}
// 提交事务
public void commit(TransactionStatus transaction) {
System.out.println("提交事务");
if(dataSourceTransactionManager != null) dataSourceTransactionManagermit(transaction);
}
// 回滚事务
public void rollback(TransactionStatus transaction) {
System.out.println("回滚事务...");
if(dataSourceTransactionManager != null) llback(transaction);
}
}
3.3 通过AOP封装事务⼯具类, 基于环绕通知和异常通知来触发事务。
import flect.Method;
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.flect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import ansaction.TransactionStatus;
import ansaction.interceptor.TransactionAspectSupport;
import ansaction.TransactionUtil;
@Aspect// 申明为切⾯
@Component
/**
* 切⾯类封装事务逻辑
*/
public class AopTransaction {
@Autowired
private TransactionUtil transactionUtil;
private TransactionStatus transactionStatus;
/**
* 环绕通知在⽅法之前和之后处理事情
* @param pjp 切⼊点
*/
@Around("execution(* com.wulei.service.*.*(..))")
public void around(ProceedingJoinPoint pjp) throws Throwable {
// 1.获取⽅法的注解
MyAnnotation annotation = MethodMyAnnotation(pjp);
// 2.判断是否需要开启事务
transactionStatus = begin(annotation);
// 3.调⽤⽬标代理对象⽅法
pjp.proceed();
// 4.判断关闭事务
commit(transactionStatus);
}
/**
* 获取代理⽅法上的事务注解
* @param pjp 切⼊点
*/
private MyAnnotation getMethodMyAnnotation(ProceedingJoinPoint pjp) throws Exception {        //1. 获取代理对对象的⽅法
String methodName = Signature().getName();
//2. 获取⽬标对象
Class<?> classTarget = Target().getClass();
//3. 获取⽬标对象类型
Class<?>[] par = ((MethodSignature) Signature()).getParameterTypes();
//4. 获取⽬标对象⽅法
Method objMethod = Method(methodName, par);
//5. 获取该⽅法上的事务注解
MyAnnotation annotation = DeclaredAnnotation(MyAnnotation.class);
return annotation;
}
/** 开启事务  */
private TransactionStatus begin(MyAnnotation annotation) {
if(annotation == null) return null;
return transactionUtil.begin();
}
/** 关闭事务 */
private void commit(TransactionStatus transactionStatus) {
if(transactionStatus != null) transactionUtilmit(transactionStatus);
}
/**
* 异常通知进⾏回滚事务
*/
@AfterThrowing("execution(* com.wulei.service.*.*(..))")
public void afterThrowing() {
// 获取当前事务直接回滚
/
/TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
if(transactionStatus != null) llback(transactionStatus);
}
}
4. 编写dao层
import org.springframework.beans.factory.annotation.Autowired;
import org.JdbcTemplate;
import org.springframework.stereotype.Repository;
/*
CREATE TABLE `t_users` (
`name` varchar(20) NOT NULL,
`age` int(5) DEFAULT NULL,
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
*/
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public int add(String name, Integer age) {
String sql = "INSERT INTO t_users(NAME, age) VALUES(?,?);";
int result = jdbcTemplate.update(sql, name, age);
System.out.println("插⼊成功");
return result;
}
}
5. 编写service
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wulei.dao.UserDao;
import ansaction.MyAnnotation;
@Service
public class UserService {
@Autowired
private UserDao userDao;
// 加上事务注解
@MyAnnotation
public void add() {
userDao.add("test001", 20);
int i = 1 / 0;
userDao.add("test002", 21);
//        // 如果⾮要try,那么出现异常不会被aop的异常通知监测到,必须⼿动在catch⾥⾯回滚事务。
//        try {
//            userDao.add("test001", 20);
/
/            int i = 1 / 0;
//            userDao.add("test002", 21);
//        } catch (Exception e) {
//            // 回滚当前事务
//            System.out.println("回滚");
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
//        }
}
public void del() {
System.out.println("");
}
}
6. 测试
import t.support.ClassPathXmlApplicationContext;
import com.wulei.service.UserService;
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("l");
UserService userService = (UserService) Bean("userService");
// aop()对userService类进⾏了拦截,添加⾃定义事务注解的⽅法会触发事务逻辑
userService.add();
// del()⽅法没有加注解,则什么也不会触发。
//userService.del();
}
}
@Transactional  ⾃调⽤事务失效分析
出处:
  在同⼀个类中,⼀个⽅法调⽤另外⼀个有注解(⽐如@Async,@Transational)的⽅法,注解是不会⽣效的。
  ⽐如,下⾯代码例⼦中,有两⽅法,⼀个有@Transational注解,⼀个没有。如果调⽤了有注解的addPerson()⽅法,会启动⼀个Transaction;如果调⽤updatePersonByPhoneNo(),因为它内部调⽤了有注解的addPerson(),如果你以为系统也会为它启动⼀个Transaction,那就错了,实际上是没有的。
@Service
public class PersonServiceImpl implements PersonService {
@Autowired
PersonDao personDao;
@Override
@Transactional

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。