Spring源码解析-bean实例化

​ 本文介绍Spring创建 bean 过程中的第一个步骤:实例化 bean。

1. Bean实例化源码

​ 虽然实例化Bean有多种方式(包括静态工厂和工厂实例),但是他们的实际调用是在同一个方法里,接下来我们看源码。

​ 在AbstractAutowireCapableBeanFactory类中createBeanInstance()方法源码如下:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 解析 bean,将 bean 类名解析为 class 引用Class<?> beanClass = resolveBeanClass(mbd, beanName);// beanClass不为空,且beanClass的修饰符为不为public,且不允许访问非公共构造函数和方法,然后则抛出异常if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());}// ① Spring5.0新增的实例化策略,如果设置了该策略,将会覆盖(不执行)构造方法和工厂方法实例化策略Supplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}// ② 如果有工厂方法的话,则使用工厂方法实例化beanif (mbd.getFactoryMethodName() != null)  {return instantiateUsingFactoryMethod(beanName, mbd, args);}// 一个类可能有多个构造器,所以Spring得根据参数个数、类型确定需要调用的构造器// 在使用构造器创建实例后,Spring会将解析过后确定下来的构造器(或工厂方法)保存在缓存中,避免再次创建相同bean时再次解析// ③ 也就是说当创建一个相同的bean时,使用之间保存的快照// 这里可能会有一个疑问,什么时候会创建相同的bean呢?//  ③-->① 单例模式(singletom): Spring不会缓存该模式的实例,那么对于单例模式的bean,什么时候会用到该实例化策略呢?//        我们知道对于IoC容器除了可以索取bean之外,还能销毁bean,当我们调用xmlBeanFactory.destroyBean(myBeanName,myBeanInstance),//         销毁bean时,容器是不会销毁已经解析的构造函数快照的,如果再次调用xmlBeanFactory.getBean(myBeanName)时,就会使用该策略了.//  ③-->② 原型模式(propotype): 对于该模式的理解就简单了,IoC容器不会缓存原型模式bean的实例,当我们第二次向容器索取同一个bean时,就会使用该策略了.boolean resolved = false;boolean autowireNecessary = false;if (args == null) {//constructorArgumentLock 构造函数的常用锁synchronized (mbd.constructorArgumentLock) {// 如果已缓存的解析的构造函数或者工厂方法不为空,则可以重复利用构造函数解析// 因为需要根据参数确认到底使用哪个构造函数,该过程比较消耗性能,所有采用缓存机制if (mbd.resolvedConstructorOrFactoryMethod != null) {//该bean已经被解析过resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}// 如果该bean已经被解析过if (resolved) {// 如果已经解析过,使用已经解析好的构造器实例化if (autowireNecessary) {构造函数自动注入return autowireConstructor(beanName, mbd, null, null);}// 使用默认无参构造函数实例化else {return instantiateBean(beanName, mbd);}}// ④ 需要根据参数解析、确定构造函数// 主要是检查已经注册的 SmartInstantiationAwareBeanPostProcessorConstructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); 解析的构造器不为空 || 注入类型为构造函数自动注入 || bean定义中有构造器参数 || 传入参数不为空if (ctors != null|| mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR|| mbd.hasConstructorArgumentValues()|| !ObjectUtils.isEmpty(args))  {//构造函数自动注入return autowireConstructor(beanName, mbd, ctors, args);}// ⑤ 无任何的特殊处理,则使用默认的无参构造函数实例化beanreturn instantiateBean(beanName, mbd);
}

实例化 bean 是一个复杂的过程,其主要的逻辑为:

  • 如果存在 Supplier 回调,则调用 obtainFromSupplier() 进行初始化
  • 如果存在工厂方法,则使用工厂方法进行初始化
  • 首先判断缓存,如果缓存中存在,即已经解析过了,则直接使用已经解析了的,根据 constructorArgumentsResolved 参数来判断是使用构造函数自动注入还是默认构造函数
  • 如果缓存中没有,则需要先确定到底使用哪个构造函数来完成解析工作,因为一个类有多个构造函数,每个构造函数都有不同的构造参数,所以需要根据参数来锁定构造函数并完成初始化,如果存在参数则使用相应的带有参数的构造函数,否则使用默认构造函数。

