likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 15

概述

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

配置 ProxyFactory 的 proxyTargetClass 属性

再次回到createProxy方法中。

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

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-autoproxy proxy-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#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的话,则将proxyFactoryproxyTargetClass属性设置为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);
   }
}

其实还是在判断是否需要将proxyFactoryproxyTargetClass设置为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添加到proxyFactoryinterfaces集合中。

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

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

总结

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

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