您的位置:首页 > 编程语言

使用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();
}

}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: