Spring 源码阅读 57:配置 ProxyFactory 的 proxyTargetClass 属性
概述
为 Bean 实例创建代理的第一步,是完成 ProxyFactory 的创建和配置,上一篇分析到了 ProxyFactory 通过构造方法创建,并复制了后处理器的一些配置属性。本文接着上一篇,分析在创建 AOP 代理对象之前,还对 ProxyFactory 进行了哪些配置操作。
配置 ProxyFactory 的 proxyTargetClass 属性
再次回到createProxy方法中。

在copyFrom方法之后,进入了一个if语句块。
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
proxyTargetClass 属性
最外层的判断条件是proxyFactory的proxyTargetClass属性,这个属性的的默认值是false,但是通过上一步调用的copyFrom方法,这个值已经被后处理器的相同属性的值覆盖了,而后处理器的proxyTargetClass属性值,取决于我们开启 Spring AOP 特性的时候指定的值。
通过注解开启时,在注解属性中配置:
@EnableAspectJAutoProxy(proxyTargetClass = true)
如果是通过 XML 开启 AOP 特性,则在 XML 标签中配置:
<aop:aspectj-autoproxy proxy-target-class="true" />
如果在开启 AOP 特性是没有配置,则默认值是false。proxyTargetClass属性是用来配置是否代理目标类,简而言之就是是否所有的代理对象都通过 CGLIB 的方式来创建,在之后的流程中,Spring 会根据 Bean 实例来判断是采用 JDK 动态代理的方式创建代理对象,还是通过 CGLIB 的方式创建代理对象,如果proxyTargetClass属性配置为true,则全部采用 CGLIB 的方式。
因此,这里的if语句,通过判断proxyTargetClass的值来决定是直接采用 CGLIB 的方式,还是做进一步判断。假设proxyTargetClass的值是false,那么,则会进入if语句块中的逻辑。进入之后还有一个if判断,判断条件是shouldProxyTargetClass方法的结果。
shouldProxyTargetClass 方法
我们进入shouldProxyTargetClass方法。
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#shouldProxyTargetClass
protected boolean shouldProxyTargetClass(Class<?> beanClass, @Nullable String beanName) {
return (this.beanFactory instanceof ConfigurableListableBeanFactory &&
AutoProxyUtils.shouldProxyTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName));
}
这里需要在进入 AutoProxyUtils 的同名方法。
// org.springframework.aop.framework.autoproxy.AutoProxyUtils#shouldProxyTargetClass
public static boolean shouldProxyTargetClass(
ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) {
if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
return Boolean.TRUE.equals(bd.getAttribute(PRESERVE_TARGET_CLASS_ATTRIBUTE));
}
return false;
}
这里的逻辑是从 BeanDefinition 上获取对应的属性preserveTargetClass,如果它是true,则返回true。它配置的还是是否代理目标类。
如果shouldProxyTargetClass方法结果是true的话,则将proxyFactory的proxyTargetClass属性设置为true,表示创建代理的时候使用 CGLIB 的方式。否则,会执行else语句块中的evaluateProxyInterfaces方法。
evaluateProxyInterfaces 方法
查看这个方法的源码。
// org.springframework.aop.framework.ProxyProcessorSupport#evaluateProxyInterfaces
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
boolean hasReasonableProxyInterface = 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);
}
}
其实还是在判断是否需要将proxyFactory的proxyTargetClass设置为true。
方法体中,首先会获取到当前 Bean 类型所有的接口targetInterfaces。然后,声明变量hasReasonableProxyInterface用来标记是否有合适的接口,因为 JDK 动态代理是基于接口的代理,因此,这个标记的目的是为了判断是否可以使用 JDK 动态代理。
在接下来的for循环中,遍历了targetInterfaces中所有的接口,并通过三个条件,来判断一个接口是不是适合作为 JDK 动态代理实现的接口。
第一个判断条件通过isConfigurationCallbackInterface方法来判断。
// org.springframework.aop.framework.ProxyProcessorSupport#isConfigurationCallbackInterface
protected boolean isConfigurationCallbackInterface(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#isInternalLanguageInterface
protected boolean isInternalLanguageInterface(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添加到proxyFactory的interfaces集合中。
如果遍历完所有的接口之后hasReasonableProxyInterface仍然为false,那么说明没有合适的接口用于创建 JDK 动态代理,则将proxyFactory的proxyTargetClass设置为true。
至此,以上的这部分就完成了proxyTargetClass属性的配置。
总结
本文介绍了createProxy方法中,为 ProxyFactory 工厂对象配置proxyTargetClass属性的原理,Spring 会根据后处理器的配置以及 Bean 实例实现的接口,判断是否具备通过 JDK 动态代理给 Bean 实例创建 AOP 代理的条件,并给proxyTargetClass属性配置相应的值。
下一篇将继续介绍后面的流程。
转载自:https://juejin.cn/post/7157741829130059783