pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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.powernode</groupId>
<artifactId>course27</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!--仓库-->
<repositories>
<!--spring里程碑版本的仓库-->
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<!--依赖-->
<dependencies>
<!--spring context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--spring aspects-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
1. 事务处理 116
1.1 概述 116
项目中的事务控制是在所难免的。在一个业务流程当中,可能需要多条DML语句共同完成,为了保证数据的安全,这多条DML语句要么同时成功,要么同时失败。这就需要添加事务控制的代码。例如以下伪代码:
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();
}
}
public void 业务方法3(){
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();
}
}
public void 业务方法2(){
try{
// 开启事务
startTransaction();
// 执行核心业务逻辑
step1();
step2();
step3();
....
// 提交事务
commitTransaction();
}catch(Exception e){
// 回滚事务
rollbackTransaction();
}
}
public void 业务方法3(){
try{
// 开启事务
startTransaction();
// 执行核心业务逻辑
step1();
step2();
step3();
....
// 提交事务
commitTransaction();
}catch(Exception e){
// 回滚事务
rollbackTransaction();
}
}
}
//......
可以看到,这些业务类中的每一个业务方法都是需要控制事务的,而控制事务的代码又是固定的格式,都是:
try{
// 开启事务
startTransaction();
// 执行核心业务逻辑
//......
// 提交事务
commitTransaction();
}catch(Exception e){
// 回滚事务
rollbackTransaction();
}
1.2 案例 116
这个控制事务的代码就是和业务逻辑没有关系的“交叉业务”。以上伪代码当中可以看到这些交叉业务的代码没有得到复用,并且如果这些交叉业务代码需要修改,那必然需要修改多处,难维护,怎么解决?可以采用AOP思想解决。可以把以上控制事务的代码作为环绕通知,切入到目标类的方法当中。接下来我们做一下这件事,有两个业务类,如下:
银行账户的业务类
package com.powernode.spring6.service;
import org.springframework.stereotype.Service;
/**
* AOP的实际案例 事务处理116
**/
@Service
public class AccountService { // 目标对象
// 目标方法
// 转账的业务方法
public void transfer(){
System.out.println("银行账户正在完成转账操作...");
}
// 目标方法
// 取款的业务方法
public void withdraw(){
System.out.println("正在取款,请稍后...");
}
}
订单业务类
package com.powernode.spring6.service;
import org.springframework.stereotype.Service;
/**
* AOP的实际案例 事务处理116
**/
@Service
public class OrderService { // 目标对象
// 目标方法
// 生成订单的业务方法
public void generate(){
System.out.println("正在生成订单...");
}
// 目标方法
// 取消订单的业务方法
public void cancel(){
System.out.println("订单已取消...");
String s = null;
s.toString();
}
}
注意,以上两个业务类已经纳入spring bean的管理,因为都添加了@Component注解。
接下来我们给以上两个业务类的4个方法添加事务控制代码,使用AOP来完成:
package com.powernode.spring6.service;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
* 事务切面 116
**/
@Component
@Aspect
public class TransactionAspect {
// 编程式事务解决方案。
@Around("execution(* com.powernode.spring6.service..*(..))")
public void aroundAdvice(ProceedingJoinPoint joinPoint){
try {
// 前环绕
System.out.println("开启事务");
// 执行目标
joinPoint.proceed();
// 后环绕
System.out.println("提交事务");
} catch (Throwable e) {
System.out.println("回滚事务");
}
}
}
<?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"/>
<!--启动自动代理-->
<aop:aspectj-autoproxy/>
</beans>
package com.powernode.spring6.test;
import com.powernode.spring6.service.AccountService;
import com.powernode.spring6.service.OrderService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//事务测试 116
public class AOPRealAppTest {
@Test
public void testTransaction(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
accountService.transfer();
accountService.withdraw();
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
orderService.generate();
orderService.cancel();
}
}
2. 安全日志 117
2.1 案例 117
需求是这样的:项目开发结束了,已经上线了。运行正常。客户提出了新的需求:凡事在系统中进行修改操作的,删除操作的,新增操作的,都要把这个人记录下来。因为这几个操作是属于危险行为。例如有业务类和业务方法:
业务类
package com.powernode.spring6.biz;
import org.springframework.stereotype.Service;
/**
* AOP实际案例 安全日志 117
**/
@Service
public class VipService {
public void saveVip(){
System.out.println("新增会员信息");
}
public void deleteVip(){
System.out.println("删除会员信息");
}
public void modifyVip(){
System.out.println("修改会员信息");
}
public void getVip(){
System.out.println("获取会员信息");
}
}
业务类
package com.powernode.spring6.biz;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
/**
* AOP实际案例 安全日志 117
**/
@Service
public class UserService {
public void saveUser(){
System.out.println("新增用户信息");
}
public void deleteUser(){
System.out.println("删除用户信息");
}
public void modifyUser(){
System.out.println("修改用户信息");
}
public void getUser(){
System.out.println("获取用户信息");
}
}
注意:已经添加了@Component注解。
接下来我们使用aop来解决上面的需求:编写一个负责安全的切面类
日志切面
package com.powernode.spring6.biz;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 日志切面
* AOP实际案例 安全日志 117
**/
@Component
@Aspect
public class SecurityLogAspect {
@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()")
// @Before("execution(* com.powernode.spring6.biz..*(..))")
public void beforeAdvice(JoinPoint joinPoint){
// 系统时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String nowTime = sdf.format(new Date());
// 输出日志信息
System.out.println(nowTime + " zhangsan : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
}
}
<?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"/>
<!--启动自动代理-->
<aop:aspectj-autoproxy/>
</beans>
//日志测试 117
@Test
public void testSecurityLog(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
VipService vipService = applicationContext.getBean("vipService", VipService.class);
userService.saveUser();
userService.deleteUser();
userService.modifyUser();
userService.getUser();
vipService.saveVip();
vipService.deleteVip();
vipService.modifyVip();
vipService.getVip();
}