上述源码中第②歩,如果有工厂方法的话,则通过工厂方法实例化Bean。

2. 测试用例

编写MyTest类,类中测试源码如下:

@Test
public void test3() {// 静态工厂System.out.println("静态工厂");Dog dog3 = xmlBeanFactory.getBean("dog3", Dog.class);dog3.sayHello();
}@Test
public void test4() {// 实例工厂System.out.println("实例工厂");Dog dog4 = xmlBeanFactory.getBean("dog4", Dog.class);dog4.sayHello();
}

该处配置不同于普通的bean,粘贴一下配置文件信息,方便大家分析。

<!-- 静态工厂方法实例化 -->
<bean id="dog3" class="cn.sxw.ssm.DogStaticFactory" factory-method="newInstance"><!-- 指定构造器参数 index对应构造器中参数的位置 --><constructor-arg index="0" value="小明"/><constructor-arg index="1" value="3"/>
</bean><!-- 实例工厂方法实例化 -->
<bean id="dogFactory" class="cn.sxw.ssm.DogFactory"/>
<!-- 不能指定class属性,此时必须使用factory-bean属性来指定工厂Bean,factory-method属性指定实例化Bean的方法 -->
<bean id="dog4" factory-bean="dogFactory" factory-method="newInstance"><constructor-arg index="0" value="小明"/><constructor-arg index="1" value="3"/>
</bean>

3. obtainFromSupplier()

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

​ 首先从 BeanDefinition 中获取 Supplier,如果不为空,则调用 obtainFromSupplier() 。那么 Supplier 是什么呢?在这之前也没有提到过这个字段,Spring5.0新增的实例化策略,如果设置了该策略,将会不执行构造方法和工厂方法实的例化策略。

public interface Supplier<T> {T get();
}

​ Supplier 接口仅有一个功能性的 get(),该方法会返回一个 T 类型的对象,有点儿类似工厂方法。这个接口有什么作用?用于指定创建 bean 的回调,如果我们设置了这样的回调,那么其他的构造器或者工厂方法都会没有用。在什么设置该参数呢?Spring 提供了相应的 setter 方法,如下:

public void setInstanceSupplier(@Nullable Supplier<?> instanceSupplier) {this.instanceSupplier = instanceSupplier;
}

​ 在构造 BeanDefinition 的时候设置了该值,如下(以 RootBeanDefinition 为例):

    public <T> RootBeanDefinition(@Nullable Class<T> beanClass, String scope, @Nullable Supplier<T> instanceSupplier) {super();setBeanClass(beanClass);setScope(scope);setInstanceSupplier(instanceSupplier);}

