写点什么

Spring 高手之路 23——AOP 触发机制与代理逻辑的执行

作者:砖业洋__
  • 2024-10-24
    广东
  • 本文字数:6758 字

    阅读完需:约 22 分钟

Spring高手之路23——AOP触发机制与代理逻辑的执行

1. 从整体视角学习 Bean 是如何被 AOP 代理的

为了全面理解Bean是如何被AOP代理的,我们把前面几篇文章串联一下,需要关注以下几点,并针对每个关键点学习相应的源码部分:


1. AOP 代理的触发机制(本章需要讲解的)理解Spring如何决定哪些Bean需要被代理。


关键点:


  • BeanPostProcessor 接口Spring AOP的自动代理创建器实现了这个接口,通过它在Bean初始化的前后进行处理。


源码部分:AbstractAutoProxyCreator(位于org.springframework.aop.framework.autoproxy包中)


  • 方法:postProcessAfterInitialization

  • 方法:wrapIfNecessary


2. 确定哪些 Bean 需要代理(前面已讲)理解Spring如何确定哪些Bean需要被代理,这通常涉及到扫描和匹配切面。


这部分内容在 Spring 高手之路 21——深入剖析 Spring AOP 代理对象的创建的第2节“匹配增强器Advisors(源码分析+时序图说明)”已经讲解,可自行前往翻阅学习。


关键点:


  • Advisor 和 Advice:这些是Spring AOP中的切面和通知。


源码部分:


  • AbstractAdvisorAutoProxyCreator 的方法getAdvicesAndAdvisorsForBean(位于org.springframework.aop.framework.autoproxy包中)


3. 创建代理对象(前面已讲)理解Spring如何创建代理对象,包括使用JDK动态代理和CGLIB代理。


这部分内容在 Spring 高手之路 22——AOP 切面类的封装与解析的第3.2节“TargetSource 的构建”有提到,可自行前往翻阅学习。


关键点:


  • 代理工厂(ProxyFactory):用于创建代理对象。

  • 动态代理实现JDK动态代理和CGLIB)。


源码部分:


  • ProxyFactory 的方法getProxy(位于org.springframework.aop.framework包中)

  • JdkDynamicAopProxy 的方法invoke(位于org.springframework.aop.framework包中)

  • CglibAopProxy 的方法getProxy(位于org.springframework.aop.framework包中)


4. 代理对象的配置(前面已讲)理解如何配置代理对象的属性,如切入点和通知。


这部分内容在 Spring 高手之路 22——AOP 切面类的封装与解析的第3.2节“TargetSource 的构建”有提到,可自行前往翻阅学习。


关键点:


  • AdvisedSupport 类:包含了代理配置的相关信息。


源码部分:


  • AdvisedSupport(位于org.springframework.aop.framework包中)


5. 执行代理逻辑(本章需要讲解的)理解代理对象在运行时如何拦截方法调用并执行通知逻辑。


关键点:


  • 拦截器链:代理对象通过拦截器链来执行切面逻辑。


源码部分:


  • ReflectiveMethodInvocation 的方法proceed(位于org.springframework.aop.framework包中)


推荐学习路径


  1. 从高层理解代理触发机制


  • 查看AbstractAutoProxyCreator类,特别是wrapIfNecessarypostProcessAfterInitialization方法,理解代理触发的整体流程。


  1. 深入理解切面和通知的获取


  • 查看AbstractAdvisorAutoProxyCreator类,特别是getAdvicesAndAdvisorsForBean方法,理解如何获取适用于目标Bean的切面和通知。


  1. 代理对象的创建细节


  • 查看ProxyFactory类,特别是getProxy方法,理解代理对象的具体创建过程。


  1. 理解动态代理的实现


  • 查看JdkDynamicAopProxyCglibAopProxy类,理解JDK动态代理和CGLIB代理的具体实现细节。


  1. 配置代理对象


  • 查看AdvisedSupport类,理解代理对象的配置和管理。


  1. 执行代理逻辑


  • 查看ReflectiveMethodInvocation类,特别是proceed方法,理解代理对象在运行时如何拦截方法调用并执行切面逻辑。

2. AOP 代理的触发机制

  还记得BeanPostProcessor接口吗?这在“Spring 高手之路 13——BeanFactoryPostProcessor 与 BeanDefinitionRegistryPostProcessor 解析”有讲到。


