Spring源码解析-bean实例化

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);
    }

    // ② 如果有工厂方法的话,则使用工厂方法实例化bean
    if (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);
        }
    }

    // ④ 需要根据参数解析、确定构造函数
    // 主要是检查已经注册的 SmartInstantiationAwareBeanPostProcessor
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
     解析的构造器不为空 || 注入类型为构造函数自动注入 || bean定义中有构造器参数 || 传入参数不为空
    if (ctors != null
            || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR
            || mbd.hasConstructorArgumentValues()
            || !ObjectUtils.isEmpty(args))  {
        //构造函数自动注入
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // ⑤ 无任何的特殊处理,则使用默认的无参构造函数实例化bean
    return 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);
        // 初始化 BeanWrapper
        initBeanWrapper(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 对象,其中会涉及到其他 bean
                minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
            }
            else {
                // 以上均未能获取,则将最小方法参数个数置为0
                minNrOfArgs = 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_VALUE
                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;
                }
                // 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()方法实例化Bean
            beanInstance = 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 对象,其中会涉及到其他 bean
    minNrOfArgs = 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的过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值