Spring 源码阅读 49:AOP 代理创建的过程

简介: 本文分析了 Spring 创建 AOP 代理的重要方法wrapIfNecessary以及它为 Bean 对象创建代理对象的主要流程。

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 48:用于创建 AOP 代理的后处理器分析

概述

上一篇中,我们从 AspectJAwareAdvisorAutoProxyCreator 和 AnnotationAwareAspectJAutoProxyCreator 共同的父类 AbstractAutoProxyCreator 中,找到了 Spring 创建代理的逻辑。在postProcessAfterInitialization方法中,Spring 为已经完成初始化的 Bean 实例创建代理,其中,最核心的就是调用了wrapIfNecessary方法。

本文将从wrapIfNecessary方法入手,分析 AOP 代理创建的过程。

wrapIfNecessary 方法

先看wrapIfNecessary方法的源码。

// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessaryprotectedObjectwrapIfNecessary(Objectbean, StringbeanName, ObjectcacheKey) {
if (StringUtils.hasLength(beanName) &&this.targetSourcedBeans.contains(beanName)) {
returnbean;
   }
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
returnbean;
   }
if (isInfrastructureClass(bean.getClass()) ||shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
returnbean;
   }
// Create proxy if we have advice.Object[] specificInterceptors=getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors!=DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Objectproxy=createProxy(
bean.getClass(), beanName, specificInterceptors, newSingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
returnproxy;
   }
this.advisedBeans.put(cacheKey, Boolean.FALSE);
returnbean;
}

这个方法的源码大致可以分为两部分。前半部分是一些判断,主要过滤了一些不需要进行代理的情况,并直接将 Bean 实例返回;后半部分是主要处理逻辑的部分。

我们先看前半部分,这里判断了三种不需要处理的情况。

  1. 如果这个 Bean 对象配置了自定义的 TargetSource,且已经被postProcessBeforeInstantiation方法处理过滤, 则此处不需要处理。
  2. 如果advisedBeans集合中,这个对象的cacheKey对应的值是false,则说明这个对象不需要被处理。
  3. 如果当前对象的类型是基础类,包括用来配置 AOP 的类型,那么不需要被处理,并且在advisedBeans集合中,将它的cacheKey对应的值配置为false,这样下次直接在集合中判断其是否需要处理就可以了。

接下来就是主要的业务逻辑了,也就是下面这部分。

// Create proxy if we have advice.Object[] specificInterceptors=getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors!=DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Objectproxy=createProxy(
bean.getClass(), beanName, specificInterceptors, newSingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
returnproxy;
   }
this.advisedBeans.put(cacheKey, Boolean.FALSE);
returnbean;

先从整体来看,首先,会调用getAdvicesAndAdvisorsForBean方法,获取到一个名叫specificInterceptors的 Object 数组,从方法名称中可以看出,这里是为了获取当前 Bean 对象能够匹配到的增强的集合。

如果获取到的集合为空,那么说明当前的对象不需要增强,也就不需要创建代理,直接返回即可。再返回之前,还会在advisedBeans集合中,将其对应的值设置为false,这样,下次就可以直接判断这个 Bean 对象不处理即可。

如果获取到的集合不为空,那么,就通过createProxy方法为其创建代理,然后在添加一些相应的缓存信息,最后将代理对象返回,这样,最终注册到 Spring 容器中的 Bean 对象就是这个代理对象。

虽然这个方法中没有包含多少细节,但是,我们可以从中看出,Spring 创建 AOP 代理大致可以分为两个步骤,第一步是根据 Bean 对象找到与之匹配的增强,第二步是为其创建代理对象。

接下来,我们在进入这两个关键的方法,看一下其中的主要逻辑。

getAdvicesAndAdvisorsForBean

先看getAdvicesAndAdvisorsForBean方法,这个方法的实现可以在 AbstractAdvisorAutoProxyCreator 中找到

// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean@Override@NullableprotectedObject[] getAdvicesAndAdvisorsForBean(
Class<?>beanClass, StringbeanName, @NullableTargetSourcetargetSource) {
List<Advisor>advisors=findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
returnDO_NOT_PROXY;
   }
returnadvisors.toArray();
}

其主要逻辑是调用了findEligibleAdvisors方法,并将返回的列表转换为数组。我们进入findEligibleAdvisors方法。

// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisorsprotectedList<Advisor>findEligibleAdvisors(Class<?>beanClass, StringbeanName) {
List<Advisor>candidateAdvisors=findCandidateAdvisors();
List<Advisor>eligibleAdvisors=findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors=sortAdvisors(eligibleAdvisors);
   }
returneligibleAdvisors;
}

我们这里先做宏观的流程梳理,暂时不深入细节,因此,这里根据源码中的注释信息,结合方法和变量的名称,大致分析方法的逻辑。

首先,通过findCandidateAdvisors方法找到候选的增强信息,这里应该是找到所有我们通过注解和 XML 配置的切面信息中的增强。

然后,再通过findAdvisorsThatCanApply方法,筛选出「有资格的」(eligible)增强信息,这里应该指的是能够与当前的 Bean 对象匹配的增强。

第三行中调用的extendAdvisors方法,是对获取到的增强列表的一个扩展处理。

最后返回排序后的结果。

createProxy

再大概看一下createProxy方法的源码。

protectedObjectcreateProxy(Class<?>beanClass, @NullableStringbeanName,
@NullableObject[] specificInterceptors, TargetSourcetargetSource) {
if (this.beanFactoryinstanceofConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
   }
ProxyFactoryproxyFactory=newProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
      }
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }
Advisor[] advisors=buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
   }
returnproxyFactory.getProxy(getProxyClassLoader());
}

这里可以看到一个关键的 ProxyFactory 类,以上整个方法中都围绕它展开。首先创建了一个 ProxyFactory 对象,然后对其进行各种各样的配置,最后通过它的getProxy方法创建代理对象。

具体的细节,我们放到之后再深入分析。

题外话:关于切面配置的加载

除了以上的流程,这里还有一个地方需要额外关注一下,那就是在查找切面集合的步骤中,上面提到了一个findCandidateAdvisors方法。

在 Spring 中配置切面有两种方式,分别是 XML 配置和注解配置,虽然切面的工作原理都是一样的,但是获取切面配置的逻辑有所不同。

因此,findCandidateAdvisors方法在 AspectJAwareAdvisorAutoProxyCreator 和 AnnotationAwareAspectJAutoProxyCreator 这两个后处理器的实现类中有不同的实现。AspectJAwareAdvisorAutoProxyCreator 继承了父类中的实现,而 AnnotationAwareAspectJAutoProxyCreator 重写了父类的实现。

对于 XML 配置的切面,需要从 XML 配置中加载切面的配置,而 XML 配置是在 Spring 容器初始化的时候进行的。对于注解配置的切面,则需要加载完用来配置切面的 Bean 对象后读取。因此,对于findCandidateAdvisors方法的原理分析,之后会分两种情况来分析。

总结

本文分析了 Spring 创建 AOP 代理的重要方法wrapIfNecessary以及它为 Bean 对象创建代理对象的主要流程。从下一篇开始,我将围绕这个流程,做深入的原理分析。


目录
相关文章
|
6天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
6天前
|
XML Java 数据格式
Spring使用AOP 的其他方式
Spring使用AOP 的其他方式
15 2
|
6天前
|
XML Java 数据格式
Spring 项目如何使用AOP
Spring 项目如何使用AOP
19 2
|
11天前
|
Java 开发者 Spring
Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
23 5
|
12天前
|
XML Java 数据格式
Spring AOP
【5月更文挑战第1天】Spring AOP
27 5
|
12天前
|
Java 编译器 开发者
Spring的AOP理解
Spring的AOP理解
|
12天前
|
XML Java 数据格式
如何在Spring AOP中定义和应用通知?
【4月更文挑战第30天】如何在Spring AOP中定义和应用通知?
17 0
|
3月前
|
Java 数据库连接 应用服务中间件
Spring5源码(39)-Aop事物管理简介及编程式事物实现
Spring5源码(39)-Aop事物管理简介及编程式事物实现
26 0
|
4月前
AOP&面向切面编程
AOP&面向切面编程
56 0
|
4月前
|
Java 程序员 Maven
Spring AOP入门指南:轻松掌握面向切面编程的基础知识
Spring AOP入门指南:轻松掌握面向切面编程的基础知识