spring源码系列8:AOP源码解析之代理的创建(下)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: spring源码系列8:AOP源码解析之代理的创建(下)
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
        return wrapIfNecessary(bean, beanName, cacheKey);
      }
    }
    return bean;
  }

关键便在于wrapIfNecessary方法: 名字 wrap if Necessary  如果满足条件包装这个方法就可以拆成两部分看:

  1. 满足条件:
  2. 包装:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //1.自定义TargetSource,已经进行过代理子类生成 。 不包装直接返回Bean实例
    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
    }
    //2.已经判定不需要代理的, 不代理
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
    }
    //3.isInfrastructureClass(bean.getClass())是基础设施类的不代理
    //4.shouldSkip(bean.getClass(), beanName)应该跳过的不代理
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
    }
    // Create proxy if we have advice.
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      Object proxy = createProxy(
          bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
    }
    //5.没有具体拦截器的不代理
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
  }

我们挨个分析下这两部分


(2.4.1) 满足条件

不代理的情况

  • 自定义TargetSource,已经进行过代理子类生成 。 不包装直接返回Bean实例。()
  • 已经判定不需要代理的, 不代理。 直接返回Bean实例
  • isInfrastructureClass(bean.getClass())是基础设施类的不代理。 (上文已经提过)
  • shouldSkip(bean.getClass(), beanName)应该跳过的不代理 (上文已经提过)
  • 没有具体拦截器的不代理

代理的的情况

  • 有具体Advice的才代理:getAdvicesAndAdvisorsForBean() 的返回不为空的。(Create proxy if we have advice.)

Advisor寻找:重点就落在了此处。

即getAdvicesAndAdvisorsForBean方法,这里进行的便是去容器中寻找适用于当前bean的Advisor,最终调用的是

AbstractAdvisorAutoProxyCreator.findEligibleAdvisors:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
  }
  • findCandidateAdvisors: 获取所有Advisor ,前面已经说过。 因为做了缓存此处直接从缓存中取。
  • findAdvisorsThatCanApply: 看其传入的参数,candidateAdvisors(所有的候选Advisor), 也就是这个方法肯定就是从所有的候选Advisor找出适合当前Bean
  • extendAdvisors: 允许子类添加advisor

适用性判断findAdvisorsThatCanApply最终调用AopUtils.findAdvisorsThatCanApply:

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
      return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
    //第一遍
    for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
        eligibleAdvisors.add(candidate);
      }
    }
    //第一遍
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor) {
        // already processed
        continue;
      }
      if (canApply(candidate, clazz, hasIntroductions)) {
        eligibleAdvisors.add(candidate);
      }
    }
    return eligibleAdvisors;
  }

此方法有两个for循环。 第一个for循环寻找IntroductionAdvisor(引介增强)类型的advisor,调用AopUtils.canApply 第二遍for循环寻找普通的advisor,调用AopUtils.canApply

AopUtils.canApply针对两种类型的Advisor做了不同的判断:

  • 针对IntroductionAdvisor类型advisor的,只需要校验Advisor的ClassFilter是否匹配当前类
  • 针对普通的advisor:(1)首先查看定的类是否在Pointcut的匹配范围内;(2)是的话,再查看是否能匹配此类任意方法,是的话返回true;(3)不能匹配任意方法,便会用反射的方法获取targetClass(被检测类)的全部方法逐一交由Pointcut的MethodMatcher.matches方法进行检测。
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    if (!pc.getClassFilter().matches(targetClass)) {
      return false;
    }
    //是否配置任意方法
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
      // No need to iterate the methods if we're matching any method anyway...
      return true;
    }
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
      introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }
    //逐一排查
    Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    classes.add(targetClass);
    for (Class<?> clazz : classes) {
      Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
      for (Method method : methods) {
        if ((introductionAwareMethodMatcher != null &&
            introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
            methodMatcher.matches(method, targetClass)) {
          return true;
        }
      }
    }
    return false;
  }

到此,AopUtils.canApply返回true后。findAdvisorsThatCanApply()算是找到了能应用于当前类的Advisors. 再extendAdvisors后;对,应用于当前类的Advisors一番排序后,getAdvicesAndAdvisorsForBean工作完成。

getAdvicesAndAdvisorsForBean()返回如果不为Null。那下面就是包装。


(2.4.2) 包装在getAdvicesAndAdvisorsForBean返回advisors不为null后,可以创建代理。

protected Object createProxy(
      Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);
    if (!proxyFactory.isProxyTargetClass()) {
      if (shouldProxyTargetClass(beanClass, beanName)) {
        proxyFactory.setProxyTargetClass(true);
      }
      else {
        evaluateProxyInterfaces(beanClass, proxyFactory);
      }
    }
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);
    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
    }
    return proxyFactory.getProxy(getProxyClassLoader());
  }
  • 首先创建ProxyFactory 工厂,从AbstractAutoProxyCreator复制代理属性,因为AbstractAutoProxyCreator继承了ProxyConfig,所以本身也是一个代理配置类。
  • 判断其是基于类代理, 还是基于接口代理。如果基于接口代理,获取接口,并设置到ProxyFactory 。
  • buildAdvisors()方法,整合getAdvicesAndAdvisorsForBean找的advisors 与AbstractAutoProxyCreator. interceptorNames属性可能设置的增强器,统一包装成Advisor类型数组,设置到ProxyFactory 。并且AbstractAutoProxyCreator. interceptorNames属性设置的增强器在前。
  • customizeProxyFactory(proxyFactory); 子类可以继续处理ProxyFactory
  • 设置ProxyFactory的Frozen属性
  • setPreFiltered设置ProxyFactory 的preFiltered
  • getProxy 返回代理对象

