AOP的相关知识
一、相关注解
1、 @Target(注解的用作目标)
- @Target(ElementType.Type)【接口、枚举、注解】
- @Target(ElementType.FIELD)【字段、枚举的常量】
- @Target(ElementType.METHOD)【方法】
- @Target(ElementType.PARAMETER)【方法参数】
- @Target(ElementType.CONSTRUCTOR)【构造函数】
- Target(ElementType.LOCAL_VARIABLE)【局部变量】
- Target(Element.Type.ANNOTATION_TYPE)【注解】
- Target(ElementType.PACKAGE)【包】
2、@Retention(注解的保留位置)
- @Retention(RetentionPolicy.SOURCE)【该类Annotations只在源代码级别保留,编译是忽略,在class字节码文件中不包含】
- @Retention(RetentionPolicy.CLASS)【该类Annotations编译时被保留,默认的保留策略,在class文件中存在,但JVM将会忽略,运行时无法获得】
- @Retention(RetentionPolicy.RUNTIME)【该类Annotations在JVM保留,可以被Jvm以及其他的反射机制的代码读取和使用】
3、@Document(说明该注解将被包含在javadoc中)
4、@Inherited(说明子类可以继承父类中的该注解)
5、通知注解【作用在方法上】
-
@Before(在目标方法之前运行)【前置通知】
-
@After(在目标方法结束之后运行)【后置通知】
-
@AfterReturning(在目标方法正常返回之后运行)【返回通知】
-
@AfterThrowing(在目标方法抛出异常之后运行)【异常通知】
-
@Around(环绕通知)
-
括号内的写法@xxxx(execution(" * com.xx.serviceImpl. * . * (…))")
这是匹配所有ServiceImpl包下面的所有类的所有方法 -
类型匹配模式的符号意思
- *: 匹配任何数量字符;比如( *,String)匹配了一个接受两个参数的方法,第一个可以任意类型,第二个则必须是String类型
- . . :匹配任何数量字符的重复,在类型模式中匹配任何数量子包, 在方法参数模式中匹配任何数量参数,可以是零到多个
- +: 匹配指定类型的子类型;仅能作为后缀放在类型模式后边
-
参数匹配模式:
- () 匹配了一个不接搜狐任何参数的方法
- (. .)匹配了一个接受任何数量参数的方法(零或者更多)
- (*) 匹配了一个接受一个任何类型的参数的方法
- (*,String)匹配一个接受两个参数的方法,第一个为任意类型,第二个为String类型
-
一些execution切入点表达式的例子
- execution (public * * (. .)) 【任何公共方法的执行】
- execution (* set*(. .)) 【任何一个名字以"set"开始的方法的执行】
- execution (* com.xxx.xxxService.*(. .)) 【xxxService接口定义的任意方法的执行】
- execution(* com.xx.service.* .*(. .)) 【在service包中定义的任意方法的执行】
- execution(* com.xx.service. .* .*(. .)) 【在service包后者其子包的任意方法的执行】
-
切片的用法
- 对应的注解类
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface LockKey { String key() default ""; //过期时间,使用本地缓存可以忽略,如果使用redis做缓存就需要 int expire() default 5; }
- 可以在方法上先定一个切入点PointCut(需要差监控的包名) 在写入一个方法进行标记,之后在给方法添加advice(之前标记的方法名),例子如下:
@Pointcut("execution(* com.shishibusiness.service.*.*(..))") public void myLog(){ } // @Around("myLog()") public Object beforeMethod(ProceedingJoinPoint joinPoint) { }
- 还有一个可以不写PointCut的声明直接在对应方法上加 execution 逻辑运算符(&& || !)来组合条件,这样代码可以更加简洁,例子如下:
// 添加了@annotation(里面是要接受的参数,如果是注解类,那么他就可以获取注解里面的信息) @Around("execution(public * *(..)) &&@annotation(com.xxx.annotation.LockKey)") public Object beforeMethod(ProceedingJoinPoint joinPoint) { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); // 这个就是对应的注解里面的参数 LockKey lockKey = method.getAnnotation(LockKey.class); } //再到对应的包路径能检索到地方,及execution里路径能涵盖的地方,添加相应的注解 @Resource private UserService userService; @GetMapping("/getAllUser") @ApiOperation("获取所有用户") // 这个位置就是添加的注解了 @LockKey() public List<User> getAllUser(){ return userService.getAllUser(); }
- 还有另外一种方法,等同于上述的操作,例子如下:
@Around(value = "execution(public * *(..)) && @annotation(lockKey)",argNames = "joinPoint,lockKey") public Object beforeMethod(ProceedingJoinPoint joinPoint,LockKey lockKey) { System.out.println("这里运行了"); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); //再到对应的包路径能检索到地方,及execution里路径能涵盖的地方,添加相应的注解 @Resource private UserService userService; @GetMapping("/getAllUser") @ApiOperation("获取所有用户") // 这个位置就是添加的注解了 @LockKey() public List<User> getAllUser(){ return userService.getAllUser(); }