Spring 源码--Bean 实例化

简介: 上一篇的 BeanWrapper 我们介绍了 BeanWrapper 的来由。现在我们继续看看 Spring 是如何构造一个 Bean 的。

网络异常,图片无法展示
|


上一篇的 BeanWrapper 我们介绍了 BeanWrapper 的来由。现在我们继续看看 Spring 是如何构造一个 Bean 的。

网络异常,图片无法展示
|


代码不长、也不是特别的复杂

/**
 * 使用合适的实例化策略去创建bean: FactoryMethod,构造器自动注入、或者简单的无参构造器
 */
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   // Make sure bean class is actually resolved at this point.
   Class<?> beanClass = resolveBeanClass(mbd, beanName);
   .......
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }
   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }
   // Shortcut when re-creating the same bean...
   boolean resolved = false;
   boolean autowireNecessary = false;
   if (args == null) {
      synchronized (mbd.constructorArgumentLock) {
         if (mbd.resolvedConstructorOrFactoryMethod != null) {
            resolved = true;
            autowireNecessary = mbd.constructorArgumentsResolved;
         }
      }
   }
   if (resolved) {
      if (autowireNecessary) {
         return autowireConstructor(beanName, mbd, null, null);
      }
      else {
         return instantiateBean(beanName, mbd);
      }
   }
   // Candidate constructors for autowiring?
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      return autowireConstructor(beanName, mbd, ctors, args);
   }
   // Preferred constructors for default construction?
   ctors = mbd.getPreferredConstructors();
   if (ctors != null) {
      return autowireConstructor(beanName, mbd, ctors, null);
   }
   // No special handling: simply use no-arg constructor.
   return instantiateBean(beanName, mbd);
}
复制代码
Class<?> beanClass = resolveBeanClass(mbd, beanName);
复制代码

这一步就是解释 bean 对应的类型被解释成 Class 放置到 BeanDefinition 中(创建 BeanDefinition 的时候可能是设置了 className 而非 Class 对象)


InstanceSupplier


Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
  return obtainFromSupplier(instanceSupplier, beanName);
}
复制代码

这一步是获取 Supplier 对象、调用这个对象的 get 方法即可得到创建好的 bean、然后就构建 BeanWrapper 并进行初始化。注册相应的 PropertyEditor。

protected BeanWrapper obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) {
   Object instance;
   String outerBean = this.currentlyCreatedBean.get();
   this.currentlyCreatedBean.set(beanName);
   try {
      instance = instanceSupplier.get();
   }
   finally {
      if (outerBean != null) {
         this.currentlyCreatedBean.set(outerBean);
      }
      else {
         this.currentlyCreatedBean.remove();
      }
   }
   if (instance == null) {
      instance = new NullBean();
   }
   BeanWrapper bw = new BeanWrapperImpl(instance);
   initBeanWrapper(bw);
   return bw;
}
protected void initBeanWrapper(BeanWrapper bw) {
  bw.setConversionService(getConversionService());
  registerCustomEditors(bw);
}
复制代码


那我们怎么样注册这个 Supplier 呢 ?

if (context instanceof GenericApplicationContext) {
    ((GenericApplicationContext) context).registerBean(Service.class,()->{
        System.out.println("create bean in supplier");
        return new Service();
    });
}
Service bean = context.getBean(Service.class);
复制代码


FactoryMethod


if (mbd.getFactoryMethodName() != null) {
   return instantiateUsingFactoryMethod(beanName, mbd, args);
}
protected BeanWrapper instantiateUsingFactoryMethod(
      String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
    return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}
复制代码


第二种实例化 bean 的方式。什么是 FactoryMethod 呢?

设计模式中存在静态工厂和工厂方法、而这里也是类似的。我们在配置类中声明的 bean 就是类似这种模式。

@Configuration
public class Config {
    @Bean
    public Service service() {
        return new Service();
    }
    @Bean
    public static Service staticService() {
        return new Service();
    }
}
复制代码


一步步分析代码

