前提:导入AOP的Maven坐标,无需指定版本
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1、创建自定义注解
@Target(ElementType.METHOD)
:- 指定了这个注解只能用来标注方法,不能用于类、字段等其他元素。
@Retention(RetentionPolicy.RUNTIME)
:- 表示这个注解在运行时保留,可以通过反射机制读取,允许在程序运行时通过反射获取注解信息。
type()
:- 该注解的唯一元素,类型为String默认为空字符串,可以用于区分被注解方法的类型,以便执行不同的处理逻辑。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
/**
* 用于区分被注解方法的类型,来执行不同逻辑
* @return 切入方法类型
*/
String type() default "";
}
2、编写AOP切面类
@Component
:- 将该类标记为Spring组件,由Spring容器管理,使其能够被自动扫描并注册为bean。
@Aspect
:- 声明这是一个切面类,Spring AOP会识别这个注解并处理其中定义的切点。
@Slf4j
:- Lombok注解,自动生成日志记录器(Logger),可以使用
log
变量记录日志。
- Lombok注解,自动生成日志记录器(Logger),可以使用
@Before
:- 定义前置通知,在目标方法执行前运行,使用切入点表达式
@annotation(...)
指定只拦截带有特定注解的方法。 - 这里使用的是@Before前置通知,除了该注解还有@After、@Around、@AfterThrowing、@AfterReturning注解可根据需要进行调整。
- 定义前置通知,在目标方法执行前运行,使用切入点表达式
通知类型 | 适用场景 |
| 参数校验、权限检查、日志记录 |
| 结果处理、成功日志记录 |
| 异常处理、错误日志 |
| 资源清理、审计日志 |
| 事务管理、缓存、性能监控 |
JoinPoint
参数:- 提供对连接点(被拦截方法)的访问,可以获取方法签名、参数、目标对象等信息。
@Component
@Aspect
@Slf4j
public class CustomAop {
@Before("@annotation(自定义注解相对路径.CustomAnnotation)")
public void before(JoinPoint joinPoint) throws NoSuchMethodException {
// 获取切入点的签名信息(org.aspectj.lang.reflect.MethodSignature)
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 获取方法名称(如果此时使用这个对象获取注解,会返回null,因为代理对象的方法上不会有注解)
Method method = methodSignature.getMethod();
// 获取目标类(反射)
Class<?> targetClass = joinPoint.getTarget().getClass();
// 反射从目标类中获取方法
Method targetMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
CustomAnnotation annotation = targetMethod.getAnnotation(CustomAnnotation.class);
// 根据切入点不同来执行不同逻辑
switch (annotation.type()) {
case "1":
方法1(需要的参数);
break;
case "2":
方法2(需要的参数);
break;
case "3":
方法3(需要的参数);
break;
default:
break;
}
}
/**
* 实现方法1的逻辑
* @param 需要的参数
*/
private void 方法1(需要的参数) {
// ... 具体逻辑 ...
}
/**
* 实现方法2的逻辑
* @param 需要的参数
*/
private void 方法2(需要的参数) {
// ... 具体逻辑 ...
}
/**
* 实现方法3的逻辑
* @param 需要的参数
*/
private void 方法3(需要的参数) {
// ... 具体逻辑 ...
}
}
3、实现Demo
示例:在保存接口通过前置通知注解切入进行权限校验
/**
* 自定义注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
/**
* 用于区分被注解方法的类型,来执行不同逻辑
* @return 切入方法类型
*/
String type() default "";
}
/**
* AOP切面类
*/
@Component
@Aspect
@Slf4j
public class CustomAop {
@Before("@annotation(自定义注解相对路径.CustomAnnotation)")
public void before(JoinPoint joinPoint) throws NoSuchMethodException {
// 获取切入点的签名信息
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 获取方法名称(如果此时使用这个对象获取注解,会返回null,因为代理对象的方法上不会有注解)
Method method = methodSignature.getMethod();
// 获取目标类(反射)
Class<?> targetClass = joinPoint.getTarget().getClass();
// 反射从目标类中获取方法
Method targetMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
CustomAnnotation annotation = targetMethod.getAnnotation(CustomAnnotation.class);
// 根据切入点不同来执行不同逻辑
switch (annotation.type()) {
case "save":
PermissionVerification();
break;
default:
break;
}
}
/**
* 实现权限校验逻辑
*/
private void PermissionVerification() {
// ... 权限校验逻辑 ...
}
}
@RestController
@RequestMapping("/demo")
@Slf4j
public class DemoController {
@Autowired
private DemoService demoServiceImpl;
@PostMapping("/save")
@CustomAnnotation(type = "save")
public void saveBatchJob() {
demoServiceImpl.saveBatchJob();
}
}
文章内容若存在错误或需改进的地方,欢迎大家指正!非常感谢!