BeanPostProcessor接口提供了两个方法:


  • postProcessBeforeInitialization:在Bean初始化之前调用。

  • postProcessAfterInitialization:在Bean初始化之后调用。


Spring AOP利用postProcessAfterInitialization方法在Bean初始化完成后,检查并决定是否需要对这个Bean进行代理。

2.1 postProcessAfterInitialization 方法源码分析

本节源码基于 spring-aop-5.3.16



代码提出来分析


/** * 如果该bean被子类标识为需要代理的bean,则使用配置的拦截器创建一个代理。 * @see #getAdvicesAndAdvisorsForBean */@Overridepublic Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {    // 如果bean不为空    if (bean != null) {        // 获取缓存键,通常是bean的类和名称的组合,用于在缓存中存取与该bean相关的元数据        Object cacheKey = getCacheKey(bean.getClass(), beanName);        // 从earlyProxyReferences集合中移除缓存键,如果当前bean不在该集合中,表示需要代理该bean        if (this.earlyProxyReferences.remove(cacheKey) != bean) {            // 如果需要的话,对bean进行包装(代理)            return wrapIfNecessary(bean, beanName, cacheKey);        }    }    // 如果bean为null或不需要代理,直接返回原始bean    return bean;}
复制代码


这个方法主要功能


  • 确定代理需求:该方法的主要目的是确定是否需要为Bean创建代理。如果需要,wrapIfNecessary方法将负责实际的代理创建过程。

  • 优化性能:通过缓存键和earlyProxyReferences集合,可以避免重复处理同一个Bean,提高性能。

  • 集成 AOP 功能:为需要代理的Bean创建代理对象,使得AOP切面能够在Bean的方法调用前后执行。

2.2 wrapIfNecessary 方法源码分析

代码提出来分析


/** * 如果需要,对给定的bean进行包装(代理),即如果它符合被代理的条件。 * @param bean 原始的bean实例 * @param beanName bean的名称 * @param cacheKey 用于元数据访问的缓存键 * @return 包装(代理)后的bean,或原始的bean实例 */protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {    // 如果beanName不为空且targetSourcedBeans集合包含这个beanName,直接返回原始bean    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {        return bean;    }    // 如果advisedBeans缓存中显示这个bean不需要代理,直接返回原始bean    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {        return bean;    }    // 如果bean的类是基础设施类(如Spring内部使用的类)或应该跳过代理    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {        // 将这个bean标记为不需要代理        this.advisedBeans.put(cacheKey, Boolean.FALSE);        return bean;    }
// 获取适用于这个bean的通知和切面 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); // 如果有通知和切面,则创建代理 if (specificInterceptors != DO_NOT_PROXY) { // 将这个bean标记为需要代理 this.advisedBeans.put(cacheKey, Boolean.TRUE); // 创建代理对象 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); // 缓存代理对象的类型 this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; }
// 如果没有适用于这个bean的通知和切面,将这个bean标记为不需要代理 this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean;}
复制代码


  wrapIfNecessary方法的主要功能是检查给定的Bean是否需要进行AOP代理,并在必要时为其创建代理对象。该方法在Bean初始化后被调用,以确保符合条件的Bean能够被AOP代理,从而使AOP切面能够在Bean的方法调用前后执行。

2.3 时序图演示触发机制


详细分析和说明:


  1. Caller 请求 Bean 实例


  • Caller调用BeanFactory来获取Bean实例。

  • 这一步表示应用程序或其他Bean调用者需要获取一个Bean实例。


  1. BeanFactory 创建 Bean 实例


  • BeanFactory创建Bean实例,准备进行初始化。

  • 这包括通过Spring的依赖注入机制构造Bean实例。


  1. 调用 postProcessAfterInitialization


  • BeanFactory调用AOPProxypostProcessAfterInitialization方法。

  • 这是Spring AOP框架用于在Bean初始化后进行处理的关键步骤。


  1. 获取缓存键


  • AOPProxy获取缓存键(cacheKey),通常是Bean的类和名称的组合。

  • 缓存键用于在缓存中存取与Bean相关的元数据。


  1. 检查 earlyProxyReferences 集合


  • AOPProxy检查earlyProxyReferences集合,判断当前Bean是否已经处理过。

  • 这是为了避免重复处理同一个Bean,提高性能。


  1. 判断 Bean 是否需要代理


  • 通过检查earlyProxyReferences集合,决定是否需要继续代理。

  • 如果Bean需要代理,进入wrapIfNecessary方法。


  1. 调用 wrapIfNecessary


  • wrapIfNecessary方法负责实际的代理创建过程。

  • 包括进一步的检查和代理对象的创建。


  1. 检查 targetSourcedBeans 集合


  • 判断Bean是否在 targetSourcedBeans 集合中,如果是,直接返回原始Bean

  • targetSourcedBeans集合用于存储一些特殊的Bean,不需要代理。


targetSourcedBeans 集合主要用于管理那些使用动态目标源的 Bean。在创建代理时,如果一个 Bean在这个集合中,Spring 会进行特殊处理,通常直接返回原始 Bean,而不是创建新的代理对象。这种机制确保了特殊 Bean的正确处理和高效运行。


  1. 检查 advisedBeans 集合


  • 检查缓存中是否已经标记了该Bean不需要代理,如果是,直接返回原始Bean

  • 提高处理效率,避免重复检查。


  1. 检查基础设施类和跳过条件


  • 判断Bean是否为Spring的基础设施类或是否有其他跳过条件。

  • 基础设施类通常不需要代理,因为它们是框架内部使用的。


  1. 获取通知和切面


  • 调用getAdvicesAndAdvisorsForBean方法,获取适用于该Bean的通知(Advice)和切面(Advisor)。

  • Advisor对象包含了切面逻辑,Advice对象包含了实际的通知逻辑。


  1. 检查是否有 specificInterceptors


  • 判断是否有适用于该Bean的通知和切面。

  • 如果有,继续创建代理对象。


  1. 创建代理对象


  • 调用createProxy方法,通过ProxyFactory创建代理对象。

  • 代理对象将拦截方法调用,并应用切面逻辑。


  1. 返回代理对象


  • 代理对象创建完成后,返回给AOPProxy

  • AOPProxy将代理对象返回给BeanFactory


  1. 返回原始Bean或代理对象:


  • 如果Bean不需要代理,返回原始Bean

  • 如果需要代理,返回代理对象。


  1. BeanFactory返回结果:


  • BeanFactory将最终的Bean实例(可能是原始Bean或代理对象)返回给Caller

3. AOP 代理逻辑的执行

3.1 AOP 代理如何使用拦截器

AOP代理拦截方法调用时,它会依次调用配置的拦截器链。每个拦截器都通过invoke方法处理方法调用。整个流程如下:


  • 方法调用被代理拦截:当一个方法被调用时,AOP代理会首先拦截这个调用。

  • 拦截器链处理:调用会被传递给拦截器链中的第一个拦截器。

  • 执行拦截器链:每个拦截器的invoke方法都会执行其逻辑,并决定是否调用下一个拦截器。

  • 目标方法执行:如果所有拦截器都允许,最终会调用目标方法本身。

  • 返回结果或处理异常:目标方法执行完成后,结果或异常会逐个传递回拦截器链,直到返回给最初调用方法的客户端。


比如给个简单拦截器的例子:


import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;
public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { // 在目标方法执行前 System.out.println("Before method: " + invocation.getMethod().getName());
// 调用目标方法 Object result = invocation.proceed();
// 在目标方法执行后 System.out.println("After method: " + invocation.getMethod().getName());
return result; }}
复制代码


在这个示例中,invoke方法是拦截器的核心。


  • 当方法被调用时,invoke方法会先打印“方法调用前”,然后调用invocation.proceed()执行目标方法,最后在方法执行后或发生异常时打印相应的信息。

  • invocation.proceed()方法用于继续调用拦截器链中的下一个拦截器,直到最终调用目标方法。

3.2 proceed 方法源码分析


代码提出来分析:


/** * 逐个调用拦截器链中的拦截器,并在最后执行目标方法(连接点)。 * @return 调用链中最后一个拦截器或目标方法的返回值 * @throws Throwable 如果拦截器或目标方法抛出异常 */@Override@Nullablepublic Object proceed() throws Throwable {    // 我们从索引 -1 开始,并提前递增索引。    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {        // 如果当前拦截器索引等于拦截器链的最后一个索引,则调用目标方法        return invokeJoinpoint();    }
// 获取当前索引的拦截器或拦截建议 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); // 检查是否是 InterceptorAndDynamicMethodMatcher 类型 if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // 如果是,评估动态方法匹配器 InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; // 获取目标类的 Class 对象 Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); // 检查方法是否匹配 if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { // 如果匹配成功,调用拦截器的 invoke 方法 return dm.interceptor.invoke(this); } else { // 动态匹配失败,跳过这个拦截器并调用链中的下一个 return proceed(); } } else { // 如果是普通拦截器,直接调用其 invoke 方法 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); }}
/** * 使用反射调用连接点。 * 子类可以重写此方法以使用自定义调用。 * * @return 连接点的返回值 * @throws Throwable 如果调用连接点导致异常 */@Nullableprotected Object invokeJoinpoint() throws Throwable { // 使用反射调用目标对象的目标方法,并传递参数 return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);}
复制代码


  proceed方法和invokeJoinpoint方法共同实现了AOP拦截器链的执行逻辑。proceed方法依次执行拦截器链中的每个拦截器,并在链的末尾调用目标方法。invokeJoinpoint方法通过反射调用目标方法并返回其结果。这两个方法结合起来,使得AOP能够在目标方法执行前后插入各种横切关注点,如事务管理、日志记录等。


这里判断动态方法匹配器是干嘛的?


  • 静态方法匹配器:在编译时或配置时确定方法是否匹配,例如方法名、参数类型等。这种匹配是静态的,无法根据实际调用时的参数值进行判断。

  • 动态方法匹配器:在方法调用时动态评估方法匹配条件,例如根据实际传递的参数值判断是否匹配。这种匹配是动态的,可以提供更灵活的控制。


普通的AOP通常使用静态匹配,这里不用深究动态匹配器。

3.3 时序图


时序图详细说明:


  1. Caller 调用代理方法


  • Caller调用代理对象的方法,开始AOP的执行过程。

  • 代理对象是通过AOP创建的,包含了拦截器链。


  1. 创建 ReflectiveMethodInvocation 实例


  • 在代理对象内部,Spring AOP创建一个ReflectiveMethodInvocation实例。该实例封装了目标方法、拦截器链以及方法参数。

  • ReflectiveMethodInvocation负责处理方法调用的整个过程。


  1. 调用 proceed()


  • 代理对象调用ReflectiveMethodInvocationproceed()方法,开始执行拦截器链。


  1. 遍历拦截器链


  • ReflectiveMethodInvocation遍历拦截器链中的每个拦截器。

  • 对每个拦截器,ReflectiveMethodInvocation调用其invoke(this)方法。

  • 拦截器在调用目标方法之前和之后执行自定义逻辑。


  1. 调用 invokeJoinpoint()


  • 如果拦截器链中没有拦截器或所有拦截器已经执行完毕,ReflectiveMethodInvocation直接调用invokeJoinpoint()方法。

  • invokeJoinpoint()通过反射调用目标方法。


  1. 调用目标方法


  • ReflectiveMethodInvocation通过反射调用实际的目标方法。

  • 目标方法执行并返回结果。


  1. 返回结果


  • 目标方法执行完成后,结果返回给ReflectiveMethodInvocation

  • ReflectiveMethodInvocation将结果返回给所有拦截器,依次向上返回。


  1. 代理对象返回最终结果


  • ReflectiveMethodInvocation最终将结果返回给代理对象。

  • 代理对象将结果返回给Caller


欢迎一键三连~


有问题请留言,大家一起探讨学习


----------------------Talk is cheap, show me the code-----------------------

发布于: 刚刚阅读数: 5
用户头像

砖业洋__

关注

永远热爱,永远热泪盈眶,永远在路上。 2020-04-30 加入

掘金/InfoQ签约作者,阿里云/51CTO专家博主,华为云·云享专家,CSDN博客专家,腾讯云·2023年度影响力创作者/优质共创者,擅长MySQL和Java领域,技术分享,全网同名———砖业洋__

评论

发布
暂无评论
Spring高手之路23——AOP触发机制与代理逻辑的执行_spring_砖业洋___InfoQ写作社区