1.AOP使用场景
- 日志记录
- 权限控制
- 缓存处理
- 监控
- 事务处理
- …
2.pom依赖
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3.自定义注解
如果需要更详细了解如何自定义注解见https://blog.csdn.net/qq_42513284/article/details/109047862
package com.qin.test.annotation;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
/**
* @author 小枫
* @version 1.0.0
* @date 2020/10/12 21:30
* @desciption MyTestAnnotation
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyTestAnnotation {
String value() default "";
String name() default "";
}
3.AOP切面
package com.qin.test.aspect;
import com.qin.test.annotation.MyTestAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author 小枫
* @version 1.0.0
* @date 2020/10/12 21:30
* @desciption MyTestAspect
*/
@Component
@Aspect
public class MyTestAspect {
/**
* 定义切入点,切入点为com.qin..TestController.te*(..)
* public: 方法权限修饰符,可以为*,*为任意
* *: 方法返回值,可以为* Object+: Object以及子类,但不包含void
* ..: com.qin下的所有包
* te*: 以te为前缀的方法
* (..): 任意参数,可以为 (String,..)、(String,*)、(Object+,..)等
* 通过@Pointcut注解声明频繁使用的切点表达式
*/
@Pointcut("execution(* com.qin..te*(String,*))")
public void methodPointCut() {
}
/**
* 定义切入点,切入点为@MyTestAnnotation注解
* 通过@Pointcut注解声明频繁使用的切点表达式
*/
@Pointcut("@annotation(com.qin.test.annotation.MyTestAnnotation)")
public void annotationPointCut() {
}
/**
* @description 在连接点执行之前执行的通知
* || :满足任意符合条件的连接点
*/
@Before("methodPointCut() || annotationPointCut()")
public void doBeforeTest() {
System.out.println("Before......................");
}
/**
* @description 在连接点执行之后执行的通知(返回通知和异常通知的异常)
*/
@After("methodPointCut() || annotationPointCut()")
public void doAfterTest() {
System.out.println("After......................");
}
/**
* @description 在连接点执行之后执行的通知(返回通知)
*
*/
@AfterReturning("methodPointCut() || annotationPointCut()")
public void doAfterReturningTest() {
System.out.println("AfterReturning......................");
}
/**
* @description 在连接点执行之后执行的通知(异常通知)
*/
@AfterThrowing("methodPointCut() || annotationPointCut()")
public void doAfterThrowingTest() {
System.out.println("AfterThrowing......................");
}
@Around("methodPointCut() || annotationPointCut()")
public void doAroundTest(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Around proceed before......................");
MethodSignature signature =(MethodSignature) proceedingJoinPoint.getSignature();
Method method = signature.getMethod();
MyTestAnnotation annotation = method.getAnnotation(MyTestAnnotation.class);
System.out.println(annotation.name());
System.out.println(annotation.value());
proceedingJoinPoint.proceed();
System.out.println("Around proceed after......................");
}
}
3.1.execution表达式
<权限修饰符> <返回值> <包名> <类> <方法> <参数>
其中返回类型模式、方法名模式和参数模式时必须的,其它项都是可选的
表达式 | 描述 |
---|---|
public String com.qin.test.controller.TestController.test(…) | 匹配权限修饰符为public,返回值为String,在com.qin.test.controller包下的TestController类中方法名为test的任意方法参数的方法 |
public Object+ com.qin.test.controller.TestController.test(…) | 匹配权限修饰符为public,返回值为Object以及Object以及子类,但不包含void,在com.qin.test.controller包下的TestController类中方法名为test的任意方法参数的方法 |
public * com.qin…TestController.test(…) | 匹配权限修饰符为public,返回值为任意类型,在com.qin的子包下的TestController类中方法名为test的任意方法参数的方法 |
public * com.qin…TestController.te*(…) | 匹配权限修饰符为public,返回值为任意类型,在com.qin的子包下的TestController类中方法名为前缀为te的任意方法参数的方法 |
public * com.qin…TestController.*st(…) | 匹配权限修饰符为public,返回值为任意类型,在com.qin的子包下的TestController类中方法名为后缀为st的任意方法参数的方法 |
public * com.qin…TestController.*st(String,…) | 匹配权限修饰符为public,返回值为任意类型,在com.qin的子包下的TestController类中方法名为后缀为st,方法第一个参数为String类型的方法 |
public * com.qin…TestController.test(String,*) | 匹配权限修饰符为public,返回值为任意类型,在com.qin的子包下的TestController类中方法名为后缀为st,方法第一个参数为String类型的方法(仅有两个参数) |
3.2.通知
注解 | 描述 |
---|---|
@Pointcut | 声明切点 |
@Before | 在连接点执行之前执行的通知 |
@After | 在连接点执行之后执行的通知(返回通知和异常通知的异常) |
@AfterReturning | 在连接点执行之后执行的通知(返回通知) |
@AfterThrowing | 在连接点执行之后执行的通知(异常通知) |
@Around | 环绕通知:既可在执行目标方法之前增强动作,也可在执行目标方法之后织入增强的执行;可以控制目标方法是否执行以及如何执行(比如修改目标方法的参数) |
4.执行顺序测试
4.1.测试类
package com.qin.test.controller;
import com.qin.test.annotation.MyTestAnnotation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 小枫
* @version 1.0.0
* @date 2020/10/13 21:20
* @desciption TestController
*/
@RestController
public class TestController {
/**
* 方式一: execution表达式
*/
@RequestMapping("/test")
public void test(String name,Integer age) throws Exception {
System.out.println("target method..................");
//throw new Exception("eeeeeeeeee");
}
/**
* 方式二: 注解
*/
@RequestMapping("/test2")
@MyTestAnnotation(value = "v",name = "n")
public void anTest(String name,Integer age){
System.out.println("target method..................");
}
}
4.2.测试结果
1.抛出异常时
Around proceed before......................
Before......................
target method..................
After......................
AfterThrowing......................
2.正常执行
Around proceed before......................
Before......................
target method..................
Around proceed after......................
After......................
AfterReturning......................