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