public BeanWrapper instantiateUsingFactoryMethod(
      String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
   BeanWrapperImpl bw = new BeanWrapperImpl();
   this.beanFactory.initBeanWrapper(bw);
   Object factoryBean;
   Class<?> factoryClass;
   boolean isStatic;
   String factoryBeanName = mbd.getFactoryBeanName();
   if (factoryBeanName != null) {
      if (factoryBeanName.equals(beanName)) {
         throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
               "factory-bean reference points back to the same bean definition");
      }
      factoryBean = this.beanFactory.getBean(factoryBeanName);
      if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
         throw new ImplicitlyAppearedSingletonException();
      }
      factoryClass = factoryBean.getClass();
      isStatic = false;
   }
   else {
      // It's a static factory method on the bean class.
      if (!mbd.hasBeanClass()) {
         throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
               "bean definition declares neither a bean class nor a factory-bean reference");
      }
      factoryBean = null;
      factoryClass = mbd.getBeanClass();
      isStatic = true;
   }
  ................
复制代码


mbd.getFactoryBeanName() 如果 @Bean 注解对应的方法非静态、那么则返回它的配置类的 beanId 、此处为 config


如果 @Bean 注解对应的方法是静态、那么则返回为 null。这个设置的过程是在 ConfigurationClassPostProcessor 中使用 ConfigurationClassBeanDefinitionReader 扫描注册 bean 的时候解释设置的。


factoryBean 则为这个配置类的实例、是从 Spring 容器中获取的。而对于静态方法来说、后续通过反射调用 @Bean 修饰的方法时、invoke 的对象可以为 null 、所以 @Bean 修饰的是静态方法时、factoryBean = null 。

.........................
Method factoryMethodToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
if (explicitArgs != null) {
   argsToUse = explicitArgs;
}
else {
   Object[] argsToResolve = null;
   synchronized (mbd.constructorArgumentLock) {
      factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
     // 上一次创建 bean 时使用过的方法、主要是针对 prototype 类型的
      if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
         // 上一次创建 bean 使用的构造函数参数
         argsToUse = mbd.resolvedConstructorArguments;
         if (argsToUse == null) {
           // 上一次使用的构造函数参数(尚未转型或者需要额外处理)
            argsToResolve = mbd.preparedConstructorArguments;
         }
      }
   }
   if (argsToResolve != null) {
     // 涉及到类型转换、属性编辑、可参见上一篇文章
      argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
   }
}
.........................
复制代码


这里主要是对参数的解释以及调用方法的选定、主要都是从 BeanDefinition 中获取、如果是第一次的话、则不会从中获取到值。


explicitArgs 如果调用 Spring#getBean 方法有指定该值、则不为 null。如果指定了该值、那么即使 BeanDefinition 中有上一次解释过的产生 bean 的 method 也不会再使用、只会从中再次选择最合适的。

@Bean()
@Scope(value = SCOPE_PROTOTYPE)
public static Service staticService() {
    return new Service();
}
public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(JunitSpringBootApplication.class, args);
        Object staticService = context.getBean("staticService");
        staticService = context.getBean("staticService");
    }
复制代码


第二次调用 context.getBean("staticService") 时、则会进入到使用上一次创建的缓存中。

if (factoryMethodToUse == null || argsToUse == null) {
   // Need to determine the factory method...
   // Try all methods with this name to see if they match the given arguments.
   factoryClass = ClassUtils.getUserClass(factoryClass);
   List<Method> candidates = null;
   if (mbd.isFactoryMethodUnique) {
      if (factoryMethodToUse == null) {
         factoryMethodToUse = mbd.getResolvedFactoryMethod();
      }
      if (factoryMethodToUse != null) {
         candidates = Collections.singletonList(factoryMethodToUse);
      }
   }
   if (candidates == null) {
      candidates = new ArrayList<>();
      Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
      for (Method candidate : rawCandidates) {
         if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
            candidates.add(candidate);
         }
      }
   }
   ................     
复制代码


判断一个 BeanDefinition 的 FactoryMethod 是否唯一是根据 @Bean 修饰的方法名在配置类中是否唯一、因为方法名就是其 beanId。如果存在两个一样方法名的方法、不管是否是静态还是非静态方法、都是不唯一的。如果是唯一的话、则直接成为候选方法。

如果不是唯一的话、那么就反射获取配置类的所有方法、然后根据方法名和是否是静态方法进行筛选。此时可能存在多个候选方法。

