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属性配置相应的值。

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

目录
相关文章
|
3天前
|
安全 Java 开发者
深入理解Spring Boot配置绑定及其实战应用
【4月更文挑战第10天】本文详细探讨了Spring Boot中配置绑定的核心概念,并结合实战示例,展示了如何在项目中有效地使用这些技术来管理和绑定配置属性。
10 1
|
23小时前
|
监控 Java 应用服务中间件
Spring Boot 源码面试知识点
【5月更文挑战第12天】Spring Boot 是一个强大且广泛使用的框架,旨在简化 Spring 应用程序的开发过程。深入了解 Spring Boot 的源码,有助于开发者更好地使用和定制这个框架。以下是一些关键的知识点:
12 6
|
2天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
11 3
|
2天前
|
消息中间件 开发框架 Java
什么是Spring Boot 自动配置?
Spring Boot 是一个流行的 Java 开发框架,它提供了许多便利的功能和工具,帮助开发者快速构建应用程序。其中一个最引人注目的特性是其强大的自动配置功能。
6 0
|
4天前
|
Java Spring
Spring文件配置以及获取
Spring文件配置以及获取
11 0
|
4天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
6天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
47 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
6天前
|
XML Java 数据格式
Spring 属性注入方式
Spring 属性注入方式
13 2
|
7天前
|
Java 数据库连接 数据库
Spring事务简介,事务角色,事务属性
Spring事务简介,事务角色,事务属性
16 2
|
7天前
|
Java Apache Spring
Spring BeanUtils与Apache BeanUtils提供基本属性复制,适用于简单需求
Spring BeanUtils与Apache BeanUtils提供基本属性复制,适用于简单需求;Cglib BeanCopier用于转换为Cglib代理对象;Apache PropertyUtils处理属性操作;Dozer支持复杂对象映射。选择工具取决于具体需求,如需精细控制或对象映射,推荐Dozer或Apache PropertyUtils。Apache BeanUtils可能因潜在的封装性破坏被禁用。
20 3