天天看點

23--Spring通過工廠方法執行個體化bean

前兩節已經介紹了Spring通過無參和有參兩種方式執行個體化bean,本小節介紹Spring通過工廠方法執行個體化bean。工廠方法又包含了執行個體工廠方法和靜态工廠方法,但是這兩者的實際調用是在同一個方法裡,接下來我們看源碼。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {

    // 確定此時beanClass已經被解析
    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);
    }

    // ③ 當建立一個相同的bean時,使用之間儲存的快照
    // 這裡可能會有一個疑問,什麼時候會建立相同的bean呢?
    // 		③-->① 單例模式: Spring不會緩存該模式的執行個體,那麼對于單例模式的bean,什麼時候會用到該執行個體化政策呢?
    //                 我們知道對于IoC容器除了可以索取bean之外,還能銷毀bean,當我們調用xmlBeanFactory.destroyBean(myBeanName,myBeanInstance),
    //				   銷毀bean時,容器是不會銷毀已經解析的構造函數快照的,如果再次調用xmlBeanFactory.getBean(myBeanName)時,就會使用該政策了.
    // 		③-->② 原型模式: 對于該模式的了解就簡單了,IoC容器不會緩存原型模式bean的執行個體,當我們第二次向容器索取同一個bean時,就會使用該政策了.
    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) {
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    // 如果該bean已經被解析過
    if (resolved) {
        // 使用已經解析過的構造函數執行個體化
        if (autowireNecessary) {
            return autowireConstructor(beanName, mbd, null, null);
        }
        // 使用預設無參構造函數執行個體化
        else {
            return instantiateBean(beanName, mbd);
        }
    }

    // ④ 确定需要使用的構造函數
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    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

1.測試用例

打開day01下的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="com.lyc.cn.v2.day01.DogStaticFactory" factory-method="newInstance">
    <!-- 指定構造器參數 index對應構造器中參數的位置 -->
    <constructor-arg index="0" value="小明"/>
    <constructor-arg index="1" value="3"/>
</bean>

<!-- 執行個體工廠方法執行個體化 -->
<bean id="dogFactory" class="com.lyc.cn.v2.day01.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>
           
2.instantiateUsingFactoryMethod工廠方法執行個體化bean源碼
public BeanWrapper instantiateUsingFactoryMethod(
			final String beanName, final RootBeanDefinition mbd, @Nullable final Object[] explicitArgs) {

    BeanWrapperImpl bw = new BeanWrapperImpl();
    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 {
        // 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;
    }

    Method factoryMethodToUse = null;
    ArgumentsHolder argsHolderToUse = null;
    Object[] argsToUse = null;

    // 2、判斷有無顯式指定參數,如果有則優先使用,如xmlBeanFactory.getBean("cat", "美美",3);
    if (explicitArgs != null) {
        argsToUse = explicitArgs;
    }
    // 3、從緩存中加載工廠方法和構造函數參數
    else {
        Object[] argsToResolve = null;
        synchronized (mbd.constructorArgumentLock) {
            factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
            if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
                // Found a cached factory method...
                argsToUse = mbd.resolvedConstructorArguments;
                if (argsToUse == null) {
                    argsToResolve = mbd.preparedConstructorArguments;
                }
            }
        }
        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) {
            // 如指定參數不為空,則使用指定參數個數作為最小方法參數個數
            minNrOfArgs = explicitArgs.length;
        }
        else {
            // 嘗試從BeanDefinition中加載構造函數資訊,以确定最小方法參數個數
            if (mbd.hasConstructorArgumentValues()) {
                ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
                resolvedValues = new ConstructorArgumentValues();
                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 、指定方法參數不為空,則優先使用指定方法參數
                if (explicitArgs != null){
                    // Explicit arguments given -> arguments length must match exactly.
                    if (paramTypes.length != explicitArgs.length) {
                        continue;
                    }
                    argsHolder = new ArgumentsHolder(explicitArgs);
                }
                // 5.2、否則,解析方法參數
                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.length == 1);
                    }
                    catch (UnsatisfiedDependencyException ex) {
                        // Swallow and try next overloaded factory method.
                        if (causes == null) {
                            causes = new LinkedList<>();
                        }
                        causes.add(ex);
                        continue;
                    }
                }
                // 5.3、 通過構造函數參數權重對比,得出最适合使用的構造函數
                // 先判斷是傳回是在寬松模式下解析構造函數還是在嚴格模式下解析構造函數。(預設是寬松模式)
                // 對于寬松模式:例如構造函數為(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 若果未能明确解析出需要使用的工廠方法
                // 對于具有相同數量參數的方法,如果具有相同類型的差異權值,則收集這些候選對象,并最終引發歧義異常。
                // 但是,隻在非寬松的構造函數解析模式中執行該檢查,并顯式地忽略覆寫的方法(具有相同的參數簽名)。
                // Find out about ambiguity: In case of the same type difference weight
                // for methods with the same number of parameters, collect such candidates
                // and eventually raise an ambiguity exception.
                // However, only perform that check in non-lenient constructor resolution mode,
                // and explicitly ignore overridden methods (with the same parameter signature).
                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;
            beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                    this.beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, this.beanFactory, fb, factoryMethod, args),
                    this.beanFactory.getAccessControlContext());
        }
        else {
            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類的所有方法),如下圖

    23--Spring通過工廠方法執行個體化bean
    其次,從擷取到的所有方法中篩選出可能符合條件的方法,這裡也有一個小技巧也提現了Spring代碼的高效性
// 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);
}
           

然後按照方法參數個數進行排序,并預先解析最小方法參數個數,通過循環所有的候選方法,比對候選工廠方法的參數權重,得出最适合的工廠方法。

  • 6、異常處理
  • 7、根據解析出來的工廠方法建立對應的bean的執行個體
3.執行個體化bean

執行個體化的方式很簡單,通過調用Method.invoke()方法完成bean的執行個體化。代碼很簡單,不在分析了