pom文件依赖
<!-- AOP切面编程启动环境依赖组 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1、通过execution表达式实现切入增强
package com.xch.aop_execution.advicetype;
import org.springframework.stereotype.Component;
/**
* 计算业务实现类
*
* @author XuChenghe
* @date 2023/8/19 17:43
*/
@Component
public class Count {
/**
* 正常计算方法
*
* @param num1
* @param num2
* @return
*/
public Integer normal(Integer num1, Integer num2) {
System.out.println("-----执行正常计算方法的逻辑(开始)-----");
int x = 1 / 1;
System.out.println("-----执行正常计算方法的逻辑(结束)-----");
return num1 + num2;
}
/**
* 异常计算方法
*
* @param num1
* @param num2
* @return
*/
public Integer abnormal(Integer num1, Integer num2) {
System.out.println("-----执行异常计算方法的逻辑(开始)-----");
int x = 1 / 0;
System.out.println("-----执行异常计算方法的逻辑(结束)-----");
return num1 - num2;
}
}
package com.xch.aop_execution.advicetype;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* 计算业务的切面类:测试通知类型
* -其中,后置通知/返回通知 和 最终通知的执行顺序,会根据Spring的版本而异
*
* @author XuChenghe
* @date 2023/8/19 17:41
*/
@Aspect // 声明该类的作用:切面类
@Component // 等价于@Configuration:交由Spring的IOC容器管理
public class CountAspect {
/**
* 定义公共切入点
* execution表达式配置规则:
* -execution([修饰符] 返回类型 方法全限定名称(参数) [异常])
* -其中修饰符和异常可选
* -参数..代表任意入参
* -*代表任意返回类型或任意方法名
* -*xxx/xxx*代表以xxx结尾或开头的任意方法名
* -xxx.*代表xxx类下的所有方法或xxx包下所有类的所有方法
* -xxx..*代表xxx包下所有包或类的所有方法
*/
@Pointcut("execution(* com.xch.aop_execution.advicetype.Count.*(..))")
public void myPointcut() {
// 公共切入点
}
/**
* 前置通知
*
* @param joinPoint 连接点:当前切入方法的信息
*/
@Before("myPointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("=====前置通知(开始):执行安全检查逻辑=====");
// 前置通知,可以拿到方法名称+方法入参
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("name = " + name);
System.out.println("args = " + Arrays.toString(args));
System.out.println("=====前置通知(结束):执行安全检查逻辑=====");
}
/**
* 后置通知/返回通知
* 只有目标方法中没有抛出异常,即正常执行返回结果后,才进入该通知
*
* @param joinPoint 连接点:当前切入方法的信息
* @param result 方法执行的返回结果
*/
@AfterReturning(pointcut = "myPointcut()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("=====后置通知/返回通知(开始):记录日志=====");
// 后置通知/返回通知,可以拿到方法名称+方法入参+方法返回值
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("name = " + name);
System.out.println("args = " + Arrays.toString(args));
System.out.println("result = " + result);
System.out.println("=====后置通知/返回通知(结束):记录日志=====");
}
/**
* 最终通知
* 不管目标方法中有没有抛出异常,都会进入该通知
*
* @param joinPoint 连接点:当前切入方法的信息
*/
@After("myPointcut()")
public void after(JoinPoint joinPoint) {
System.out.println("=====最终通知(开始):释放资源=====");
// 最终通知,可以拿到方法名称+方法入参
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("name = " + name);
System.out.println("args = " + Arrays.toString(args));
System.out.println("=====最终通知(结束):释放资源=====");
}
/**
* 环绕通知
* 该通知等价于其余四种通知的大集合
* 执行了被切入的方法代码,改变了原执行路径,需返回执行结果
*
* @param pjp 连接点:当前切入方法的信息
* @return 方法执行的返回结果
* @throws Throwable 方法执行时抛出的异常
*/
@Around("myPointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("=====环绕通知(开始):手动开启事务=====");
// 环绕通知,可以拿到方法名称+方法入参+方法返回值
String name = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
Object proceed = pjp.proceed();
System.out.println("name = " + name);
System.out.println("args = " + Arrays.toString(args));
System.out.println("proceed = " + proceed);
System.out.println("=====环绕通知(结束):手动提交事务=====");
return proceed;
}
/**
* 抛出异常后通知
* 不能在Around环绕通知中捕获异常,否则不会给Spring AOP处理进入该通知
*
* @param joinPoint 连接点:当前切入方法的信息
* @param exception 方法执行时抛出的异常
*/
@AfterThrowing(pointcut = "myPointcut()", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
System.out.println("=====抛出异常后通知(开始):手动回滚事务=====");
// 抛出异常后通知,可以拿到方法名称+方法入参+抛出的异常
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("name = " + name);
System.out.println("args = " + Arrays.toString(args));
exception.printStackTrace();
System.out.println("=====抛出异常后通知(结束):手动回滚事务=====");
}
}
package com.xch.aop_execution.advicetype;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 计算业务测试类
*
* @author XuChenghe
* @date 2023/8/19 18:17
*/
@SpringBootTest
public class CountTest {
@Autowired
private Count count;
@Test
public void test() {
count.normal(3, 8);
System.out.println();
count.abnormal(8, 3);
}
}
2、通过annotation注解实现切入增强
package com.xch.aop_annotation.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户信息实体类
*
* @author XuChenghe
* @date 2023/8/19 12:37
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
/**
* 用户ID
*/
private Integer id;
/**
* 用户名称
*/
private String name;
}
package com.xch.aop_annotation.controller;
import com.xch.aop_annotation.annotation.LogAnnotation;
import com.xch.aop_annotation.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
/**
* 用户信息管理接口层实现类
*
* @author XuChenghe
* @date 2023/8/19 12:35
*/
@RestController
@RequestMapping("/user")
public class UserController {
/**
* 获取所有用户信息
*
* @return
*/
@GetMapping("/getUserList")
public List<User> getUserList() {
// 模拟业务的数据库操作
return Arrays.asList(
new User(1, "Alice"),
new User(2, "Bob"),
new User(3, "Lucy")
);
}
/**
* 通过用户ID获取用户信息
*
* @param id
* @return
*/
@GetMapping("/getUserById/{id}")
public User getUserById(@PathVariable("id") Integer id) {
// 模拟业务的数据库操作
return new User(1, "Alice");
}
/**
* 通过用户名称获取用户信息
*
* @param name
* @return
*/
@GetMapping("/getUserByName/{name}")
public User getUserByName(@PathVariable("name") String name) {
// 模拟业务的数据库操作
return new User(2, "Bob");
}
/**
* 保存用户信息
*
* @return
*/
@GetMapping("/saveUser")
@LogAnnotation("保存用户信息")
public String saveUser() {
// 模拟业务的数据库操作
return "添加用户信息成功!";
}
/**
* 编辑用户信息
*
* @return
*/
@GetMapping("/editUser")
@LogAnnotation("编辑用户信息")
public String editUser() {
// 模拟业务的数据库操作
return "编辑用户信息成功!";
}
/**
* 通过用户ID删除用户信息
*
* @param id
* @return
*/
@GetMapping("/deleteUserById/{id}")
@LogAnnotation("通过用户ID删除用户信息")
public String deleteUserById(@PathVariable("id") Integer id) {
// 模拟业务的数据库操作
return "通过用户ID删除用户信息成功!";
}
/**
* 通过用户名称删除用户信息
*
* @param name
* @return
*/
@GetMapping("/deleteUserByName/{name}")
@LogAnnotation("通过用户名称删除用户信息")
public String deleteUserByName(@PathVariable("name") String name) {
// 模拟业务的数据库操作
return "通过用户名称删除用户信息成功!";
}
}
package com.xch.aop_annotation.annotation;
import java.lang.annotation.*;
/**
* 记录写操作方法日志的注解
*
* @author XuChenghe
* @date 2023/8/19 12:57
*/
@Target(ElementType.METHOD) // 声明该注解作用的地方:方法
@Retention(RetentionPolicy.RUNTIME) // 声明该注解作用的时间:运行时
@Documented // 以上三个都是元注解:描述注解的注解
public @interface LogAnnotation {
/**
* 方法的作用描述
*/
String value() default "暂无作用描述(默认)";
}
package com.xch.aop_annotation.aspect;
import com.xch.aop_annotation.annotation.LogAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 记录写操作方法日志的的切面类
*
* @author XuChenghe
* @date 2023/8/19 13:06
*/
@Aspect // 声明该类的作用:切面类
@Component // 等价于@Configuration:交由Spring的IOC容器管理
public class LogAspect {
/**
* 定义公共切入点
*/
@Pointcut("@annotation(com.xch.aop_annotation.annotation.LogAnnotation)")
public void logPointCut() {
// 公共切入点
}
/**
* 切面的执行方法(环绕通知):记录日志操作的代码
*
* @param pjp 连接点:当前切入方法的信息
* @return 方法执行的返回结果
*/
@Around("logPointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 模拟业务的记录日志到数据库操作
// 当前切入的方法名称
String name = pjp.getSignature().getName();
// 当前切入的方法返回值(执行了被切入的方法代码,改变了原执行路径,需返回执行结果)
Object proceed = pjp.proceed();
// 当前切入的方法入参
Object[] args = pjp.getArgs();
// 获取注解的属性内容
// 1.通过反射反向获取方法
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
// 2.通过反射反向获取注解
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
if (logAnnotation != null) {
String value = logAnnotation.value();
System.out.println("========================================");
System.out.println("name = " + name);
System.out.println("proceed = " + proceed);
System.out.println("args = " + Arrays.toString(args));
System.out.println("value = " + value);
System.out.println("========================================");
}
return proceed;
}
}