Spring 源码阅读 57:配置 ProxyFactory 的 proxyTargetClass 属性

简介: 本文介绍了createProxy方法中,为 ProxyFactory 工厂对象配置proxyTargetClass属性的原理,Spring 会根据后处理器的配置以及 Bean 实例实现的接口,判断是否具备通过 JDK 动态代理给 Bean 实例创建 AOP 代理的条件,并给proxyTargetClass属性配置相应的值。

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 56:创建 ProxyFactory

概述

为 Bean 实例创建代理的第一步,是完成 ProxyFactory 的创建和配置,上一篇分析到了 ProxyFactory 通过构造方法创建,并复制了后处理器的一些配置属性。本文接着上一篇,分析在创建 AOP 代理对象之前,还对 ProxyFactory 进行了哪些配置操作。

配置 ProxyFactory 的 proxyTargetClass 属性

再次回到createProxy方法中。

image.png

copyFrom方法之后,进入了一个if语句块。

if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
   }
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
   }
}

proxyTargetClass 属性

最外层的判断条件是proxyFactoryproxyTargetClass属性,这个属性的的默认值是false,但是通过上一步调用的copyFrom方法,这个值已经被后处理器的相同属性的值覆盖了,而后处理器的proxyTargetClass属性值,取决于我们开启 Spring AOP 特性的时候指定的值。

通过注解开启时,在注解属性中配置:

@EnableAspectJAutoProxy(proxyTargetClass=true)

如果是通过 XML 开启 AOP 特性,则在 XML 标签中配置:

<aop:aspectj-autoproxyproxy-target-class="true"/>

如果在开启 AOP 特性是没有配置,则默认值是falseproxyTargetClass属性是用来配置是否代理目标类,简而言之就是是否所有的代理对象都通过 CGLIB 的方式来创建,在之后的流程中,Spring 会根据 Bean 实例来判断是采用 JDK 动态代理的方式创建代理对象,还是通过 CGLIB 的方式创建代理对象,如果proxyTargetClass属性配置为true,则全部采用 CGLIB 的方式。

因此,这里的if语句,通过判断proxyTargetClass的值来决定是直接采用 CGLIB 的方式,还是做进一步判断。假设proxyTargetClass的值是false,那么,则会进入if语句块中的逻辑。进入之后还有一个if判断,判断条件是shouldProxyTargetClass方法的结果。

shouldProxyTargetClass 方法

我们进入shouldProxyTargetClass方法。

// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#shouldProxyTargetClassprotectedbooleanshouldProxyTargetClass(Class<?>beanClass, @NullableStringbeanName) {
return (this.beanFactoryinstanceofConfigurableListableBeanFactory&&AutoProxyUtils.shouldProxyTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName));
}

这里需要在进入 AutoProxyUtils 的同名方法。

// org.springframework.aop.framework.autoproxy.AutoProxyUtils#shouldProxyTargetClasspublicstaticbooleanshouldProxyTargetClass(
ConfigurableListableBeanFactorybeanFactory, @NullableStringbeanName) {
if (beanName!=null&&beanFactory.containsBeanDefinition(beanName)) {
BeanDefinitionbd=beanFactory.getBeanDefinition(beanName);
returnBoolean.TRUE.equals(bd.getAttribute(PRESERVE_TARGET_CLASS_ATTRIBUTE));
   }
returnfalse;
}

这里的逻辑是从 BeanDefinition 上获取对应的属性preserveTargetClass,如果它是true,则返回true。它配置的还是是否代理目标类。

如果shouldProxyTargetClass方法结果是true的话,则将proxyFactoryproxyTargetClass属性设置为true,表示创建代理的时候使用 CGLIB 的方式。否则,会执行else语句块中的evaluateProxyInterfaces方法。

evaluateProxyInterfaces 方法

查看这个方法的源码。

// org.springframework.aop.framework.ProxyProcessorSupport#evaluateProxyInterfacesprotectedvoidevaluateProxyInterfaces(Class<?>beanClass, ProxyFactoryproxyFactory) {
Class<?>[] targetInterfaces=ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
booleanhasReasonableProxyInterface=false;
for (Class<?>ifc : targetInterfaces) {
if (!isConfigurationCallbackInterface(ifc) &&!isInternalLanguageInterface(ifc) &&ifc.getMethods().length>0) {
hasReasonableProxyInterface=true;
break;
      }
   }
