Q1-spring aop 中的 @within @annotation 详细介绍和使用

在Spring AOP(面向切面编程)中,@within@annotation 是两个用于定义切点的注解。切点是在程序执行过程中,插入切面代码的特定位置。Spring AOP允许你在应用程序的特定方法上或特定类上应用切面。

  1. @within 注解:

@within 注解用于匹配标注有特定注解的类的方法。它的作用是选择那些所在类标注了指定注解的方法,然后将切面逻辑应用于这些方法。下面是一个示例:

  [java]
1
2
3
4
5
6
7
8
9
@Aspect @Component public class MyAspect { @Before("@within(com.example.MyAnnotation)") public void beforeMethodWithinAnnotation() { // 在标注有 @MyAnnotation 注解的类中的方法执行之前执行 } }

在上面的例子中,@Before 注解表示在匹配的切点方法执行之前执行 beforeMethodWithinAnnotation 方法。这个切点会匹配那些所在类标注了 com.example.MyAnnotation 注解的方法。

  1. @annotation 注解:

@annotation 注解用于匹配标注有特定注解的方法。它的作用是选择那些被标注了指定注解的方法,然后将切面逻辑应用于这些方法。下面是一个示例:

  [java]
1
2
3
4
5
6
7
8
9
@Aspect @Component public class MyAspect { @Before("@annotation(com.example.Loggable)") public void beforeLoggableMethod() { // 在标注有 @Loggable 注解的方法执行之前执行 } }

在上面的例子中,@Before 注解表示在匹配的切点方法执行之前执行 beforeLoggableMethod 方法。这个切点会匹配那些被标注了 com.example.Loggable 注解的方法。

总结:

  • @within 用于匹配标注有指定注解的类中的方法。
  • @annotation 用于匹配被指定注解标注的方法。

这些注解是Spring AOP中定义切点的一种方式。你可以将切面逻辑应用于特定的类或方法,并根据需要在方法执行前或执行后插入自定义的行为。注意,切面和切点的定义通常结合在一起,切面定义了切点以及在切点上执行的通知(advice)逻辑。

实战使用例子

常见写法

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package com.github.houbb.auto.log.spring.aop; import com.github.houbb.aop.spring.util.SpringAopUtil; import com.github.houbb.auto.log.annotation.AutoLog; import com.github.houbb.auto.log.core.bs.AutoLogBs; import com.github.houbb.auto.log.spring.context.SpringAopAutoLogContext; 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.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 这是一种写法 * 自动日志输出 aop * @author binbin.hou * @since 0.0.3 */ @Aspect @Component @EnableAspectJAutoProxy @Deprecated public class AutoLogAop { /** * * 切面方法: * * (1)扫描所有的共有方法 * <pre> * execution(public * *(..)) * </pre> * * 问题:切面太大,废弃。 * 使用扫描注解的方式替代。 * * (2)扫描指定注解的方式 * * 其实可以在 aop 中直接获取到注解信息,暂时先不调整。 * 暂时先不添加 public 的限定 * * (3)直接改成注解的优缺点: * 优点:减少了 aop 的切面访问 * 缺点:弱化了注解的特性,本来是只要是 {@link com.github.houbb.auto.log.annotation.AutoLog} 指定的注解即可, * * 不过考虑到使用者的熟练度,如果用户知道了自定义注解,自定义 aop 应该也不是问题。 * * // 匹配任意public方法 * execution(public * *(..)) * // 匹配任意以set开头的方法 * execution(* set*(..)) * // 匹配AccountService接口中定义的任意方法 * execution(* com.xyz.service.AccountService.*(..)) * // 匹配service包定义的任意方法 * execution(* com.xyz.service.*.*(..)) * // 匹配service或其子包中定义的任意方法 * execution(* com.xyz.service..*.*(..)) * */ @Pointcut("@within(com.github.houbb.auto.log.annotation.AutoLog)" + "|| @annotation(com.github.houbb.auto.log.annotation.AutoLog)") public void autoLogPointcut() { } /** * 执行核心方法 * * 相当于 MethodInterceptor * * @param point 切点 * @return 结果 * @throws Throwable 异常信息 * @since 0.0.3 */ @Around("autoLogPointcut()") public Object around(ProceedingJoinPoint point) throws Throwable { Method method = SpringAopUtil.getCurrentMethod(point); AutoLog autoLog = AnnotationUtils.getAnnotation(method, AutoLog.class); //获取当前类注解信息 if(autoLog == null) { autoLog = SpringAopUtil.getClassAnnotation(point, AutoLog.class); } // 如果不存在 if(autoLog == null) { return point.proceed(); } // 如果存在,则执行切面的逻辑 SpringAopAutoLogContext logContext = SpringAopAutoLogContext.newInstance(); logContext.method(method) .autoLog(autoLog) .params(point.getArgs()); logContext.point(point); return AutoLogBs.newInstance() .context(logContext) .execute(); } }

动态切面

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.github.houbb.auto.log.spring.aop; import org.aspectj.lang.annotation.Aspect; import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; /** * 动态配置的切面 * 自动日志输出 aop * @author binbin.hou * @since 0.3.0 */ @Configuration @Aspect //@EnableAspectJAutoProxy public class AutoLogDynamicPointcut { /** * 切面设置,直接和 spring 的配置对应 ${},可以从 properties 或者配置中心读取。更加灵活 */ @Value("${auto.log.pointcut:@within(com.github.houbb.auto.log.annotation.AutoLog)||@annotation(com.github.houbb.auto.log.annotation.AutoLog)}") private String pointcut; @Bean("autoLogPointcutAdvisor") public AspectJExpressionPointcutAdvisor autoLogPointcutAdvisor() { AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor(); advisor.setExpression(pointcut); advisor.setAdvice(new AutoLogAdvice()); return advisor; } }

对应的切面实现。

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.github.houbb.auto.log.spring.aop; import com.github.houbb.auto.log.annotation.AutoLog; import com.github.houbb.auto.log.core.bs.AutoLogBs; import com.github.houbb.auto.log.spring.config.DefaultAutoLogGlobalConfig; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.core.annotation.AnnotationUtils; import java.lang.reflect.Method; /** * 切面拦截器 * * @author binbin.hou * @since 0.3.0 */ public class AutoLogAdvice implements MethodInterceptor { private AutoLog getAutoLogAnnotation(final MethodInvocation methodInvocation) { //1. 方法级别 Method method = methodInvocation.getMethod(); AutoLog autoLog = AnnotationUtils.getAnnotation(method, AutoLog.class); if (autoLog != null) { return autoLog; } //2. 类级别 final Class<?> targetClass = methodInvocation.getThis().getClass(); autoLog = targetClass.getAnnotation(AutoLog.class); if (autoLog != null) { return autoLog; } //3. 默认的注解信息 return DefaultAutoLogGlobalConfig.class.getAnnotation(AutoLog.class); } @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { AutoLog autoLog = getAutoLogAnnotation(methodInvocation); // 如果存在,则执行切面的逻辑 AutoLogAdviceContext logContext = AutoLogAdviceContext.newInstance(); logContext.methodInvocation(methodInvocation) .method(methodInvocation.getMethod()) .autoLog(autoLog) .params(methodInvocation.getArguments()) ; return AutoLogBs.newInstance() .context(logContext) .execute(); } }

参考资料