if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
  Method uniqueCandidate = candidates.get(0);
  if (uniqueCandidate.getParameterCount() == 0) {
    mbd.factoryMethodToIntrospect = uniqueCandidate;
    synchronized (mbd.constructorArgumentLock) {
      mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
      mbd.constructorArgumentsResolved = true;
      mbd.resolvedConstructorArguments = EMPTY_ARGS;
    }
    bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
    return bw;
  }
}
复制代码


如果候选方法只有一个、并且没有传入参数、并且 @Bean 修饰的方法没有入参、那么就非常简单的进行反射调用该方法即可。


如果候选方法大于一个的话、则对其进行排序。public 的高于非 public、入参多的高于入参少的(对于配置类的 bean、Spring 一直想给它最好的爱)

if (candidates.size() > 1) {  // explicitly skip immutable singletonList
   candidates.sort(AutowireUtils.EXECUTABLE_COMPARATOR);
}
public static final Comparator<Executable> EXECUTABLE_COMPARATOR = (e1, e2) -> {
  int result = Boolean.compare(Modifier.isPublic(e2.getModifiers()), Modifier.isPublic(e1.getModifiers()));
  return result != 0 ? result : Integer.compare(e2.getParameterCount(), e1.getParameterCount());
};
复制代码


所有配置类里面的 @Bean 创建的 BeanDefinition 都是 AUTOWIRE_CONSTRUCTOR 的。


minTypeDiffWeight 代表的是方法参数类型与实际从 Spring 中找出来的 bean 参数类型的差异权重。差异小的就选其作为最后的候选方法、调用它创建返回 bean。

ConstructorArgumentValues resolvedValues = null;
boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Method> ambiguousFactoryMethods = null;
int minNrOfArgs;
if (explicitArgs != null) {
   minNrOfArgs = explicitArgs.length;
}
else {
   // We don't have arguments passed in programmatically, so we need to resolve the
   // arguments specified in the constructor arguments held in the bean definition.
   if (mbd.hasConstructorArgumentValues()) {
      ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
      resolvedValues = new ConstructorArgumentValues();
      minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
   }
   else {
      minNrOfArgs = 0;
   }
}
LinkedList<UnsatisfiedDependencyException> causes = null;
复制代码


这里面涉及到依赖关系的处理、默认来说是先从 BeanFactory 中找出该参数类型的所有 beanName、如果是一个则直接使用该 bean、如果是多个则比较 beanId 。如果都找不到或者无法确定哪个 bean 作为方法入参则抛出异常、for 循环则会继续下一个候选方法进行比较筛选。如果出现类型差异一致、则会记录、后续退出循环抛出模凌无法选出正确方法的异常。

for (Method candidate : candidates) {
   int parameterCount = candidate.getParameterCount();
   if (parameterCount >= minNrOfArgs) {
      ArgumentsHolder argsHolder;
      Class<?>[] paramTypes = candidate.getParameterTypes();
      if (explicitArgs != null) {
         // Explicit arguments given -> arguments length must match exactly.
         if (paramTypes.length != explicitArgs.length) {
            continue;
         }
         argsHolder = new ArgumentsHolder(explicitArgs);
      }
      else {
         // Resolved constructor arguments: type conversion and/or autowiring necessary.
         try {
            String[] paramNames = null;
            ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
            if (pnd != null) {
               paramNames = pnd.getParameterNames(candidate);
            }
            argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
                  paramTypes, paramNames, candidate, autowiring, candidates.size() == 1);
         }
         catch (UnsatisfiedDependencyException ex) {
            if (logger.isTraceEnabled()) {
               logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);
            }
            // Swallow and try next overloaded factory method.
            if (causes == null) {
               causes = new LinkedList<>();
            }
            causes.add(ex);
            continue;
         }
      }
      int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
            argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
      // Choose this factory method if it represents the closest match.
      if (typeDiffWeight < minTypeDiffWeight) {
         factoryMethodToUse = candidate;
         argsHolderToUse = argsHolder;
         argsToUse = argsHolder.arguments;
         minTypeDiffWeight = typeDiffWeight;
         ambiguousFactoryMethods = null;
      }
      else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
            !mbd.isLenientConstructorResolution() &&
            paramTypes.length == factoryMethodToUse.getParameterCount() &&
            !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
         if (ambiguousFactoryMethods == null) {
            ambiguousFactoryMethods = new LinkedHashSet<>();
            ambiguousFactoryMethods.add(factoryMethodToUse);
         }
        // 模凌两可
         ambiguousFactoryMethods.add(candidate);
      }
   }
}
复制代码

