使用Aop切面编程需要先了解涉及到的关键词:
@Aspect:定义切面类,切面类需要加上@Aspect、@Component注解
@Pointcut:告诉切面类,通知方法在什么时候运行的 表达式,他能锁定出一个或者一种方法,或者说锁定了一个位置出来。
@Around:环绕增强通知,会执行两次,一次在目标方法执行之前第二次在执行之后,Around功能比较强大,既可以在目标方法执行又会在目标方法执行之后执行,也可以修改目标方法参数以及修改目标方法返回值
@AfterReturning:后置增强通知,目标方法返回后调用
@Before:前置增强通知,目标方法执行之前执行
@AfterThrowing:异常抛出增强通知
@After:目标方法正常返回和抛出异常时都会执行
通知执行顺序:@Around-->Before方法-->method方法-->@AfterReturning方法-->@After-->@Around
- pom引入相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 定义注解
package com.stars.demo.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface ProcessHandle {
}
- 定义切面类
其中 @Pointcut()切点分为execution方式和annotation方式。execution可以用路径表达式指定哪些类织入切面,annotation可以指定被哪些注解修饰的代码织入切面。这里使用的是第二种方式。定义切面类注意需要添加@Aspect、@Component注解
package com.stars.demo.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ProcessHandleContract {
//切面连接点放到注解处
@Pointcut("@annotation(com.stars.demo.annotation.ProcessHandle)")
private void processHandleCut() {
}
//spring aop切面方法主要有:Around、Before、After、AfterReturning、AfterThrowing
//执行顺序:@Around-->Before方法-->method方法-->@AfterReturning方法-->@After-->@Around
@Around("processHandleCut()")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
System.out.println("@Around:进入方法之前...");
//获取目标方法的入参,可以对入参进行修改
Object[] args = point.getArgs();
Object proceed = point.proceed(args);
System.out.println("@Around:执行方法之后...");
return proceed;
}
@Before("processHandleCut()")
public void doBefore(JoinPoint joinPoint){
System.out.println("--------Before-----------");
}
@After("processHandleCut()")
public void doAfter(JoinPoint joinPoint) {
System.out.println("--------After-----------");
}
@AfterReturning("processHandleCut()")
public void doAfterReturning(JoinPoint joinPoint) {
System.out.println("--------AfterReturning-----------");
}
@AfterThrowing("processHandleCut()")
public void doAfterThrowing(JoinPoint joinPoint) {
System.out.println("--------AfterThrowing-----------");
}
}
- 使用切面
@GetMapping("/queryAll")
@ProcessHandle
public List<UserInfo> queryAll(){
System.out.println("开始执行Method");
List<UserInfo> userInfos = userInfoService.queryAll();
return userInfos;
}
请求执行queryAll接口,打印结果: 从打印结果能看到各通知执行顺序
- 补充几种切面不生效的常见原因
- 切面类需要添加@Aspect、@Component注解
- @Pointcut()切点设置有问题
- 目标类需要由Spring托管,不能是new()出来的也不能是类内部调用
- 切入的目标方法需要是public修饰,static、final修饰的方法都不能切入