1. 为什么Spring AOP最常和自定义注解进行结合使用呢?
自动jdk提供了注解功能之后,注解就作为一个JVM机器可以识别的机器注释,在需要使用的时候标注注解即可;不需要使用的时候移除注解即可。
标注注解本身对原本的业务逻辑没有明显的侵入,因为从注解的使用上来看,任何地方仅仅标注一个注解,本身是不会产生任何额外能力的。
注解被标注后,要想生效,是需要有专门的逻辑,通过反射去获取目标上标注的注解对象后,然后才能进一步的确定执行什么样的策略,目标上标注某个注解后产生的额外能力,从来都不是注解本身提供的能力,而是解析注解的地方才赋予的能力。
很显然,一个注解要想达到这种能力,它就应该能很方便的被动被解析。
因此一个注解,最好的应用场景无非如下:
(1)和各种开源框架的生命周期阶段进行紧密整合,插入自定义的逻辑去解析目标上标注的注解。
(2)和AOP进行整合。
其实,说白了还是咱们之前一直在聊的勾子,也就是注解的最常见使用场景就是和勾子进行整合,在勾子中解析目标上标注的注解,然后在勾子对应的生命周期阶段,通过获取到的注解实例靠近一步决策应该产生什么样的逻辑行为。
而很明显,AOP正是动态勾子的核心实现。
所以,自定义注解和AOP的结合就成了一种主流的AOP使用场景。一方面因为注解本身适用于AOP这种场景,一方面是因为注解在AOP的各种使用场景中是最灵活的,需要就标注,不需要就拿掉,几乎没有任何硬编码的地方。
2. Aspectj快速的实现基于自定义注解的AOP切面
AOP的底层原理就是基于动态代理,而实现动态代理有jdk的方式和开源cglib的方式。
而AspectJ作为一种编译阶段织入逻辑的动态代理实现,从spring整合后几乎就已经成为了业务侧快速开发一个AOP组件的唯一手段了。
尽管开发者仍旧可以通过spring提供的基础AOP的核心API,进行AOP的更底层实现,但是采用aspect的方式明显降低了开发AOP组件的复杂度。
3.注解,如何避免注解的传播嵌套?
(1)嵌套注解:一个注解本身的成员是另外一个注解,一层一层进行引用,这种注解就叫做嵌套注解。即注解对象B作为注解A的成员嵌套在A注解中。
(2)复合注解:一个注解定义上,标注了另外一个注解。这种注解就叫做符合注解。
(3)注解的传播嵌套:在AOP切指定注解时,比如有ServiceA的a方法,ServiceB的b方法,ServiceC的c方法,都标注了指定的注解,那么假设一个请求进来后,调度路径是a方法,a方法里调度b方法,b方法里调度c方法;那么此时,a\b\c方法都会织入切面逻辑。但是有时候,我们需要实现的需求是,在一个请求进来后,织入的逻辑永远只指定调度路径中第一个标注注解的方法,其他底层调度路径中的方法即便标注了注解,也不就真正织入逻辑。这理解起来类似于事务注解的传播属性机制。
解决这个问题,有两种,一种是强制校验;另外一种是内部路径的注解AOP默认失效。
核心思路是:肯定需要一个当前线程上下文,即ThreadLocal,用来存储当前线程整个调度路径中处于栈底的目标方法的标记-即首次标注目标注解的方法。
3.1 避免注解的传播嵌套
3.2 内层调度路径的注解AOP默认失效
文档信息
- 本文作者:Marshall