​ 如果设置了 instanceSupplier 则调用 obtainFromSupplier() 完成 bean 的初始化,如下:

    protected BeanWrapper obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) {String outerBean = this.currentlyCreatedBean.get();this.currentlyCreatedBean.set(beanName);Object instance;try {// 调用 Supplier 的 get(),返回一个对象instance = instanceSupplier.get();}finally {if (outerBean != null) {this.currentlyCreatedBean.set(outerBean);}else {this.currentlyCreatedBean.remove();}}// 根据对象构造 BeanWrapper 对象BeanWrapper bw = new BeanWrapperImpl(instance);// 初始化 BeanWrapperinitBeanWrapper(bw);return bw;}

​ 代码相对简单,调用 Supplier 的 get() 方法,获得一个 bean 实例对象,然后根据该实例对象构造一个 BeanWrapper 对象 bw,最后初始化该对象。

4. instantiateUsingFactoryMethod()

​ 通过Bean实例化源码,如果有工厂方法,就调用ConstructorResolver类中instantiateUsingFactoryMethod()方法,实现工厂方法实例化bean。

protected BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}

​ 构造一个 ConstructorResolver 对象,然后调用其 instantiateUsingFactoryMethod() 方法。ConstructorResolver 是构造方法或者工厂类初始化 bean 的委托类。

4.1 instantiateUsingFactoryMethod()源码:

public BeanWrapper instantiateUsingFactoryMethod(final String beanName, final RootBeanDefinition mbd, @Nullable final Object[] explicitArgs) {// 构造 BeanWrapperImpl 对象BeanWrapperImpl bw = new BeanWrapperImpl();// 初始化 BeanWrapperImpl// 向BeanWrapper对象中添加 ConversionService 对象和属性编辑器 PropertyEditor 对象this.beanFactory.initBeanWrapper(bw);Object factoryBean;Class<?> factoryClass;boolean isStatic;// 1、判断是实例工厂还是静态工厂方法// 获取factoryBeanName,即配置文件中的工厂方法// 注意:静态工厂方法是没有factoryBeanName的,所以如果factoryBeanName不为null,// 则一定是实例工厂方法,否则就是静态工厂方法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");}// 获取工厂factoryBeanName实例factoryBean = this.beanFactory.getBean(factoryBeanName);if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {throw new ImplicitlyAppearedSingletonException();}factoryClass = factoryBean.getClass();isStatic = false;}else {// 工厂名为空,则其可能是一个静态工厂// 静态工厂创建bean,必须要提供工厂的全类名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;}// 工厂方法Method factoryMethodToUse = null;ArgumentsHolder argsHolderToUse = null;//参数Object[] argsToUse = null;// 2、判断有无显式指定参数,如果有则优先使用,如xmlBeanFactory.getBean("cat", "美美",3);// 工厂方法的参数,在调用 getBean 方法的时候指定了方法参数if (explicitArgs != null) {argsToUse = explicitArgs;}// 3、从缓存中加载工厂方法和构造函数参数else {// 没有指定,则尝试从配置文件中解析Object[] argsToResolve = null;//首先尝试从缓存中获取synchronized (mbd.constructorArgumentLock) {// 获取缓存中的构造函数或者工厂方法factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {// 获取缓存中的构造参数argsToUse = mbd.resolvedConstructorArguments;if (argsToUse == null) {// 获取缓存中的构造函数参数的包可见字段argsToResolve = mbd.preparedConstructorArguments;}}}// 缓存中存在,则解析存储在 BeanDefinition 中的参数// 如给定方法的构造函数 A(int ,int ),则通过此方法后就会把配置文件中的("1","1")转换为 (1,1)// 缓存中的值可能是原始值也有可能是最终值if (argsToResolve != null) {argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);}}// 4、未能从缓存中加载工厂方法和构造函数参数,// 则解析并确定应该使用哪一个工厂方法实例化,并解析构造函数参数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);// 4.1、获取factoryClass中所有的方法Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);List<Method> candidateSet = new ArrayList<>();// 4.2、从获取到的所有方法中筛选出可能符合条件的方法for (Method candidate : rawCandidates) {// isStatic-->是之前解析过的,如果当前工厂方法是静态工厂方法,那么isStatic-->true;// 如果当前工厂方法是实例工厂方法,那么isStatic-->false// 通过Modifier.isStatic(candidate.getModifiers()) == isStatic判断,过滤掉一部分不符合条件的方法// mbd.isFactoryMethod(candidate)-->判断是否工厂方法if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {candidateSet.add(candidate);}}// 4.3、对候选工厂方法按照方法的参数个数进行倒序排序Method[] candidates = candidateSet.toArray(new Method[0]);AutowireUtils.sortFactoryMethods(candidates);ConstructorArgumentValues resolvedValues = null;boolean autowiring = (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);int minTypeDiffWeight = Integer.MAX_VALUE;Set<Method> ambiguousFactoryMethods = null;// 4.4、定义最小工厂方法参数个数,以备循环解析候选方法使用int minNrOfArgs;if (explicitArgs != null) {// getBean() 有传递参数,即指定参数不为空,则使用指定参数个数作为最小方法参数个数minNrOfArgs = explicitArgs.length;}else {// getBean() 没有传递参数,则尝试从BeanDefinition中加载构造函数信息,以确定最小方法参数个数if (mbd.hasConstructorArgumentValues()) {// 构造函数的参数ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();resolvedValues = new ConstructorArgumentValues();// 解析构造函数的参数// 将该 bean 的构造函数参数解析为 resolvedValues 对象,其中会涉及到其他 beanminNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);}else {// 以上均未能获取,则将最小方法参数个数置为0minNrOfArgs = 0;}}// 5.循环候选工厂方法,并确定最终使用的工厂方法LinkedList<UnsatisfiedDependencyException> causes = null;for (Method candidate : candidates) {Class<?>[] paramTypes = candidate.getParameterTypes();// 如果候选方法的参数个数大于之前定义的最小方法参数个数,则继续循环// 如果候选方法的参数个数为1,而定义的最小方法参数个数为2,那么肯定不会使用该方法作为工厂方法if (paramTypes.length >= minNrOfArgs) {ArgumentsHolder argsHolder;// 5.1 、getBean()传递了参数,即指定方法参数不为空,则优先使用指定方法参数if (explicitArgs != null){// Explicit arguments given -> arguments length must match exactly.if (paramTypes.length != explicitArgs.length) {continue;}argsHolder = new ArgumentsHolder(explicitArgs);}// 5.2、未传递参数,解析构造方法参数else {try {String[] paramNames = null;// 获取 ParameterNameDiscoverer 对象// ParameterNameDiscoverer 是用于解析方法和构造函数的参数名称的接口,为参数名称探测器ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();if (pnd != null) {// 获取指定构造函数的参数名称paramNames = pnd.getParameterNames(candidate);}// 在已经解析的构造函数参数值的情况下,创建一个参数持有者对象argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,paramTypes, paramNames, candidate, autowiring, candidates.length == 1);}catch (UnsatisfiedDependencyException ex) {// Swallow and try next overloaded factory method.if (causes == null) {causes = new LinkedList<>();}causes.add(ex);continue;}}// 5.3、 通过构造函数参数权重对比,得出最适合使用的构造函数// 先判断是返回是在宽松模式下解析构造函数还是在严格模式下解析构造函数。(默认是宽松模式)// // isLenientConstructorResolution 判断解析构造函数的时候是否以宽松模式还是严格模式// 严格模式:解析构造函数时,必须所有的都需要匹配,否则抛出异常// 宽松模式:使用具有"最接近的模式"进行匹配// typeDiffWeight:类型差异权重// 对于宽松模式:例如构造函数为(String name,int age),配置文件中定义(value="美美",value="3")//    那么对于age来讲,配置文件中的"3",可以被解析为int也可以被解析为String,//   这个时候就需要来判断参数的权重,使用ConstructorResolver的静态内部类ArgumentsHolder分别对字符型和数字型的参数做权重判断//   权重越小,则说明构造函数越匹配// 对于严格模式:严格返回权重值,不会根据分别比较而返回比对值// minTypeDiffWeight = Integer.MAX_VALUE;而权重比较返回结果都是在Integer.MAX_VALUE做减法,起返回最大值为Integer.MAX_VALUEint 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;}// 5.4 若果未能明确解析出需要使用的工厂方法// 对于具有相同数量参数的方法,如果具有相同类型的差异权值,则收集这些候选对象,并最终引发歧义异常。// 但是,只在非宽松的构造函数解析模式中执行该检查,并显式地忽略覆盖的方法(具有相同的参数签名)。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);}}}// 6、没有可执行的工厂方法,抛出异常,异常处理if (factoryMethodToUse == null) {if (causes != null) {UnsatisfiedDependencyException ex = causes.removeLast();for (Exception cause : causes) {this.beanFactory.onSuppressedException(cause);}throw ex;}List<String> argTypes = new ArrayList<>(minNrOfArgs);if (explicitArgs != null) {for (Object arg : explicitArgs) {argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");}}else if (resolvedValues != null){Set<ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());valueHolders.addAll(resolvedValues.getGenericArgumentValues());for (ValueHolder value : valueHolders) {String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) :(value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null"));argTypes.add(argType);}}String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);throw new BeanCreationException(mbd.getResourceDescription(), beanName,"No matching factory method found: " +(mbd.getFactoryBeanName() != null ?"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +"Check that a method with the specified name " +(minNrOfArgs > 0 ? "and arguments " : "") +"exists and that it is " +(isStatic ? "static" : "non-static") + ".");}else if (void.class == factoryMethodToUse.getReturnType()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Invalid factory method '" + mbd.getFactoryMethodName() +"': needs to have a non-void return type!");}else if (ambiguousFactoryMethods != null) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Ambiguous factory method matches found in bean '" + beanName + "' " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +ambiguousFactoryMethods);}if (explicitArgs == null && argsHolderToUse != null) {//将解析的构造函数加入缓存argsHolderToUse.storeCache(mbd, factoryMethodToUse);}}// 7、根据解析出来的工厂方法创建对应的bean的实例try {Object beanInstance;if (System.getSecurityManager() != null) {final Object fb = factoryBean;final Method factoryMethod = factoryMethodToUse;final Object[] args = argsToUse;//通过执行工厂方法来创建bean实例beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->this.beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, this.beanFactory, fb, factoryMethod, args),this.beanFactory.getAccessControlContext());}else {// 调用instantiate()方法实例化BeanbeanInstance = this.beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse);}bw.setBeanInstance(beanInstance);return bw;}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean instantiation via factory method failed", ex);}
}

​ 从代码中的注释可以看出来,工厂实例化与构造函数方法实例化有异曲同工之处。

  1. 判断是实例工厂还是静态工厂方法

    静态工厂方法是没有factoryBeanName的,所以如果factoryBeanName不为null,则一定是实例工厂方法,否则就是静态工厂方法。且如是实例工厂需要获取工厂的bean实例,以备后续实例化使用。

  2. 判断有无显式指定参数,如果有则优先使用,如xmlBeanFactory.getBean(“cat”, “美美”,3);

  3. 从缓存中加载工厂方法和构造函数参数

  4. 未能从缓存中加载工厂方法和构造函数参数,则解析并确定应该使用哪一个工厂方法实例化,并解析构造函数参数。

    首先,获取factoryClass中所有的方法,注意(这里获取到的不仅仅是工厂方法,而是factoryClass类的所有方法)。

    然后按照方法参数个数进行排序,并预先解析最小方法参数个数,通过循环所有的候选方法,比对候选工厂方法的参数权重,得出最适合的工厂方法。

  • 6、异常处理
  • 7、根据解析出来的工厂方法创建对应的bean的实例

instantiateUsingFactoryMethod() 方法体实在是太大了,处理细节感觉很复杂,需要硬着头皮看完,中间断断续续的。吐槽这里的代码风格,完全不符合 Spring 代码风格。Spring 的一贯做法是将一个复杂逻辑进行拆分,分为多个细小的模块进行嵌套,每个模块负责一部分功能,模块与模块之间层层嵌套,上一层一般都是对下一层的总结和概括,这样就会使得每一层的逻辑变得清晰易懂。

​ 回归到上面的方法体,虽然代码体量大,但是总体我们还是可看清楚这个方法要做的事情。一句话概括就是:确定工厂对象,然后获取构造函数和构造参数,最后调用 InstantiationStrategy 对象的 instantiate() 来创建 bean 实例。下面我们就这个句概括的话进行拆分并详细说明。

4.2 思路分析1:确定工厂对象

​ 首先获取工厂方法名,若工厂方法名不为空,则调用 beanFactory.getBean() 获取工厂对象,若为空,则可能为一个静态工厂,对于静态工厂则必须提供工厂类的全类名,同时设置 factoryBean = null

4.3 思路分析2:构造参数确认

​ 工厂对象确定后,则是确认构造参数。构造参数的确认主要分为三种情况:explicitArgs 参数、缓存中获取、配置文件中解析。

4.4 思路分析3:explicitArgs 参数

​ explicitArgs 参数是我们调用 getBean() 时传递进来,一般该参数就是用于初始化 bean 时所传递的参数,如果该参数不为空,则可以确定构造函数的参数就是它了。

缓存中获取

​ 在该方法的最后,我们会发现这样一段代码:argsHolderToUse.storeCache(mbd, factoryMethodToUse) ,这段代码主要是将构造函数、构造参数保存到缓存中,如下:

public void storeCache(RootBeanDefinition mbd, Executable constructorOrFactoryMethod) {synchronized (mbd.constructorArgumentLock) {mbd.resolvedConstructorOrFactoryMethod = constructorOrFactoryMethod;mbd.constructorArgumentsResolved = true;if (this.resolveNecessary) {mbd.preparedConstructorArguments = this.preparedArguments;}else {mbd.resolvedConstructorArguments = this.arguments;}}
}

​ 其中涉及到的几个参数 constructorArgumentLockresolvedConstructorOrFactoryMethodconstructorArgumentsResolvedresolvedConstructorArguments。这些参数都是跟构造函数、构造函数缓存有关的。

  • constructorArgumentLock:构造函数的缓存锁

  • resolvedConstructorOrFactoryMethod:缓存已经解析的构造函数或者工厂方法

  • constructorArgumentsResolved:标记字段,标记构造函数、参数已经解析了。默认为false

  • resolvedConstructorArguments:缓存已经解析的构造函数参数,包可见字段

    所以从缓存中获取就是提取这几个参数的值,如下:

synchronized (mbd.constructorArgumentLock) {// 获取缓存中的构造函数或者工厂方法factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {// 获取缓存中的构造参数argsToUse = mbd.resolvedConstructorArguments;if (argsToUse == null) {// 获取缓存中的构造函数参数的包可见字段argsToResolve = mbd.preparedConstructorArguments;}}
}

​ 如果缓存中存在构造参数,则需要调用 resolvePreparedArguments() 方法进行转换,因为缓存中的值有可能是最终值也有可能不是最终值,比如我们构造函数中的类型为 Integer 类型的 1 ,但是原始的参数类型有可能是 String 类型的 1 ,所以即便是从缓存中得到了构造参数也需要经过一番的类型转换确保参数类型完全对应。

4.5思路分析4: 配置文件中解析

​ 即没有通过传递参数的方式传递构造参数,缓存中也没有,那就只能通过解析配置文件获取构造参数了。

​ 在 bean 解析类的文章中我们了解了,配置文件中的信息都会转换到 BeanDefinition 实例对象中,所以配置文件中的参数可以直接通过 BeanDefinition 对象获取。代码如下:

if (mbd.hasConstructorArgumentValues()) {// 构造函数的参数ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();resolvedValues = new ConstructorArgumentValues();// 解析构造函数的参数// 将该 bean 的构造函数参数解析为 resolvedValues 对象,其中会涉及到其他 beanminNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}

通过 BeanDefinition 的 getConstructorArgumentValues() 就可以获取构造信息了,有了构造信息就可以获取相关的参数值信息了,获取的参数信息包括直接值和引用,这一步骤的处理交由 resolveConstructorArguments() 完成,该方法会将构造参数信息解析为 resolvedValues 对象 并返回解析到的参数个数。

4.6 思路分析5:构造函数

​ 确定构造参数后,下一步则是确定构造函数。第一步则是通过 getCandidateMethods() 获取所有的构造方法,同时对构造方法进行刷选,然后在对其进行排序处理(AutowireUtils.sortFactoryMethods(candidates)),排序的主要目的是为了能够更加方便的找到匹配的构造函数,因为构造函数的确认是根据参数个数确认的。排序的规则是:public 构造函数优先参数数量降序、非 public 构造参数数量降序。

​ 通过迭代 candidates(包含了所有要匹配的构造函数)的方式,一次比较其参数,如果显示提供了参数(explicitArgs != null),则直接比较两者是否相等,如果相等则表示找到了,否则继续比较。如果没有显示提供参数,则需要获取 ParameterNameDiscoverer 对象,该对象为参数名称探测器,主要用于发现方法和构造函数的参数名称。

​ 将参数包装成 ArgumentsHolder 对象,该对象用于保存参数,我们称之为参数持有者。当将对象包装成 ArgumentsHolder 对象后,我们就可以通过它来进行构造函数匹配,匹配分为严格模式和宽松模式。

  • 严格模式:解析构造函数时,必须所有参数都需要匹配,否则抛出异常

  • 宽松模式:使用具有”最接近的模式”进行匹配

      判断的依据是根据 `BeanDefinition` 的 `isLenientConstructorResolution` 属性(该参数是我们在构造 `AbstractBeanDefinition` 对象是传递的)来获取类型差异权重(`typeDiffWeight`) 的。如果 `typeDiffWeight < minTypeDiffWeight` ,则代表“最接近的模式”,选择其作为构造函数,否则只有两者具有相同的参数数量且类型差异权重相等才会纳入考虑范围。至此,构造函数已经确认了。
    

5. 实例化bean

​ 工厂对象、构造函数、构造参数都已经确认了,则最后一步就是调用 InstantiationStrategy (SimpleInstantiationStrategy)对象的 instantiate() 来创建 bean 实例,源码如下:

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, @Nullable Object factoryBean, Method factoryMethod, @Nullable Object... args) {try {if (System.getSecurityManager() != null) {AccessController.doPrivileged(() -> {ReflectionUtils.makeAccessible(factoryMethod);return null;});} else {ReflectionUtils.makeAccessible(factoryMethod);}Method priorInvokedFactoryMethod = (Method)currentlyInvokedFactoryMethod.get();Object var9;try {currentlyInvokedFactoryMethod.set(factoryMethod);// 执行工厂方法,并返回实例//调用Method.invoke()方法完成bean的实例化Object result = factoryMethod.invoke(factoryBean, args);if (result == null) {result = new NullBean();}var9 = result;} finally {if (priorInvokedFactoryMethod != null) {currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);} else {currentlyInvokedFactoryMethod.remove();}}return var9;} catch (IllegalArgumentException var16) {throw new BeanInstantiationException(factoryMethod, "Illegal arguments to factory method '" + factoryMethod.getName() + "'; args: " + StringUtils.arrayToCommaDelimitedString(args), var16);} catch (IllegalAccessException var17) {throw new BeanInstantiationException(factoryMethod, "Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", var17);} catch (InvocationTargetException var18) {String msg = "Factory method '" + factoryMethod.getName() + "' threw exception";if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory && ((ConfigurableBeanFactory)owner).isCurrentlyInCreation(bd.getFactoryBeanName())) {msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider declaring the factory method as static for independence from its containing instance. " + msg;}throw new BeanInstantiationException(factoryMethod, msg, var18.getTargetException());}
}

instantiate() 最核心的部分就是利用 Java 反射执行工厂方法并返回创建好的实例,也就是这段代码:

Object result = factoryMethod.invoke(factoryBean, args);

以上便是Spring实例化Bean的过程。

Spring源码解析-bean实例化相关推荐

  1. Spring 源码解析 - Bean创建过程 以及 解决循环依赖

    一.Spring Bean创建过程以及循环依赖 上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的 bean 定义信息,被组装成了 BeanDef ...

  2. 三 spring源码解析--- Bean解析接口结构分析

    2019独角兽企业重金招聘Python工程师标准>>> 解析Bean是通过定义抽象的bean reader来解析,结构图如下 1.AbstractBeanDefinitionRead ...

  3. spring源码解析bean定义五ContextNamespaceHandler一

    2019独角兽企业重金招聘Python工程师标准>>> 前言 本文转自"天河聊技术"微信公众号 本次介绍ContextNamespaceHandler的prope ...

  4. Spring源码解析 - AbstractBeanFactory 实现接口与父类分析

    2019独角兽企业重金招聘Python工程师标准>>> 我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegi ...

  5. 人人都能看懂的Spring源码解析,Spring如何解决循环依赖

    人人都能看懂的Spring源码解析,Spring如何解决循环依赖 原理解析 什么是循环依赖 循环依赖会有什么问题? 如何解决循环依赖 问题的根本原因 如何解决 为什么需要三级缓存? Spring的三级 ...

  6. Spring源码解析:自定义标签的解析过程

    2019独角兽企业重金招聘Python工程师标准>>> spring version : 4.3.x Spring 中的标签分为默认标签和自定义标签两类,上一篇我们探究了默认标签的解 ...

  7. Spring源码解析 -- SpringWeb请求映射Map初始化

    简介 在上篇文章中,大致解析了Spring如何将请求路径与处理方法进行映射,但映射相关的初始化对于我们来说还是一团迷雾 本篇文章就来探索下,请求路径和处理方法的映射,是如何进行初始化的 概览 基于上篇 ...

  8. Spring 源码解析 -- SpringWeb请求映射解析

    Spring 源码解析 – SpringWeb请求映射解析 简介 基于上篇请求路径初步探索,了解到了一个请求到具体处理方法的大致路径,本篇就继续探索,看下路径是如何匹配到处理方法的 概览 基于上篇:S ...

  9. Spring源码解析【完整版】--【bilibili地址:https://www.bilibili.com/video/BV1oW41167AV】

    [本文为bilibili视频雷丰阳的Spring源码解析的完整版总结文章,其中文章前面大部分为他人博文的搬运,后面补充了其未总结的部分] 一.Java的注解 1. 注解的概念 注释:用文字描述程序,给 ...

最新文章

  1. 配置Cisco ASA and Cisco *** Client 4.x with Windows 2003 IAS RADIUS Authentication
  2. 推荐一款最好的服务器备份软件
  3. 前端学习(2563):如何触发组件更新
  4. C#中的类型转换大总结
  5. Big Sur更新下载过慢?亲测!满速下载macOS原版系统
  6. 简述人工智能的发展历程图_简述华强北airpods的发展历程
  7. 岗位理解_如何正确理解策划岗位
  8. 收据找不到怎么退押金_押金收据单不见了,能退押金吗,合同上有写押金多少的 - 找法网免费法律咨询...
  9. keras-bert学习
  10. 01-C语言之父:丹尼斯·里奇
  11. CASS11:超越自我,再续辉煌!CASS10.1.6:延续经典,只为更好!
  12. 【字节跳动】数据分析师面经
  13. 反弹shell的各种姿势
  14. Jlink下使用swd
  15. openGauss数据库开发调试工具指导
  16. 神州数码交换机CS6200命令学习(三)
  17. CUDA安装时提示:The following process must be stopped before the CUDA Visual Studio Integrated
  18. 链表ADT设计模板的简单应用——链表的ADT的实现C++版
  19. Cesium,ClippingPlanes,任意剪裁面对3DTiles剪裁
  20. 招聘应届毕业生的经验

热门文章

  1. RabbitMQ 下载安装 (window) 百度网盘
  2. 风口之上,车联网系统到底会不会是“另一个”智能手机系统?
  3. CUDA out of memory. Tried to allocate 150.00 MiB (GPU 0; 4.00 GiB total capacity; 2.24 GiB already a
  4. WKWebView预初始化
  5. 暄桐教室的50本必读书 | 39《前朝梦忆》
  6. Envoy架构理解--理解xDS/Listener/Cluster/Router/Filter
  7. 谜语(发送给你的爱人吧)
  8. 【Linux】制作U-Boot烧写镜像到SD卡的过程(上篇)
  9. PDF办公技巧之PDF怎么删除其中一页
  10. WCF实现双工通讯及客户端调用