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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【小家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的声明周期中来)

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


相关文章
|
17天前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
105 26
|
2月前
|
XML 安全 Java
|
2月前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
2月前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
2月前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
75 6
|
2月前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
169 3
|
2月前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
50 1
|
30天前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
251 17
Spring Boot 两种部署到服务器的方式
|
30天前
|
Dart 前端开发 JavaScript
springboot自动配置原理
Spring Boot 自动配置原理:通过 `@EnableAutoConfiguration` 开启自动配置,扫描 `META-INF/spring.factories` 下的配置类,省去手动编写配置文件。使用 `@ConditionalXXX` 注解判断配置类是否生效,导入对应的 starter 后自动配置生效。通过 `@EnableConfigurationProperties` 加载配置属性,默认值与配置文件中的值结合使用。总结来说,Spring Boot 通过这些机制简化了开发配置流程,提升了开发效率。
62 17
springboot自动配置原理
|
1月前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
88 11