if (hasReasonableProxyInterface) {
// Must allow for introductions; can't just set interfaces to the target's interfaces only.for (Class<?>ifc : targetInterfaces) {
proxyFactory.addInterface(ifc);
      }
   }
else {
proxyFactory.setProxyTargetClass(true);
   }
}

其实还是在判断是否需要将proxyFactoryproxyTargetClass设置为true

方法体中,首先会获取到当前 Bean 类型所有的接口targetInterfaces。然后,声明变量hasReasonableProxyInterface用来标记是否有合适的接口,因为 JDK 动态代理是基于接口的代理,因此,这个标记的目的是为了判断是否可以使用 JDK 动态代理。

在接下来的for循环中,遍历了targetInterfaces中所有的接口,并通过三个条件,来判断一个接口是不是适合作为 JDK 动态代理实现的接口。

第一个判断条件通过isConfigurationCallbackInterface方法来判断。

// org.springframework.aop.framework.ProxyProcessorSupport#isConfigurationCallbackInterfaceprotectedbooleanisConfigurationCallbackInterface(Class<?>ifc) {
return (InitializingBean.class==ifc||DisposableBean.class==ifc||Closeable.class==ifc||AutoCloseable.class==ifc||ObjectUtils.containsElement(ifc.getInterfaces(), Aware.class));
}

这些接口不是一个创建 JDK 动态代理的合适的接口。

第二个判断条件通过isInternalLanguageInterface方法来判断。

// org.springframework.aop.framework.ProxyProcessorSupport#isInternalLanguageInterfaceprotectedbooleanisInternalLanguageInterface(Class<?>ifc) {
return (ifc.getName().equals("groovy.lang.GroovyObject") ||ifc.getName().endsWith(".cglib.proxy.Factory") ||ifc.getName().endsWith(".bytebuddy.MockAccess"));
}

接口名称符合这些条件的接口,属于内部语言接口,也不适合用于创建 JDK 动态代理。

第三个判断条件就是,接口中如果没有声明的方法,也不适合用于创建 JDK 动态代理。

targetInterfaces中,有任何一个接口同时符合这三个条件,hasReasonableProxyInterface都会被赋值true。然后,将targetInterfaces添加到proxyFactoryinterfaces集合中。

如果遍历完所有的接口之后hasReasonableProxyInterface仍然为false,那么说明没有合适的接口用于创建 JDK 动态代理,则将proxyFactoryproxyTargetClass设置为true

至此,以上的这部分就完成了proxyTargetClass属性的配置。

总结

本文介绍了createProxy方法中,为 ProxyFactory 工厂对象配置proxyTargetClass属性的原理,Spring 会根据后处理器的配置以及 Bean 实例实现的接口,判断是否具备通过 JDK 动态代理给 Bean 实例创建 AOP 代理的条件,并给proxyTargetClass属性配置相应的值。

下一篇将继续介绍后面的流程。

目录
相关文章
|
1天前
|
Java uml Spring
手写spring第四章-完善bean实例化,自动填充成员属性
手写spring第四章-完善bean实例化,自动填充成员属性
8 0
|
2天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
8天前
|
存储 安全 Java
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
16 0
|
8天前
|
安全 Java 数据库
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(上)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)
34 0
|
9天前
|
安全 Java Spring
Spring Security 5.7 最新配置细节(直接就能用),WebSecurityConfigurerAdapter 已废弃
Spring Security 5.7 最新配置细节(直接就能用),WebSecurityConfigurerAdapter 已废弃
21 0
|
9天前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
25 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
|
9天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
15天前
|
JSON Java 数据库连接
属性注入掌握:Spring Boot配置属性的高级技巧与最佳实践
属性注入掌握:Spring Boot配置属性的高级技巧与最佳实践
23 1
|
15天前
|
Java 数据库连接 Spring
简化配置,提高灵活性:Spring中的参数化配置技巧
简化配置,提高灵活性:Spring中的参数化配置技巧
20 0
|
30天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
43 0