AOP源码解析二 代理 属性解读 源码解析

1,122 阅读4分钟

前言

会更新AOP 系列源码文章 主要更新目录

图片.png

篇幅问题 会分章程更新

创建代理

AbstractAutoProxyCreator.createProxy():根据增强方法创建代理对象

  • ProxyFactory proxyFactory = new ProxyFactory()无参构造 ProxyFactory,此处讲解一下两种有参构造方法:

    • public ProxyFactory(Object target):

      public ProxyFactory(Object target) {
      	// 将目标对象封装成 SingletonTargetSource 保存到父类的字段中
         	setTarget(target);
          // 获取目标对象 class 所有接口保存到 AdvisedSupport 中的 interfaces 集合中
         	setInterfaces(ClassUtils.getAllInterfaces(target));
      }
      

      ClassUtils.getAllInterfaces(target) 底层调用 getAllInterfacesForClassAsSet(java.lang.Class<?>, java.lang.ClassLoader):

      • if (clazz.isInterface() && isVisible(clazz, classLoader))

        • 条件一:判断当前目标对象是接口
        • 条件二:检查给定的类在给定的 ClassLoader 中是否可见
      • Class<?>[] ifcs = current.getInterfaces():拿到自己实现的接口,拿不到接口实现的接口

      • current = current.getSuperclass():递归寻找父类的接口,去获取父类实现的接口

    • public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor):

      public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {
          // 添加一个代理的接口
          addInterface(proxyInterface);
          // 添加通知,底层调用 addAdvisor
          addAdvice(interceptor);
      }
      
      • addAdvisor(pos, new DefaultPointcutAdvisor(advice)):Spring 中 Advice 对应的接口就是 Advisor,Spring 使用 Advisor 包装 Advice 实例
  • proxyFactory.copyFrom(this):填充一些信息到 proxyFactory

  • if (!proxyFactory.isProxyTargetClass()):条件成立说明 proxyTargetClass 为 false(默认),两种配置方法:

    • <aop:aspectj-autoproxy proxy-target-class="true"/> :强制使用 CGLIB
    • @EnableAspectJAutoProxy(proxyTargetClass = true)

    if (shouldProxyTargetClass(beanClass, beanName)):如果 bd 内有 preserveTargetClass = true ,那么这个 bd 对应的 class 创建代理时必须使用 CGLIB,条件成立设置 proxyTargetClass 为 true

    evaluateProxyInterfaces(beanClass, proxyFactory)根据目标类判定是否可以使用 JDK 动态代理

    • targetInterfaces = ClassUtils.getAllInterfacesForClass():获取当前目标对象 class 和父类的全部实现接口

    • boolean hasReasonableProxyInterface = false:实现的接口中是否有一个合理的接口

    • if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) && ifc.getMethods().length > 0):遍历所有的接口,如果有任意一个接口满足条件,设置 hRPI 变量为 true

      • 条件一:判断当前接口是否是 Spring 生命周期内会回调的接口
      • 条件二:接口不能是 GroovyObject、Factory、MockAccess 类型的
      • 条件三:找到一个可以使用的被代理的接口
    • if (hasReasonableProxyInterface)有合理的接口,将这些接口设置到 proxyFactory 内

    • proxyFactory.setProxyTargetClass(true)没有合理的代理接口,强制使用 CGLIB 创建对象

  • advisors = buildAdvisors(beanName, specificInterceptors):匹配目标对象 clazz 的 Advisors,填充至 ProxyFactory

  • proxyFactory.setPreFiltered(true):设置为 true 表示传递给 proxyFactory 的 Advisors 信息做过基础类和方法的匹配

  • return proxyFactory.getProxy(getProxyClassLoader()):创建代理对象

    public Object getProxy() {
        return createAopProxy().getProxy();
    }
    

    DefaultAopProxyFactory.createAopProxy(AdvisedSupport config):参数是一个配置对象,保存着创建代理需要的生产资料,会加锁创建,保证线程安全

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 条件二为 true 代表强制使用 CGLIB 动态代理
        if (config.isOptimize() || config.isProxyTargetClass() || 
            // 条件三:被代理对象没有实现任何接口或者只实现了 SpringProxy 接口,只能使用 CGLIB 动态代理
            hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("");
            }
            // 条件成立说明 target 【是接口或者是已经被代理过的类型】,只能使用 JDK 动态代理
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);	// 使用 JDK 动态代理
            }
            return new ObjenesisCglibAopProxy(config);	// 使用 CGLIB 动态代理
        }
        else {
            return new JdkDynamicAopProxy(config);		// 【有接口的情况下只能使用 JDK 动态代理】
        }
    }
    

    JdkDynamicAopProxy.getProxy(java.lang.ClassLoader):获取 JDK 的代理对象

      public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
          // 配置类封装到 JdkDynamicAopProxy.advised 属性中
          this.advised = config;
      }
      public Object getProxy(@Nullable ClassLoader classLoader) {
          // 获取需要代理的接口数组
          Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
          
          // 查找当前所有的需要代理的接口,看是否有 equals 方法和 hashcode 方法,如果有就做一个标记
          findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
          
          // 该方法最终返回一个代理类对象
          return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
          // classLoader:类加载器  proxiedInterfaces:生成的代理类,需要实现的接口集合
          // this JdkDynamicAopProxy 实现了 InvocationHandler
      }
    

    AopProxyUtils.completeProxiedInterfaces(this.advised, true):获取代理的接口数组,并添加 SpringProxy 接口

    • specifiedInterfaces = advised.getProxiedInterfaces():从 ProxyFactory 中拿到所有的 target 提取出来的接口

      • if (specifiedInterfaces.length == 0):如果没有实现接口,检查当前 target 是不是接口或者已经是代理类,封装到 ProxyFactory 的 interfaces 集合中
    • addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class):判断目标对象所有接口中是否有 SpringProxy 接口,没有的话需要添加,这个接口标识这个代理类型是 Spring 管理的

      • addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class):判断目标对象的所有接口,是否已经有 Advised 接口
      • addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class)):判断目标对象的所有接口,是否已经有 DecoratingProxy 接口
      • int nonUserIfcCount = 0:非用户自定义的接口数量,接下来要添加上面的三个接口了
      • proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount]:创建一个新的 class 数组,长度是原目标对象提取出来的接口数量和 Spring 追加的数量,然后进行 System.arraycopy 拷贝到新数组中
      • int index = specifiedInterfaces.length:获取原目标对象提取出来的接口数量,当作 index
      • if(addSpringProxy):根据上面三个布尔值把接口添加到新数组中
      • return proxiedInterfaces:返回追加后的接口集合

    JdkDynamicAopProxy.findDefinedEqualsAndHashCodeMethods():查找在任何定义在接口中的 equals 和 hashCode 方法

    • for (Class<?> proxiedInterface : proxiedInterfaces):遍历所有的接口

      • Method[] methods = proxiedInterface.getDeclaredMethods():获取接口中的所有方法

      • for (Method method : methods):遍历所有的方法

        • if (AopUtils.isEqualsMethod(method)):当前方法是 equals 方法,把 equalsDefined 置为 true
        • if (AopUtils.isHashCodeMethod(method)):当前方法是 hashCode 方法,把 hashCodeDefined 置为 true
      • if (this.equalsDefined && this.hashCodeDefined):如果有一个接口中有这两种方法,直接返回

本文正在参加「金石计划 . 瓜分6万现金大奖」