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

简介: 本文分析了用于创建 AOP 代理对象的后处理器,主要是 AbstractAutoProxyCreator 类中的后处理方法。

基于 Spring Framework v5.2.6.RELEASE

概述

前面两篇文章分析了在注解配置和 XML 配置的情况下,Spring 是如何开启 AOP 特性的,经过源码分析我们知道,这两种方式的 AOP 开启,本质上都是注册了一个用来创建 AOP 代理的后处理器。

对于在 XML 中配置切面的情况,Spring 会把创建代理的工作交给 AspectJAwareAdvisorAutoProxyCreator 后处理器来处理,而对于通过注解配置切面的情况,则交由 AnnotationAwareAspectJAutoProxyCreator 后处理器处理。

在本文章,将对这两个后处理器进行分析,了解他们大致的工作原理。

继承关系

AspectJAwareAdvisorAutoProxyCreator 和 AnnotationAwareAspectJAutoProxyCreator 两个后处理器的继承关系,在前两篇文章中已经了解过了,这里我们再做简单的回顾。

image.png

从上图中可以知道,这两个类是直接继承的父子类,他们有很多共同继承的类和实现的接口,可以推测出他们的工作逻辑有很大一部分是相同的。

由于他们是作为后处理器注册到 Spring 容器中的,因此,他们完成 AOP 代理创建的逻辑,一定是在后处理器的方法中。从上图中看到,他们共实现了三个后处理器的接口,接下来,我们再看这三个接口中定义的方法。

实现接口

先看看这三个接口都定义了哪些方法。

image.png

既然后处理器的作用要生成一个代理对象,那么,我们就应该去分析后处理器中可以修改被处理的 Bean 对象的处理方法,再上图中罗列的方法中有以下这些:

  • postProcessBeforeInstantiation方法可以在 Spring 实例化 Bean 对象之前,创建一个 Bean 对象。
  • postProcessBeforeInitialization方法可以在 Bean 的初始化方法被执行前,修改 Bean 对象。
  • postProcessAfterInitialization方法可以在 Bean 的初始化方法被执行前,修改 Bean 对象。

以上三个方法,都可能在执行的过程中,将当前的 Bean 对象,修改为新创建的代理对象。因此,接下来,我们需要从两个后处理器中找到这三个方法的实现逻辑。

不过,在源码中可以发现,这两个后处理器的后处理方法,都是在它们共同的父类 AbstractAutoProxyCreator 中实现的,所以,我们之后只需要分析 AbstractAutoProxyCreator 类中实现的处理方法就行了。并且,postProcessBeforeInitialization方法的实现是一个没有任何处理逻辑的默认实现,我们接下来从postProcessBeforeInstantiationpostProcessAfterInitialization两个方法中分析自动创建代理的逻辑。

代理创建的原理

下面从 AbstractAutoProxyCreator 的源码中,找到这两个方法进行分析。

postProcessBeforeInstantiation

先看postProcessBeforeInstantiation方法

@OverridepublicObjectpostProcessBeforeInstantiation(Class<?>beanClass, StringbeanName) {
ObjectcacheKey=getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) ||!this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
returnnull;
      }
if (isInfrastructureClass(beanClass) ||shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
returnnull;
      }
   }
// Create proxy here if we have a custom TargetSource.// Suppresses unnecessary default instantiation of the target bean:// The TargetSource will handle target instances in a custom fashion.TargetSourcetargetSource=getCustomTargetSource(beanClass, beanName);
if (targetSource!=null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
      }
Object[] specificInterceptors=getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Objectproxy=createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
returnproxy;
   }
returnnull;
}

方法的核心是后半部分的if语句块,在if语句块之前,通过getCustomTargetSource获取了当前 Bean 对象的自定义 TargetSource。TargetSource 指的是,Spring 创建的代理在调用目标方法时候要查找的目标来源,在创建代理对象时,当前的 Bean 对象,会被封装成一个 TargetSource 交给代理类,用于找到被增强的对象本身。

通常情况下,我们不会自定义 TargetSource,因此,这个方法并不是我们要找的创建代理的地方。