FactoryMethod 到此就结束了。最后选取一个最合适的方法放射调用产生 bean

当我们在配置类中、重载方法的时候、那么就会不是唯一的 BeanFactory、如果我们的参数还是都是依赖 bean 的父类/父接口、那么就会变得模凌两可进而抛出异常


Constructor


// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
   synchronized (mbd.constructorArgumentLock) {
      if (mbd.resolvedConstructorOrFactoryMethod != null) {
         resolved = true;
         autowireNecessary = mbd.constructorArgumentsResolved;
      }
   }
}
if (resolved) {
   if (autowireNecessary) {
      return autowireConstructor(beanName, mbd, null, null);
   }
   else {
      return instantiateBean(beanName, mbd);
   }
}
复制代码


这个是有缓存、解释过之后的操作。当你的 bean 是 prototype 的时候、且不是在配置类中声明的时候、则第二次获取该 bean 时会进入到该代码块中。

// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
      mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
   return autowireConstructor(beanName, mbd, ctors, args);
}
复制代码


这块代码最终会来到 AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors 中

抛开 Lookup 注解以及缓存

  1. 找出这个类所申明的构造函数
  2. 找出所有使用啦 @Autowire 注解的构造器。如果已经存在了一个 required 为 true 的构造器、那么就不能有第二个构造器被 @Autowire 修饰。如果都是为 false 的话、可以有多个
  3. 如果不存在 required = true 的构造器(存在有@Autowire 修饰的构造器)、那么如果存在默认构造器、那么也将它加入到数组中一次返回
  4. 如果不存在 @Autowire 修饰的构造器、但是存在一个非默认构造器、即入参大于 0 的构造器、那么就返回它


如果返回的构造器数组不为 null 、那么就进入到 ConstructorResolver#autowireConstructor 方法中。根据传入的构造函数、如果只有一个、那么就选择它来创建 bean、如果多个、那么就按照顺序一个个尝试、逻辑上跟选择 FactoryMethod 是一样的。

  1. 先排序 (public 优先、参数多的优先)
  2. for 循环、在 Spring 中寻找参数对象、比较参数类型的差异度、选择出差异度最小的构造函数
  3. 反射调用构造函数、创建出 bean
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
   return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
return instantiateBean(beanName, mbd);
复制代码


getPreferredConstructors 该方法默认返回 null 只有一个子类尝试返回。ClassDerivedBeanDefinition 该类只有我们尝试主动注册 Supplier 的时候才会使用该类(上面的 InstanceSupplier)、其他情况都是返回 null

@Override
@Nullable
public Constructor<?>[] getPreferredConstructors() {
   Class<?> clazz = getBeanClass();
  // Kotlin 才会有可能返回非 null
   Constructor<?> primaryCtor = BeanUtils.findPrimaryConstructor(clazz);
   if (primaryCtor != null) {
      return new Constructor<?>[] {primaryCtor};
   }
  // public 的构造函数
   Constructor<?>[] publicCtors = clazz.getConstructors();
   if (publicCtors.length > 0) {
      return publicCtors;
   }
   return null;
}
复制代码

而 instantiateBean(beanName, mbd); 则是非常简单的通过反射调用无参构造方法反射创建 bean。而这里面涉及到方法注入(replace/lookup)、后续文章会介绍到。


最后


supplier 是简单的、而 FactoryMethod 也相对 Constructor 来说简单一些、因为不用找出对应的 method、只要按照排序以及规则筛选出合适的 method 即可、当然这个排序和规则同样适用于 Contructor。

constructor 则是需要选出一个合适的构造函数、@Autowire 修饰的、还是没有、然后使用默认构造函数。多个的时候、筛选出合适的构造函数、跟 FactoryMethod 是一致的。



目录
相关文章
|
19天前
|
XML 安全 Java
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
71 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
2天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
17 6
|
3天前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
29 3
|
17天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
38 2
|
1月前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
17天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
27 1
|
1月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
63 9
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
下一篇
DataWorks