@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 java.lang.reflect.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 = forName.getDeclaredMethods();
- //3. 遍历每个方法的信息
- for (Method method : declaredMethods) {
- System.out.println("方法名称:" + method.getName());
- //4. 获取方法上面的注解
- MyAnnotation annotation = method.getDeclaredAnnotation(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>
- <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
- <dependency>
- <groupId>com.mchange</groupId>
- <artifactId>c3p0</artifactId>
- <version>0.9.5.2</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>5.1.37</version>
- </dependency>
- </dependencies>
2. 配置spring.xml文件
- <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.wulei"/>
- <!-- 开启切面代理 -->
- <aop:aspectj-autoproxy />
- <!-- 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://localhost:3306/test"></property>
- <property name="user" value="root"></property>
- <property name="password" value="root"></property>
- </bean>
- <!-- 2. JdbcTemplate工具类实例 -->
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.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 org.springframework.context.annotation.Scope;
- import org.springframework.jdbc.datasource.DataSourceTransactionManager;
- import org.springframework.stereotype.Component;
- import org.springframework.transaction.TransactionStatus;
- import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
- @Component
- @Scope("prototype") // 申明为多例,解决线程安全问题。
- /**
- * 手写编程式事务
- */
- public class TransactionUtil {
- // 全局接受事务状态
- private TransactionStatus transactionStatus;
- // 获取事务源
- @Autowired
- private DataSourceTransactionManager dataSourceTransactionManager;
- // 开启事务
- public TransactionStatus begin() {
- System.out.println("开启事务");
- transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
- return transactionStatus;
- }
- // 提交事务
- public void commit(TransactionStatus transaction) {
- System.out.println("提交事务");
- if(dataSourceTransactionManager != null) dataSourceTransactionManager.commit(transaction);
- }
- // 回滚事务
- public void rollback(TransactionStatus transaction) {
- System.out.println("回滚事务...");
- if(dataSourceTransactionManager != null) dataSourceTransactionManager.rollback(transaction);
- }
- }
3.3 通过AOP封装事务工具类, 基于环绕通知和异常通知来触发事务。
- import java.lang.reflect.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.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 com.wulei.transaction.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 = this.getMethodMyAnnotation(pjp);
- // 2.判断是否需要开启事务
- transactionStatus = begin(annotation);
- // 3.调用目标代理对象方法
- pjp.proceed();
- // 4.判断关闭事务
- commit(transactionStatus);
- }
- /**
- * 获取代理方法上的事务注解
- * @param pjp 切入点
- */
- private MyAnnotation getMethodMyAnnotation(ProceedingJoinPoint pjp) throws Exception {
- //1. 获取代理对对象的方法
- String methodName = pjp.getSignature().getName();
- //2. 获取目标对象
- Class<?> classTarget = pjp.getTarget().getClass();
- //3. 获取目标对象类型
- Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
- //4. 获取目标对象方法
- Method objMethod = classTarget.getMethod(methodName, par);
- //5. 获取该方法上的事务注解
- MyAnnotation annotation = objMethod.getDeclaredAnnotation(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) transactionUtil.commit(transactionStatus);
- }
- /**
- * 异常通知进行 回滚事务
- */
- @AfterThrowing("execution(* com.wulei.service.*.*(..))")
- public void afterThrowing() {
- // 获取当前事务 直接回滚
- //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
- if(transactionStatus != null) transactionUtil.rollback(transactionStatus);
- }
- }
4. 编写dao层
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.jdbc.core.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 com.wulei.transaction.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("del...");
- }
- }
6. 测试
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import com.wulei.service.UserService;
- public class Main {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
- UserService userService = (UserService) applicationContext.getBean("userService");
- // aop()对userService类进行了拦截,添加自定义事务注解的方法会触发事务逻辑
- userService.add();
- // del()方法没有加注解,则什么也不会触发。
- //userService.del();
- }
- }
@Transactional实现原理的更多相关文章
- Java:Spring @Transactional工作原理
本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: propagation(事务传播)和isolation(隔离性)等属性的使用 事务使用 ...
- 25.Spring @Transactional工作原理
转自:http://www.importnew.com/12300.html 本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: prop ...
- 事务之五:Spring @Transactional工作原理
本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的. JPA(Java Persistence API--java持久层)和事务管理 很重要的一点是JPA本身 ...
- JPA数据懒加载LAZY配合事务@Transactional使用(三)
上篇博文<JPA数据懒加载LAZY和实时加载EAGER(二)>讲到,如果使用懒加载来调用关联数据,必须要保证主查询session(数据库连接会话)的生命周期没有结束,否则,你是无法抽取到数 ...
- 通俗的讲法理解spring的事务实现原理
拿房屋买卖举例,流程:销售房屋 -- 接待员 -- 销售员 -- 财务 售楼处 存放着所有待售和已售的房屋数据(数据源 datasource) 总经理 带领一套自己的班底,下属员工都听自己的,服务于售 ...
- Spring 事务注解@Transactional
事务管理一般有编程式和声明式两种,编程式是直接在代码中进行编写事物处理过程,而声名式则是通过注解方式或者是在xml文件中进行配置,相对编程式很方便. 而注解方式通过@Transactional 是常见 ...
- 玩转Spring--消失的事务@Transactional
消失的事务 端午节前,组内在讨论一个问题: 一个没有加@Transactional注解的方法,去调用一个加了@Transactional的方法,会不会产生事务? 文字苍白,还是用代码说话. 先写一个@ ...
- @Transactional(转)
概述@Transactional 是声明式事务管理 编程中使用的注解 添加位置 接口实现类或接口实现方法上,而不是接口类中访问权限:public 的方法才起作用 @Transactional 注解应该 ...
- @Transactional(事务讲解)和springboot 整合事务
概述 事务在编程中分为两种:声明式事务处理和编程式事务处理 编程式事务处理:编码方式实现事务管理,常与模版类TransactionTemplate(推荐使用) 在业务代码中实现事务. 可知编程式事务每 ...
随机推荐
- window.location.hash(hash应用)---跳转到hash值制定的具体页面
location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url,用location.href=url就可以直接将页面重定向url.而location. ...
- 模板_LCA
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define maxn 1000002 //#define int long long ...
- 2019 南京网络赛A
南京网络赛自闭现场 https://nanti.jisuanke.com/t/41298 二维偏序经典题型 二维前缀和!!! #include<bits/stdc++.h> using n ...
- 同一个tomcat部署多个项目
在开发项目中,有时候我们需要在同一个tomcat中部署多个项目,小编之前也是遇到了这样的情况,碰过不少的壁,故整理总结如下,以供大家参考.(以Linux为例,其他系统同样适用) 一.首先将需要部署的项 ...
- sock( ) bind( ) connect( )
Linux下的socket()函数 调用头文件<sys/socket.h>中的socket函数 int socket(int af, int type, int protocol); 1) ...
- memcached空指针内存错误与死循环问题分析(memcached dead loop and crash bug! issue #260 and issue #370)
(由于这是发在memcached邮件列表的,所以只能用一下蹩脚的英文了) (you should read the discuss about issue #260 first: https://g ...
- MAC截图工具
截图快捷键 ctrl+shift+A
- Vue知识整理12:事件绑定
采用v-on命令进行事件的绑定操作,通过单击按钮,实现按钮文字上数值的增加 带参数的事件过程 可以添加$event事件,实现事件信息的获取
- gzip, deflate delphi xe 2 解码 成功 哈哈
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 http://bbs.csdn.net/topics/190020986 ...
- nohup后台运行
1.信息输出 nohup java -jar xxxx.jar & 2.信息不输出 nohup java -jar xxxx.jar >/dev/null 2>&1 &am ...