postProcessAfterInitialization

接下来,找到postProcessAfterInitialization方法。

@OverridepublicObjectpostProcessAfterInitialization(@NullableObjectbean, StringbeanName) {
if (bean!=null) {
ObjectcacheKey=getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) !=bean) {
returnwrapIfNecessary(bean, beanName, cacheKey);
      }
   }
returnbean;
}

这个方法是在 Bean 实例被执行初始化操作后被调用的,因此方法可以通过参数拿到 Bean 实例对象本身,以及 Bean 的名称。

首先,确保了 Bean 不为空之后,通过getCacheKey方法获得了一个缓存 Key。这个方法在刚刚的postProcessBeforeInstantiation方法中也见过,我们来看一下这个方法。

protectedObjectgetCacheKey(Class<?>beanClass, @NullableStringbeanName) {
if (StringUtils.hasLength(beanName)) {
return (FactoryBean.class.isAssignableFrom(beanClass) ?BeanFactory.FACTORY_BEAN_PREFIX+beanName : beanName);
   }
else {
returnbeanClass;
   }
}

方法中判断了 Bean 的名称是否为空,如果不为空,则根据当前的 Bean 是不是一个 FactoryBean 来返回带或者不带工厂前缀的 Bean 名称,如果 Bean 的名称为空,则直接返回 Bean 得类型本身。

这个方法的目的是获得一个缓存 Key,用于 AbstractAutoProxyCreator 中的缓存容器。

回到postProcessAfterInitialization方法中,接着,从earlyProxyReferences集合中,移除当前 Bean 对象对应的缓存 Key 保存的内容。那这个集合中存放了什么呢?在 AbstractAutoProxyCreator 源码中,除了这里,只有一处操作了earlyProxyReferences集合,而且是put操作,在另外一个方法中。

@OverridepublicObjectgetEarlyBeanReference(Objectbean, StringbeanName) {
ObjectcacheKey=getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
returnwrapIfNecessary(bean, beanName, cacheKey);
}

这其实也是一个后处理器方法,之所以在寻找创建代理逻辑的时候没有考虑这个方法,是因为这个方法是在获取早期 Bean 对象的时候被调用的,也就是说,它不一定会被调用,而且调用的时机也不确定。在方法中,通过缓存 Key 向earlyProxyReferences集合中存放了早期的代理引用,方法最后返回了调用wrapIfNecessary方法的结果,这个方法我们稍后介绍。

再次回到postProcessBeforeInstantiation中,如果从earlyProxyReferences移除的内容,并不是 Bean 本身,则执行wrapIfNecessary方法并将结果作为后处理方法的结果返回。

wrapIfNecessary 方法

到这里,我们就需要看wrapIfNecessary方法,从名字就可以看出,它的作用是在必要的时候,对 Bean 对象进行包装,并且包装好的结果会作为容器中 Bean 的对象。因此,这个方法就是用来创建 Bean 对象代理的方法。

// 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 对象本身。

这部分逻辑,放到之后的文章来分析。

总结

本文分析了用于创建 AOP 代理对象的后处理器,主要是 AbstractAutoProxyCreator 类中的后处理方法。从它实现的后处理器接口中定义的方法中,找到了用于创建 AOP 代理的逻辑是在postProcessBeforeInstantiation方法中,也就是 AOP 代理是 Bean 执行完初始化逻辑之后创建的。

下一篇开始,将深入wrapIfNecessary方法,分析代理对象创建的过程。

目录
相关文章
|
18天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
31 0
|
1天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
8 2
|
3天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
5天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
46 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
12天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
16天前
|
存储 缓存 Java
【spring】06 循环依赖的分析与解决
【spring】06 循环依赖的分析与解决
9 1
|
16天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
1月前
|
安全 Java Spring
Spring之Aop的底层原理
Spring之Aop的底层原理
|
2月前
|
设计模式 Java uml
Spring AOP 原理
Spring AOP 原理
12 0
|
4月前
|
监控 Java Spring
Spring AOP的作用和底层原理、AOP相关术语
Spring AOP的作用和底层原理、AOP相关术语
37 0