【小家Spring】AbstractBeanFactory#getBean()、doGetBean完成Bean的初始化、实例化,以及BeanPostProcessor后置处理器源码级详细分析(下)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 【小家Spring】AbstractBeanFactory#getBean()、doGetBean完成Bean的初始化、实例化,以及BeanPostProcessor后置处理器源码级详细分析(下)

ConstructorResolver#autowireConstructor


这个方法作用是获取被包装后的bean,包装后的对象是BeanWrapper对象,这个对象的实现类是BeanWrapperImpl。其中包含被封装后待处理的bean,和设置bean属性的属性编辑器。


  protected BeanWrapper autowireConstructor(
      String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
    // 此处的this,就是DefaultListableBeanFactory嘛
    return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
  }
// ConstructorResolver#autowireConstructor
  public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,
      @Nullable Constructor<?>[] chosenCtors, @Nullable final Object[] explicitArgs) {
    //先实例化一个BeanWrapperImpl类对象
    BeanWrapperImpl bw = new BeanWrapperImpl();
    // initBeanWrapper做了一些事,比如注册解析器、value解析器等等  这是个比较大的概念,后面会有专题再说吧
    this.beanFactory.initBeanWrapper(bw);
    Constructor<?> constructorToUse = null;
    ArgumentsHolder argsHolderToUse = null;
    Object[] argsToUse = null;
     //如果构造参数不为空就直接使用这些参数即可
    if (explicitArgs != null) {
      argsToUse = explicitArgs;
    }
    // 否则构造函数的入参,交给Spring处理。它会去容器里拿~~~~~
    else {
      Object[] argsToResolve = null;
      synchronized (mbd.constructorArgumentLock) {
        //获取已缓存解析的构造函数或工厂方法(resolvedConstructorOrFactoryMethod----用于缓存已解析的构造函数或工厂方法)
                constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
        如果缓存不为空,并且构造参数已经解析缓存了,(constructorArgumentsResolved为包可见,用于表示构造参数状态是否已经解析)
        // 显然首次进来,都是为null并且没有被解析的
        constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
        if (constructorToUse != null && mbd.constructorArgumentsResolved) {
          // Found a cached constructor...
          argsToUse = mbd.resolvedConstructorArguments;
          if (argsToUse == null) {
            argsToResolve = mbd.preparedConstructorArguments;
          }
        }
      }
      // 如果上面没有解析过,显然这里参数就是null了,argsToUse也就还为null Spring下面继续解析
      if (argsToResolve != null) {
        argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
      }
    }
    //如果缓存的构造器不存在,就说明没有bean进行过解析,需要去关联对应的bean的构造器
    if (constructorToUse == null) {
      // Need to resolve the constructor.
      // 我们的传值chosenCtors 显然不为null,所以此值为true
      boolean autowiring = (chosenCtors != null ||
          mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
      ConstructorArgumentValues resolvedValues = null;
      int minNrOfArgs;
      //若传入的构造参数不为空,那最小参数长度一塔为准 
      if (explicitArgs != null) {
        minNrOfArgs = explicitArgs.length;
      }
      else {
        // 这里相当于要解析出构造函数的参数了
        //解析对应的构造参数然后添加到ConstructorArgumentValues中
        ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
        resolvedValues = new ConstructorArgumentValues();
        minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
      }
      // Take specified constructors, if any.
      //如果传入的构造器为空,则获取bean的Class对象,然后根据bean是不是public修饰的来按照不同的方式获取所有的构造器
      // 显然,我们这里(大都都会有构造器)
      // 但是此处我们发现,即使构造器不是public的,这里也能够遭到构造器来进行实例化
      Constructor<?>[] candidates = chosenCtors;
      if (candidates == null) {
        Class<?> beanClass = mbd.getBeanClass();
        try {
          //getDeclaredConstructors返回所有的构造器(包括public和private修饰的),getConstructors返回public修饰的
          candidates = (mbd.isNonPublicAccessAllowed() ? beanClass.getDeclaredConstructors() : beanClass.getConstructors());
        } catch (Throwable ex) {
          throw new BeanCreationException(mbd.getResourceDescription(), beanName,
              "Resolution of declared constructors on bean Class [" + beanClass.getName() +
              "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
        }
      }
      // 这个构造器排序有点意思
      //按照访问方式和数量对构造器进行排序;public>protect>private,在同为public时构造器入参多的排在前面
      // 所以排在第一位的,是public的,参数最多的构造器
      AutowireUtils.sortConstructors(candidates);
      int minTypeDiffWeight = Integer.MAX_VALUE;
      // 记录下,引起歧义的构造器们。就是记录下来,如果存在这种歧义,抛异常的时候用来告诉调用者
      Set<Constructor<?>> ambiguousConstructors = null;
      LinkedList<UnsatisfiedDependencyException> causes = null;
      // 开始遍历排序后的构造器了==========================
      for (Constructor<?> candidate : candidates) {
        // 拿到构造器参数的类型们
        Class<?>[] paramTypes = candidate.getParameterTypes();
        // constructorToUse不为null(表示已经找到了合适构造器),但是呢,连参数个数的长度都对应不上,那就直接break,后面的构造器全都不用看了
        if (constructorToUse != null && argsToUse.length > paramTypes.length) {
          // Already found greedy constructor that can be satisfied ->
          // do not look any further, there are only less greedy constructors left.
          break;
        }
        // 如果参数个数比最小个数还小,那就继续下一个构造器吧。
        if (paramTypes.length < minNrOfArgs) {
          continue;
        }
        ArgumentsHolder argsHolder;
        if (resolvedValues != null) {
          try {
            //兼容JDK6提供的@ConstructorProperties这个注解,如果它标注了参数名,那就以它的名字为准
            //@ConstructorProperties的作用=======》构造函数上的注解,显示该构造函数的参数如何与构造对象的getter方法相对应
            String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
            // 否则,就自己解析
            if (paramNames == null) {
              // 一般都是Bean工厂默认的DefaultParameterNameDiscoverer 解析出变量名
              ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
              if (pnd != null) {
                paramNames = pnd.getParameterNames(candidate);
              }
            }
            //根据获取到的参数名和已经查到的构造参数和构造参数类型来创建用户创建构造器用的构造参数数组
            //这个数组中包含了原始的参数列表和构造后的参数列表,用来对比用
            argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
                getUserDeclaredConstructor(candidate), autowiring);
          } catch (UnsatisfiedDependencyException ex) {
            if (this.beanFactory.logger.isTraceEnabled()) {
              this.beanFactory.logger.trace(
                  "Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
            }
            // Swallow and try next constructor.
            if (causes == null) {
              causes = new LinkedList<>();
            }
            causes.add(ex);
            continue;
          }
        } else {
          // Explicit arguments given -> arguments length must match exactly.
          if (paramTypes.length != explicitArgs.length) {
            continue;
          }
          argsHolder = new ArgumentsHolder(explicitArgs);
        }
        //lenientConstructorResolution的值ture与false有什么区别:
        //这个属性默认值是true,在大部分情况下都是使用[宽松模式],即使多个构造函数的参数数量相同、类型存在父子类、接口实现类关系也能正常创建bean。
        // false表示严格模式。与上面相反
        // typeDiffWeight:返回不同的个数的权重(权重概念?)
        int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
            argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
        // Choose this constructor if it represents the closest match.
        // 根据权重,选择一个最为合适的构造器
        if (typeDiffWeight < minTypeDiffWeight) {
          // 大都进这里来,然后是木有ambiguousConstructors 的
          constructorToUse = candidate;
          argsHolderToUse = argsHolder;
          argsToUse = argsHolder.arguments;
          minTypeDiffWeight = typeDiffWeight;
          ambiguousConstructors = null;
        }
        else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
          if (ambiguousConstructors == null) {
            ambiguousConstructors = new LinkedHashSet<>();
            ambiguousConstructors.add(constructorToUse);
          }
          ambiguousConstructors.add(candidate);
        }
      }
      // 如果此时还没发现可用的构造器,那这里就开始处理异常吧~
      if (constructorToUse == null) {
        if (causes != null) {
          UnsatisfiedDependencyException ex = causes.removeLast();
          for (Exception cause : causes) {
            this.beanFactory.onSuppressedException(cause);
          }
          throw ex;
        }
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Could not resolve matching constructor " +
            "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
      } else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Ambiguous constructor matches found in bean '" + beanName + "' " +
            "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
            ambiguousConstructors);
      }
      if (explicitArgs == null) {
        argsHolderToUse.storeCache(mbd, constructorToUse);
      }
    }
    //下面步骤都是通用的,用上面得到的构造器(无论是从bean对象中获取的还是spring自己构建的)
    // 和参数来反射创建bean实例,并放到BeanWrapperImpl对象中然后返回
    try {
      // 拿到生成Bean实例化策略,默认值为CglibSubclassingInstantiationStrategy  用CGLIB生成子类的方式
      final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy();
      Object beanInstance;
      if (System.getSecurityManager() != null) {
        final Constructor<?> ctorToUse = constructorToUse;
        final Object[] argumentsToUse = argsToUse;
        beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
            strategy.instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse),
            beanFactory.getAccessControlContext());
      }
      else {
        // 主要就是调用了策略器的instantiate,对Bean进行了最终的实例化
        // 此方法为重载方法,此处因为不需要代理,所以执行的直接是SimpleInstantiationStrategy#instantiate
        // 到此处,有一个HelloServiceImpl正式创建   然后继续到doCreateBean方法去吧
        beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
      }
      bw.setBeanInstance(beanInstance);
      return bw;
    } catch (Throwable ex) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
          "Bean instantiation via constructor failed", ex);
    }
  }


populateBean和initializeBean

具体他俩做了什么,参考:【小家Spring】AbstractAutowireCapableBeanFactory#populateBean实现Bean的依赖注入(属性赋值)和initializeBean对Bean的初始化


总结


getBean()方法是Spring IOC容器实例化、初始化Bean的核心方法,里面的逻辑也异常的复杂。

通过阅读源码,感受最深的是:

一个优秀的框架,决不仅仅是说可以通过反射能帮助创建Bean就行了,而是像Spring这样能够考虑到各式各样的情况,有非常好的容错性、有很好的异常处理以及提示调用者报错原因。当然也有一一系列的设计原则的体现:单一职责原则、面向对象的设计原则、对扩展开放对修改关闭的原则(BeanPostProcessor是个非常好的钩子,允许我们参与进Bean的声明周期中来)

阅读优秀的源码,能够在我们更深的理解到:优秀只所以优秀,是因为真的很优秀!!!


相关文章
|
15天前
|
XML 安全 Java
|
15天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
40 14
|
1月前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
13天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
26 1
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
558 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
219 2
|
1天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
28 14
|
23天前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
41 1
SpringBoot入门(7)- 配置热部署devtools工具
|
1月前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
43 2
 SpringBoot入门(7)- 配置热部署devtools工具