AOP实现
连接点 Joinpoint
在 Spring AOP 中,仅支持方法级别的连接点。
1 | public interface Joinpoint { |
proceed 方法是核心,该方法用于执行拦截器逻辑。
一个方法调用就是一个连接点,一下方法调用这个接口的定义:
1 |
|
切点Pointcut
切点是用于选择连接点的
1 | public interface Pointcut { |
Pointcut 接口中定义了两个接口,分别用于返回类型过滤器和方法匹配器。
1 | public interface ClassFilter { |
上面的两个接口均定义了 matches 方法,用户只要实现了 matches 方法,即可对连接点进行选择。
通常是用 AspectJ 表达式对连接点进行选择。Spring 中提供了一个 AspectJ 表达式切点类 - AspectJExpressionPointcut。
这个类最终实现了 Pointcut、ClassFilter 和 MethodMatcher 接口,因此该类具备了通过 AspectJ 表达式对连接点进行选择的能力。
通知 Advice
通知 Advice 即我们定义的横切逻辑,Spring 中定义了以下几种通知类型:
前置通知(Before advice)- 在目标方便调用前执行通知
后置通知(After advice)- 在目标方法完成后执行通知
返回通知(After returning advice)- 在目标方法执行成功后,调用通知
异常通知(After throwing advice)- 在目标方法抛出异常后,执行通知
环绕通知(Around advice)- 在目标方法调用前后均可执行自定义逻辑
1 | public interface Advice { |
通知接口的具体实现类:
切面Aspect
有了切点 Pointcut 和通知 Advice,由于这两个模块目前还是分离的,我们需要把它们整合在一起,就有了切面。
在 AOP 中,切面只是一个概念,并没有一个具体的接口或类与此对应。Spring 有一个接口的用途和切面很像,切点通知器 PointcutAdvisor。
1 | public interface Advisor { |
Advisor 中有一个 getAdvice 方法,用于返回通知。PointcutAdvisor 在 Advisor 基础上,新增了 getPointcut 方法,用于返回切点对象。因此 PointcutAdvisor 的实现类即可以返回切点,也可以返回通知。
织入Weaving
有了连接点、切点、通知,以及切面等,Spring 是通过何种方式将通知织入到目标方法上的。
通过实现后置处理器 BeanPostProcessor 接口。该接口是 Spring 提供的一个拓展接口,通过实现该接口,可在 bean 初始化前后做一些自定义操作。在 bean 初始化完成后,即 bean 执行完初始化方法(init-method)。Spring通过切点对 bean 类中的方法进行匹配。若匹配成功,则会为该 bean 生成代理对象,并将代理对象返回给容器。容器向后置处理器输入 bean 对象,得到 bean 对象的代理,这样就完成了织入过程。
AOP 入口
Spring AOP 抽象代理创建器实现了 BeanPostProcessor 接口,并在 bean 初始化后置处理过程中向 bean 中织入通知。创建代理对象的入口方法是在类AbstractAutoProxyCreator。
1 | public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport |
创建代理对象主要是wrapIfNecessary方法,首先校验的是是否需要生成代理对象,不需要就直接返回。
查找适合的通知器
在织入之前需要为目标 bean 查找合适的通知器
1 | //获取增强类 |
Spring 先查询出所有的通知器,然后再调用 findAdvisorsThatCanApply 对通知器进行筛选。
查找通知器
AbstractAdvisorAutoProxyCreator 中的 findCandidateAdvisors 是个空壳方法,所有逻辑封装在了一个 BeanFactoryAdvisorRetrievalHelper 的 findAdvisorBeans 方法中。
1 | public List<Advisor> findAdvisorBeans() { |
从容器中查找所有类型为 Advisor 的 bean 对应的名称,,遍历 advisorNames,并从容器中获取对应的 bean。
findCandidateAdvisors
1 | protected List<Advisor> findCandidateAdvisors() { |
buildAspectJAdvisors
@Aspect 注解的解析过程。
1 | /** |
过程是:
获取容器中所有 bean 的名称(beanName)
遍历上一步获取到的 bean 名称数组,并获取当前 beanName 对应的 bean 类型(beanType)
根据 beanType 判断当前 bean 是否是一个的 Aspect 注解类,若不是则不做任何处理
调用 advisorFactory.getAdvisors 获取通知器
其中的this.advisorFactory.getAdvisors(factory)方法解释:
1 | // 各个增强器的获取方法的实现 |
getAdvisor 方法包含两个主要步骤,一个是获取 AspectJ 表达式切点,另一个是创建 Advisor 实现类。
1 | //获取 AspectJ 表达式切点 |
获取通知器(getAdvisors)整个过程的逻辑,如下:
从目标 bean 中获取不包含 Pointcut 注解的方法列表
遍历上一步获取的方法列表,并调用 getAdvisor 获取当前方法对应的 Advisor
创建 AspectJExpressionPointcut 对象,并从方法中的注解中获取表达式,最后设置到切点对象中
创建 Advisor 实现类对象 InstantiationModelAwarePointcutAdvisorImpl
调用 instantiateAdvice 方法构建通知
调用 getAdvice 方法,并根据注解类型创建相应的通知
例如其中一个的实现:
1 | public class AspectJAfterAdvice extends AbstractAspectJAdvice |
查找合适的通知器
findAdvisorsThatCanApply
1 | protected List<Advisor> findAdvisorsThatCanApply( |
创建代理对象
通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 bean 的某些方法前后。
先创建 AopProxy 对象,然后再调用该对象的 getProxy 方法创建实际的代理类。
1 | public interface AopProxy { |
Spring 在为目标 bean 创建代理的过程中根据 bean 是否实现接口,以及一些其他配置来决定使用 AopProxy 何种实现类为目标 bean 创建代理对象。
1 | protected Object createProxy(Class<?> beanClass, @Nullable String beanName, |
getProxy 这里有两个方法调用,一个是调用 createAopProxy 创建 AopProxy 实现类对象,然后再调用 AopProxy 实现类对象中的 getProxy 创建代理对象。
1 | //创建代理对象。 |
JdkDynamicAopProxy 最终调用 Proxy.newProxyInstance 方法创建代理对象。
执行过程
得到了 bean 的代理对象,通知也以合适的方式插在了目标方法的前后。当目标方法被多个通知匹配到时,Spring 通过引入拦截器链来保证每个通知的正常执行。
对于 JDK 动态代理,代理逻辑封装在 InvocationHandler 接口实现类的 invoke 方法中。JdkDynamicAopProxy 实现了 InvocationHandler 接口。
1 |
|
流程如下:
检测 expose-proxy 是否为 true,若为 true,则暴露代理对象
获取适合当前方法的拦截器
如果拦截器链为空,则直接通过反射执行目标方法
若拦截器链不为空,则创建方法调用 ReflectiveMethodInvocation 对象
调用 ReflectiveMethodInvocation 对象的 proceed() 方法启动拦截器链
处理返回值,并返回该值
获取所有的拦截器
拦截器是指用于对目标方法的调用进行拦截的一种工具,如通知前置通知。这里说明其中一种通知连接器类型。
1 | public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable { |
如何获取拦截器
1 | //获取拦截器 |
启动拦截器链
ReflectiveMethodInvocation 贯穿于拦截器链执行的始终,是核心。 proceed 方法用于启动启动拦截器链。
1 | //所有的增强都在这个拦截器 |
proceed 根据 currentInterceptorIndex 来确定当前应执行哪个拦截器,并在调用拦截器的 invoke 方法时,将自己作为参数传给该方法。
执行目标方法
1 |
|