使用aop技术整合编程事务
2019-05-24 17:24
1186 查看
[code]<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.learn</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/javassist/javassist --> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.12.1.GA</version> </dependency> <!-- 引入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> </project>
[code]<?xml version="1.0" encoding="UTF-8"?> <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" 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"> <!-- 这里表示扫包范围 因为我们是使用注解的, --> <context:component-scan base-package="com.learn"></context:component-scan> <!-- 这里表示开启事务的注解 你如果想要事务的话,你必须开启一个事务注解, --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启事物注解 --> <!-- 1. 数据源对象: C3P0连接池 --> <!-- 第一步我们加载C3P0数据源 DBCP和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/day20"></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> <!-- 3.配置事务 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
[code]package com.learn.service; public interface UserService { public void add(); }
[code]package com.learn.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.interceptor.TransactionAspectSupport; import com.learn.dao.UserDao; import com.learn.service.UserService; import com.learn.transaction.TransactionUtils; /** * 这就是编程式事务 * * @author Leon.Sun * */ @Service public class UserServiceImpl implements UserService { @Autowired private TransactionUtils transactionUtils; @Autowired private UserDao userDao; /** * 你们觉得这样麻烦不麻烦 * 如果我在来一个方法是不是重复 * 你还要去做begin 提交 回滚 * 是不是这样的 * 那么我们怎么做 * 使用AOP技术 * 把它封装起来 * 正式进入我们的正题了 * 你一定要重构 * 怎么重构呢 * 我们写一个AopTransaction * * */ // public void add() { // TransactionStatus transactionStatus = null; // try { // transactionStatus = transactionUtils.begin(); // userDao.add("test001", 20); // System.out.println("开始报错了.........................."); //// int i = 1/0; // System.out.println("####################################"); // userDao.add("test002", 21); // if(transactionStatus!=null) { // transactionUtils.commit(transactionStatus); // } // } catch (Exception e) { // e.printStackTrace(); // if(transactionStatus!=null) { // transactionUtils.rollback(transactionStatus); // } // } // } /** * 只要我执行到userDao.add("test001", 20); * 没有提交到数据库里面就是成功的 * 是不是这样的 * 所以我打个断点只要我执行到userDao.add("test001", 20);这行的时候 * 只要他没有到数据库里面去 * 说明他肯定是事务是生效的 * 一进入方法的时候 * 他是不是先走环绕通知 * * * */ public void add() { /** * 如果我们try catch了 * 你们说这个是提交事务的还是回滚的 * 是提交事务呢还是回滚事务 * 这肯定提交了 * 为什么呢 * 因为你没有把异常抛给AOP * 因为他不知道你有 异常通知 * 我就不会回滚了 * 因为异常已经被当前自己的方法消耗掉了 * 开启事务 * 提交事务 * 然后我们查询一遍 * 是不是有了test001了 * 看到没有 * 我们是不是有了test001了 * 是不是有了 * 发现这个时候test001是有了 * 但是test002是没有的 * 因为test002报错了 * 报了一个不能除以0 * 这是我要讲的 * 以后你们用事务千万不要try * 用封装事务千万不要try * 一try肯定会出问题的 * 这个是要和你们讲一下的 * 你看我再重来一遍 * 开启事务 * 然后提交事务 * 这是你们在公司里面经常犯的错误 * * 说一个注意事项 * 在使用Spring的事务的时候 * 一般事务都使用在service层 * 不要try * 将异常抛出给外层aop异常通知接收回滚 * 这个怎么解决 * 如果你真想try * 那你就手动去跑异常回滚也可以 * 你如果真要try的话 * 反正就是尽量不要去try * 最好是将异常抛出给外层aop回滚 * 然后怎么做呢 * TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); * 或者自己直接这样写 * 通过这样子也可以 * 但是 你要知道 * 每次都这样写的话代码是非常冗余的 * 你要记住 * 是非常冗余的 * 那肯定不好 * 你们想想是不是这样的 * 所以你看再运行一遍 * 大体就把事务讲完了 * 声明式事务原理就是这么来的 * 就这点代码 * 没有特别难 * * * */ try { userDao.add("test001", 20); /** * 走到这里数据库是绝对没有数据的 * */ System.out.println("开始报错了.........................."); /** * 异常怎么处理呢 * * * 这里抛异常你们看一下效果 * */ int i = 1/0; System.out.println("####################################"); /** * 当这个走完的时候 * 他就会走到around环绕通知方法里来 * * 有人说不对啊 * 如果异常回滚怎么集成 * * */ userDao.add("test002", 21); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); /** * 手动回滚是没有的 * * 只要异常抛出AOP能够接收的到 * 异常能够接收到的情况下 * 都会回滚 * * */ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } }
[code]package com.learn.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.DefaultTran 3ff7 sactionAttribute; /** * 我们今天讲手写事务 * 我们要他的注解来实现事务 * 注解其实都是封装起来的 * 通过编程事务进行封装的 * 编程事务(需要手动begin 手动回滚 手都提交) * 在这里我们要封装几个方法 * 你们只要拿到ORM框架的接口就行了 * 我们的数据源接口是哪一个 * 就是dataSourceTransactionManager这个 * 我们要拿到事务数据源 * 然后进行控制事务 * * 我们要把注入到Spring的容器里面去 * 你们最好不要用单例的 * 否则会产生线程安全问题的 * * TransactionUtils这个类最好不要实现为单例的 * 这是为什么 * 如果是单例的话 * 可能会发生线程安全的问题 * 你们设置多例的可以 * 访问线程安全问题 * 不然会有线程安全问题 * 如果你懂线程安全肯定会懂这个问题的 * 如果单例的情况下 * 并发的情况下 * 导致被别人提交了 * 怎么设置成多例 * 原型你们知道吗 * 设置属性是不是单例的 * 把Spring的事务给你讲完了 * 自己定义一个注解 * * * * @author Leon.Sun * */ @Component public class TransactionUtils { /** * 获取事务源 * * 这里要注入一下@Autowired * */ @Autowired private DataSourceTransactionManager dataSourceTransactionManager; /** * 这个方法干嘛用的 * 是不是开启事务 * * * @return */ public TransactionStatus begin() { /** * 我们调用一下dataSourceTransactionManager * 我们先使用默认的传播级别DefaultTransactionAttribute * 没有学到传播级别 * 不知道他里面怎么去用 * 拿到了事务的一个状态 * 记得要把事务的状态返回回去 * 这里是开启一个事务 * 我要提交事务怎么提交呢 */ TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute()); return transaction; } /** * 这叫做提交事务 * * 把状态传入进来 * * * @param transaction */ public void commit(TransactionStatus transaction) { /** * 提价一个事务状态 * */ dataSourceTransactionManager.commit(transaction); } /** * 我们再写一个回滚事务 * @param transaction */ public void rollback(TransactionStatus transaction) { dataSourceTransactionManager.rollback(transaction); } }
[code]package com.learn.aop; 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; import com.learn.transaction.TransactionUtils; /** * 切面类 基于手手动事务封装 * 记住它是一个封装的事务 * 应该是使用环绕通知好呢 * 还是用后置通知好呢 * 最好是使用环绕通知好 * 因为他是拦截 * 在这里我们只需要定义一个方法就可以了 * * 这里其实就是类似于声明式事务 * 只是我都放到代码里面去了 * 我怎么验证搭建AOP事务是成功的呢 * * 解释一下注解里面是怎么封装事务的 * * * @author Leon.Sun * */ @Component @Aspect public class AopTransaction { /** * 我们把自己封装的类注入进来 * 注入进来了之后 * 写一个@Autowired * */ @Autowired private TransactionUtils transactionUtils; // TransactionUtils 不要实现为单例子: 如果为单例子的话可能会发生线程安全问题 // // 异常通知 /** * 如果你长期不提交会产生死锁的 * 会产生死锁现象的 * 是不能捕获异常的 * 我来问一下 * 如果我们这个时候不加异常通知 * public void around(ProceedingJoinPoint proceedingJoinPoint) * 这段代码这么写对不对 * 肯定不对的 * 他不会走提交 * 如果他不会走提交的话 * 那他就永远占着内存 * 他又不提交 * 这样就会长期的产生死锁对象 * 你们最好通过异常通知 * 去接收这样的一个通知 * 一旦方法抛异常的情况下 * 就做回滚 * * * @AfterThrowing这里是Spring底层的框架的默认封装好的 * 这段代码默认就封装好了 * 默认封装好的情况下你try的话 * 你想自己回滚的情况也可以 * 你如果不try把异常抛给我我会回滚的 * 因为首先框架很多是比较底层的 * 接下来会讲到很多 * 并发编程其实不难 * 之前都讲过的 * * */ @AfterThrowing("execution(* com.learn.service.UserService.add(..))") public void afterThrowing() { /** * 回滚事务 * */ System.out.println("回滚事务"); /** * 获取当前事务 直接回滚 * 这里不好写 * transactionStatus这个参数怎么传入进来呢 * 他又在两个不同的方法 * 怎么解决这个问题 * 不要用全局变量 * 用一个方法 * 获取当前的事务 * 怎么获取当前的事务 * 在这边给你讲一下 * TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() * 调用这个方法 * 这个方法干嘛用的 * 这个方法表示什么意思呢 * 直接回滚的 * 就是直接可以进入回滚的 * 那你们可以在这边看一下效果 * 你们不要去定义为全局变量 * 就回滚 * 直接回滚 * 获取当前事务就只解决回滚 * 这边讲一下该怎么做 * * */ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } // 环绕通知 在方法之前和之后处理事情 /** * 这个方法我们在这里怎么做呢 * * * @param proceedingJoinPoint * @throws Throwable */ @Around("execution(* com.learn.service.UserService.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { /** * 这里表示开启事务 */ System.out.println("开启事务"); /** * 这里begin * 把这个状态transactionStatus传进去 * */ TransactionStatus transactionStatus = transactionUtils.begin(); /** * 然后他会调我们实例的方法 * */ proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出溢出不会执行后面代码 /** * 这里提交事务 */ System.out.println("提交事务"); /** * 如果没有抛异常的情况下就commit * 然后把transactionStatus这个放进去 * 这里是AOP提交事务 * 提交你再查一下是不是就有了 * */ transactionUtils.commit(transactionStatus); } }
[code]package com.learn.test; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.learn.service.UserService; public class Test001 { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) applicationContext.getBean("userServiceImpl"); userService.add(); } }
相关文章推荐
- ASP.NET MVC3 中整合 NHibernate3.3、Spring.NET2.0 使用AOP执行事务处理
- SpringAOP整合Hibernate并使用事务(模拟买书的过程)
- ASP.NET MVC3 中整合 NHibernate3.3、Spring.NET2.0 使用AOP执行事务处理
- sniffer技术原理及应用,包括编程方法和工具使用
- 在VC中使用Windows管道技术编程
- 使用Spring进行切面(AOP)编程
- 使用Spring AOP 自动代理配置声明式事务
- 在VC中使用Windows管道技术编程
- (8) 使用JDK中的Proxy技术实现AOP功能 以及 CGLIB生成代理
- spring学习笔记8--使用spring进行面向切面的(AOP)编程(2)XML配置方式
- 在web开发中的三个层次使用事务(C#编程)
- 使用JDK中的Proxy技术实现AOP功能[动态代理]
- Spring AOP:使用NameMatchMethodPointcutAdvisor实现切面编程
- 【Linux开发技术之工具使用】配置VIM下编程和代码阅读环境
- Spring整合hibernate的事务属性介绍以及声明式事务管理 (使用Annotation和xml)
- 使用aop配置事务-实际应用
- TSX之有限事务内存,TSX之锁消除技术 和 lock free 编程之比较(有效总结)
- 使用 COM+ 参数化对象结构编程技术
- Linux多播技术的使用和编程
- 使用 COM+ 参数化对象结构编程技术