AOP的实际案例
事务控制
在一个业务流程当中,可能需要多条DML语句共同完成,所以为了保证数据的安全需要添加事务控制的代码保证多条DML语句同时成功或失败
- 业务类中的每一个业务方法控制事务的代码格式基本是固定的,我们可以采用
AOP思想
将这些交叉业务代码
抽取出来,这样代码既能复用又易维护
class 业务类1{
public void 业务方法1(){
try{
// 开启事务
startTransaction();
// 执行核心业务逻辑
step1();
step2();
step3();
....
// 提交事务
commitTransaction();
}catch(Exception e){
// 回滚事务
rollbackTransaction();
}
}
public void 业务方法2(){
try{
// 开启事务
startTransaction();
// 执行核心业务逻辑
step1();
step2();
step3();
....
// 提交事务
commitTransaction();
}catch(Exception e){
// 回滚事务
rollbackTransaction();
}
}
}
class 业务类2{
public void 业务方法1(){
try{
// 开启事务
startTransaction();
// 执行核心业务逻辑
step1();
step2();
step3();
....
// 提交事务
commitTransaction();
}catch(Exception e){
// 回滚事务
rollbackTransaction();
}
}
}
//......
第一步: 定义两个业务类(目标类)AccountService和OrderService
以及业务方法(目标方法)
// 订单相关的业务类
@Service
public class OrderService {
// 生成订单方法
public void generate(){
System.out.println("正在生成订单");
}
// 取消订单方法
public void cancel(){
System.out.println("正在取消订单");
}
}
// 银行转账相关的业务类
@Service
public class AccountService {
// 转账业务方法
public void transfer(){
System.out.println("正在进行银行账户转账");
}
// 取款业务方法
public void withdraw(){
System.out.println("正在进行取款操作");
}
}
第二步: 定义一个事务相关的切面类TransactionAspect
,把控制事务的代码写到环绕通知
的方法中,根据切点表达式
将通知切入到指定业务类的业务方法当中
@Component
@Aspect
public class TransactionAspect { // 事务切面类
@Around("execution(* com.powernode.spring6.service..*(..))")// 环绕通知
public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
// 控制事务的代码
try {
// 前环绕
System.out.println("开启事务");
// 执行目标对象的目标方法
proceedingJoinPoint.proceed();
// 后环绕
System.out.println("提交事务");
} catch (Throwable e) {
System.out.println("回滚事务");
}
}
}
第三步: 在spring的配置文件
中开启组件扫描和自动代理
<?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: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.powernode.spring6.service"/>
<!--开启自动代理,spring容器在扫描类的时候会查看该类上是否有@Aspect注解,如果有则给这个切面类生成代理对象-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
第四步: 执行业务类的业务方法,查看环绕通知
中的事务控制代码的执行情况
public class AOPTest2 {
@Test
public void testTransaction(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
// 执行orderService生成订单的方法
orderService.generate();
// 执行orderService取消订单的方法
orderService.cancel();
// 执行accountService转账的方法
accountService.transfer();
// 执行accountService取款的方法
accountService.withdraw();
}
}
开启事务
正在生成订单
提交事务
开启事务
正在取消订单
提交事务
开启事务
正在进行银行账户转账
提交事务
开启事务
正在进行取款操作
提交事务
安全日志
需求: 因为增删改操作是属于危险行为, 所以项目上线后凡是在系统中进行增删改操作的用户我们都需要记录下来
第一步: 定义业务类(目标类)UserService
和ProductService
及其业务方法(目标方法)
// 用户业务类
@Service
public class UserService {
public void getUser(){
System.out.println("获取用户信息");
}
public void saveUser(){
System.out.println("保存用户");
}
public void deleteUser(){
System.out.println("删除用户");
}
public void modifyUser(){
System.out.println("修改用户");
}
}
// 商户业务类
@Service
public class ProductService {
public void getProduct(){
System.out.println("获取商品信息");
}
public void saveProduct(){
System.out.println("保存商品");
}
public void deleteProduct(){
System.out.println("删除商品");
}
public void modifyProduct(){
System.out.println("修改商品");
}
}
第二步: 定义一个负责安全的切面类SecurityAspect
,把记录安全日志的代码写到通知
的方法中,根据切点表达式
将通知切入到指定业务类的业务方法当中
@Component
@Aspect
public class SecurityAspect {
// 抽取可重用的切点表达式
@Pointcut("execution(* com.powernode.spring6.biz..save*(..))")
public void savePointcut(){}
@Pointcut("execution(* com.powernode.spring6.biz..delete*(..))")
public void deletePointcut(){}
@Pointcut("execution(* com.powernode.spring6.biz..modify*(..))")
public void modifyPointcut(){}
// 使用抽取好的切点表达式,指定通知只作用于增删改的方法
@Before("savePointcut() || deletePointcut() || modifyPointcut()")
public void beforeAdivce(JoinPoint joinpoint){
System.out.println("XXX操作员正在操作"+joinpoint.getSignature().getName()+"方法");
}
}
第三步: 在spring的配置文件
中开启组件扫描和自动代理
<?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: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">
<!--开启组件扫描,biz也是业务层-->
<context:component-scan base-package="com.powernode.spring6.biz"/>
<!--开启自动代理,spring容器在扫描类的时候会查看该类上是否有@Aspect注解,如果有则给这个切面类生成代理对象-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
第四步: 执行业务类的业务方法,查看前置通知
中记录安全日志的代码的执行情况
@Test
public void testSecurity(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
UserService userService = applicationContext.getBean("userService", UserService.class);
ProductService productService = applicationContext.getBean("productService", ProductService.class);
// 调用userService对象的增删改查方法
userService.getUser();
userService.saveUser();
userService.deleteUser();
userService.modifyUser();
// 调用productService对象的增删改查方法
productService.getProduct();
productService.saveProduct();
productService.deleteProduct();
productService.modifyProduct();
}
获取用户信息
XXX操作员正在操作saveUser方法
保存用户
XXX操作员正在操作deleteUser方法
删除用户
XXX操作员正在操作modifyUser方法
修改用户
获取商品信息
XXX操作员正在操作saveProduct方法
保存商品
XXX操作员正在操作deLeteProduct方法
删除商品
XXX操作员正在操作modifyProduct方法
修改商品