我们可以看出,这里两大部分:

(1)创建proxyFactory工厂,并配置工厂

(2)proxyFactory.getProxy(getProxyClassLoader())返回代理对象。

proxyFactory.getProxy(ClassLoader classLoader)

proxyFactory.getProxy(ClassLoader classLoader) 首先会先创建一个默认的策略工厂DefaultAopProxyFactory。DefaultAopProxyFactory 会根据proxyFactory是基于接口的代理还是基于类的代理,选择创建JdkDynamicAopProxy对象,或者创建一个CglibAopProxy对象

这里有必要介绍下DefaultAopProxyFactory.createAopProxy方法,此方法用来判断是JDK代理,还是CGLB代理。

if (config.isOptimize() //是否对代理类的生成使用策略优化
    || config.isProxyTargetClass() //设置proxy-target-class="true
    || hasNoUserSuppliedProxyInterfaces(config)) {//目标类是否有接口存在 且只有一个接口的时候接口类型不是
        //SpringProxy类型 
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
        throw new AopConfigException("TargetSource cannot determine target class: " +
            "Either an interface or a target is required for proxy creation.");
      }
      //判断目标类是否是接口 如果目标类是接口的话,则还是使用JDK的方式生成代理对象
            //如果目标类是Proxy类型 则还是使用JDK的方式生成代理对象
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
        return new JdkDynamicAopProxy(config);
      }
      //CGLB代理。
      return new ObjenesisCglibAopProxy(config);
    }
    else {
        //JDK代理
      return new JdkDynamicAopProxy(config);
    }

我们可以看出高版本的会根据设置和目标类的实际情况选择使用CGLB或者JDK代理。也就是说,设置了proxy-target-class="true"不一定就是使用CGLB,而是根据实际情况定。具体版本具体对待。

JdkDynamicAopProxy.getProxy()或者CglibAopProxygetProxy()才是真正返回代理对象。


再次回顾JDK动态代理与CGLIB动态代理

JdkDynamicAopProxy.getProxy()

@Override
  public Object getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
      logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
  }

我们看到JDK动态代理的两个要素: Proxy+InvocationHandlerInvocationHandler 此时就是JdkDynamicAopProxy,同时JdkDynamicAopProxy封装了advised。这样完美的把advised与JDK动态代理联系在了一起

接下来就是在内存中生成一个字节码JDK代理类$Proxy66.class,生成真正的代理对象了。

CglibAopProxygetProxy()(省略部分源码,直奔主题)

// Configure CGLIB Enhancer...
      Enhancer enhancer = createEnhancer();
      Callback[] callbacks = getCallbacks(rootClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      return createProxyClassAndInstance(enhancer, callbacks);
    }

我们看到了Cglb熟悉的要素Enhancer 。MethodInterceptor去哪了呢?

跟进getCallbacks()方法,会发现此方法会创建一个DynamicAdvisedInterceptor。DynamicAdvisedInterceptor实现了MethodInterceptor,并封装了advisors。

Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

这样,CGLB动态代理完美的与advised结合在了一起。 接下来就是在内存中生成一个新的字节码CGLB代理类***?FastClassByCGLIB?29e52466,并生成真实代理对象了。


总结


springaop 底层还是JDK动态代理,CGLB动态代理。通过把增强器封装到Advised中,把Advised与InvocationHandler或者MethodInterceptor联系起来,完美的实现AOP技术


相关文章
|
27天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
27天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
27天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
3天前
|
自然语言处理 数据处理 索引
mindspeed-llm源码解析(一)preprocess_data
mindspeed-llm是昇腾模型套件代码仓,原来叫"modelLink"。这篇文章带大家阅读一下数据处理脚本preprocess_data.py(基于1.0.0分支),数据处理是模型训练的第一步,经常会用到。
15 0
|
28天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
|
4月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
96 1
|
2月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
296 1
什么是AOP面向切面编程?怎么简单理解?
|
2月前
|
XML Java 开发者
论面向方面的编程技术及其应用(AOP)
【11月更文挑战第2天】随着软件系统的规模和复杂度不断增加,传统的面向过程编程和面向对象编程(OOP)在应对横切关注点(如日志记录、事务管理、安全性检查等)时显得力不从心。面向方面的编程(Aspect-Oriented Programming,简称AOP)作为一种新的编程范式,通过将横切关注点与业务逻辑分离,提高了代码的可维护性、可重用性和可读性。本文首先概述了AOP的基本概念和技术原理,然后结合一个实际项目,详细阐述了在项目实践中使用AOP技术开发的具体步骤,最后分析了使用AOP的原因、开发过程中存在的问题及所使用的技术带来的实际应用效果。
80 5
|
4月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
【9月更文挑战第9天】AOP(面向切面编程)通过分离横切关注点提高模块化程度,如日志记录、事务管理等。Micronaut AOP基于动态代理机制,在应用启动时为带有特定注解的类生成代理对象,实现在运行时拦截方法调用并执行额外逻辑。通过简单示例展示了如何在不修改 `CalculatorService` 类的情况下记录 `add` 方法的参数和结果,仅需添加 `@Loggable` 注解即可。这不仅提高了代码的可维护性和可扩展性,还降低了引入新错误的风险。
56 13
|
3月前
|
Java 容器
AOP面向切面编程
AOP面向切面编程
54 0

推荐镜像

更多