spring源碼筆記-instantiateUsingFactoryMethod與autowireConstructor兩個(gè)Bean實(shí)例化方法

基于springboot2.1.4

1、instantiateUsingFactoryMethod

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateUsingFactoryMethod-->org.springframework.beans.factory.support.ConstructorResolver#instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs)

         public BeanWrapper instantiateUsingFactoryMethod(
            String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {

        BeanWrapperImpl bw = new BeanWrapperImpl();
        this.beanFactory.initBeanWrapper(bw);

        Object factoryBean;
        Class<?> factoryClass;
        boolean isStatic;
       // 通過beanDefinition獲取到factoryBeanName ,實(shí)際就是@Bean注解的方法
        //所在的configuration類
        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");
            }
          //  獲取configuration類的實(shí)例
            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;

        //如果在調(diào)用getBean方法時(shí)有傳參,那就用傳的參作為
        //@Bean注解的方法(工廠方法)的參數(shù),
       // 一般懶加載的bean才會(huì)傳參,啟動(dòng)過程就實(shí)例化的實(shí)際上都沒有傳參
        if (explicitArgs != null) {
            argsToUse = explicitArgs;
        }
        else {
            Object[] argsToResolve = null;
            synchronized (mbd.constructorArgumentLock) {
                factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
             //不為空表示已經(jīng)使用過工廠方法,現(xiàn)在是再次使用工廠方法,
             //  一般原型模式和Scope模式采用的上,直接使用該工廠方法和緩存的參數(shù)
                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);
            }
        }

      //  調(diào)用getBean方法沒有傳參,同時(shí)也是第一次使用工廠方法
        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);
       // 獲取configuration類的所有候選方法
            Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
            List<Method> candidateList = new ArrayList<>();
            for (Method candidate : rawCandidates) {
        // 查找到與工廠方法同名的候選方法,沒有@Bean的同名方法不被考慮
                if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
                    candidateList.add(candidate);
                }
            }

         //當(dāng)與工廠方法同名的候選方法只有一個(gè),且調(diào)用getBean方法時(shí)沒有傳參,
       //  且沒有緩存過參數(shù),直接通過調(diào)用實(shí)例化方法執(zhí)行該候選方法
            if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
                Method uniqueCandidate = candidateList.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;
                }
            }

            Method[] candidates = candidateList.toArray(new Method[0]);
     // 有多個(gè)與工廠方法同名的候選方法時(shí),進(jìn)行排序。public的方法會(huì)往前排,然后參數(shù)個(gè)數(shù)多的方法往前排
      //具體排序代碼--->org.springframework.beans.factory.support.AutowireUtils#sortConstructors

            ConstructorArgumentValues resolvedValues = null;
            boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
            int minTypeDiffWeight = Integer.MAX_VALUE;
            Set<Method> ambiguousFactoryMethods = null;

            int minNrOfArgs;
           // 如果調(diào)用getBean方法時(shí)有傳參,那么工廠方法最少參數(shù)個(gè)數(shù)要等于傳參個(gè)數(shù)
            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;
           // 遍歷同名候選方法
            for (Method candidate : candidates) {
             //   獲取候選方法的參數(shù)列表
                Class<?>[] paramTypes = candidate.getParameterTypes();

                if (paramTypes.length >= minNrOfArgs) {
                    ArgumentsHolder argsHolder;
                //在調(diào)用getBean方法時(shí)傳的參數(shù)不為空,則工廠方法的參數(shù)個(gè)數(shù)需要與
               // 傳入的參數(shù)個(gè)數(shù)嚴(yán)格一致
                    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);
                            }
                     //當(dāng)傳入的參數(shù)為空,需要根據(jù)工廠方法的參數(shù)類型注入相應(yīng)的
                   //  bean。詳細(xì)的注入代碼可查看
org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
                     //暫不過多解析,如有需要再另外開篇記錄
                            argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
                                    paramTypes, paramNames, candidate, autowiring, candidates.length == 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;
                        }
                    }
           /**計(jì)算工廠方法的權(quán)重,分嚴(yán)格模式和寬松模式,計(jì)算方式可以看本文最后的附錄
            嚴(yán)格模式會(huì)校驗(yàn)子類(注入?yún)?shù))繼承了多少層父類(方法參數(shù))層數(shù)越多權(quán)重越大,越不匹配
            ,寬松模式,只要是注入?yún)?shù)類型是方法參數(shù)類型的子類就行。
            默認(rèn)寬松模式 在argsHolders中會(huì)有arguments和rawArguments,;
           例如在注入bean時(shí),如果有經(jīng)歷過createArgumentArray方法中的TypeConverter
         (如有有定義并且注冊(cè)到beanFactory中)的,arguments和rawArguments的值是不一樣的
           如果沒有經(jīng)過轉(zhuǎn)換,兩者是一樣的。通過getBean傳入的參數(shù)兩者通常都是一樣的
           所以都是先將工廠方法的參數(shù)類型與arguments的比較,不同則賦予最大權(quán)重值,
           相同則與rawArguments比較,與rawArguments中的相同,就會(huì)賦最大權(quán)重值-1024,
           不相同,則賦最大權(quán)重值-512,經(jīng)過類型轉(zhuǎn)換一定會(huì)執(zhí)行最大權(quán)重值-512的操作。
           權(quán)重值越大,該工廠方法越不匹配??偟膩碚f就是傳入的參數(shù)或者注入的參數(shù)類型
           與工廠方法參數(shù)類型的比對(duì)。**/
                    int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
                            argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
                    // Choose this factory method if it represents the closest match.
                    if (typeDiffWeight < minTypeDiffWeight) {
            /**  當(dāng)權(quán)重小時(shí),重新設(shè)置factoryMethodToUse 和argsHolderToUse ,argsToUse ,
              并把當(dāng)前權(quán)重值設(shè)置為最小權(quán)重值,等待遍歷的下一個(gè)候選工廠方法比對(duì),
              并且將ambiguousFactoryMethods (表示有含糊同樣權(quán)重的候選方法)設(shè)置為空**/
                        factoryMethodToUse = candidate;
                        argsHolderToUse = argsHolder;
                        argsToUse = argsHolder.arguments;
                        minTypeDiffWeight = typeDiffWeight;
                        ambiguousFactoryMethods = null;
                    }
                    // 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).
           /**  當(dāng)遍歷到下一個(gè)候選方法的時(shí)候,已經(jīng)設(shè)置了factoryMethodToUse 且權(quán)重值
             與上一次的最小權(quán)重值相等時(shí),ambiguousFactoryMethods填值,這個(gè)ambiguousFactoryMethods不為空
             表示有兩個(gè)候選方法的最小權(quán)重相等,spring無法匹配出最適合的工廠方法,
             如果再繼續(xù)往下遍歷候選器,有更小的權(quán)重值,那ambiguousFactoryMethods會(huì)
             再次被設(shè)置為空**/
                    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);
                    }
                }
            }

            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") + ".");
            }
             //返回類型不能為void
            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!");
            }
           //存在含糊的兩個(gè)工廠方法,不知選哪個(gè)
            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) {
                mbd.factoryMethodToIntrospect = factoryMethodToUse;
                argsHolderToUse.storeCache(mbd, factoryMethodToUse);
            }
        }

        Assert.state(argsToUse != null, "Unresolved factory method arguments");
     //   到達(dá)這里,恭喜,可以完成實(shí)例化了
        bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
        return bw;
    }

看下例子

        @Bean
        public String getStr20(){
            System.out.println("helloTest in HelloConfigurationInner20");
            return "helloTest";
        }
        @Bean
        public Executor  getStr20(Executor executor){
            System.out.println("helloTest in HelloConfigurationInner20");
            return executor;
        }
//        兩者參數(shù)個(gè)數(shù)一樣多,權(quán)重也一樣,兩者的參數(shù)都不是通過轉(zhuǎn)換類型得來的,無法判斷哪個(gè)才是被選召的孩子
//        @Bean
//        public String getStr20(OrderBean hello){
//            System.out.println("helloTest in HelloConfigurationInner20");
//            return "helloTest"+hello;
//        }
2、autowireConstructor

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireConstructor--->org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs)
構(gòu)造器方法的過程有很多跟工廠方法相似的地方,比如,傳入?yún)?shù)的處理,候選方法的排序,參數(shù)的注入,權(quán)重計(jì)算等。不同的是instantiateUsingFactoryMethod有factoryBean的查找,重要的邏輯基本差不多,就不重復(fù)記錄了,重點(diǎn)說下在調(diào)用autowireConstructor前就要先獲取到構(gòu)造器,并作為參數(shù)傳入,重點(diǎn)來關(guān)注spring默認(rèn)的獲取構(gòu)造方法的邏輯
進(jìn)入org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance-->org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#determineConstructorsFromBeanPostProcessors遍歷所有的SmartInstantiationAwareBeanPostProcessor實(shí)例,執(zhí)行其determineCandidateConstructors方法(如有需要可以自定義SmartInstantiationAwareBeanPostProcessor,按需求重寫determineCandidateConstructors方法)默認(rèn)的processor為AutowiredAnnotationBeanPostProcessor接著進(jìn)入org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors看關(guān)鍵部分

//如果之前已經(jīng)有用構(gòu)造方法實(shí)例化bean,就會(huì)有緩存,原型模式和scope模式會(huì)有再次使用的時(shí)候
        // Quick check on the concurrent map first, with minimal locking.
        Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
        if (candidateConstructors == null) {
            // Fully synchronized resolution now...
            synchronized (this.candidateConstructorsCache) {
                candidateConstructors = this.candidateConstructorsCache.get(beanClass);
                if (candidateConstructors == null) {
                    Constructor<?>[] rawCandidates;
                    try {
//緩存中不存在的時(shí)候使用getDeclaredConstructors方法獲取所有的構(gòu)造方法
                        rawCandidates = beanClass.getDeclaredConstructors();
                    }
                    catch (Throwable ex) {
                        throw new BeanCreationException(beanName,
                                "Resolution of declared constructors on bean Class [" + beanClass.getName() +
                                "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
                    }
                    List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
                    Constructor<?> requiredConstructor = null;
                    Constructor<?> defaultConstructor = null;
//優(yōu)先的構(gòu)造方法是從kotlin文件獲取,沒玩過kotlin,不知道怎么弄,先不管
                    Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
                    int nonSyntheticConstructors = 0;
                    for (Constructor<?> candidate : rawCandidates) {
                        if (!candidate.isSynthetic()) {
                            nonSyntheticConstructors++;
                        }
                        else if (primaryConstructor != null) {
                            continue;
                        }
//查找是否有@Autowired注解
                        AnnotationAttributes ann = findAutowiredAnnotation(candidate);
                        if (ann == null) {
//如果沒有@Autowired注解,查找父類的構(gòu)造方法有沒有@Autowired注解
                            Class<?> userClass = ClassUtils.getUserClass(beanClass);
                            if (userClass != beanClass) {
                                try {
                                    Constructor<?> superCtor =
                                            userClass.getDeclaredConstructor(candidate.getParameterTypes());
                                    ann = findAutowiredAnnotation(superCtor);
                                }
                                catch (NoSuchMethodException ex) {
                                    // Simply proceed, no equivalent superclass constructor found...
                                }
                            }
                        }
                        if (ann != null) {
            //  當(dāng)@Autowired注解的構(gòu)造方法不止一個(gè),那上一次處理的候選構(gòu)造方法
         //     已經(jīng)設(shè)置到requiredConstructor 中,那么第二個(gè)@Autowired注解的
             // 候選構(gòu)造方法處理的時(shí)候就會(huì)拋異常
                            if (requiredConstructor != null) {
                                throw new BeanCreationException(beanName,
                                        "Invalid autowire-marked constructor: " + candidate +
                                        ". Found constructor with 'required' Autowired annotation already: " +
                                        requiredConstructor);
                            }
                            boolean required = determineRequiredStatus(ann);
                            if (required) {
                                if (!candidates.isEmpty()) {
                                    throw new BeanCreationException(beanName,
                                            "Invalid autowire-marked constructors: " + candidates +
                                            ". Found constructor with 'required' Autowired annotation: " +
                                            candidate);
                                }
//第一個(gè)處理的有@autowired處理的構(gòu)造方法設(shè)置requiredConstructor ,并設(shè)置到candidates中
                                requiredConstructor = candidate;
                            }
                            candidates.add(candidate);
                        }
//當(dāng)構(gòu)造方法沒有@Autowired注解且參數(shù)個(gè)數(shù)為0,選為defaultConstructor 
                        else if (candidate.getParameterCount() == 0) {
                            defaultConstructor = candidate;
                        }
                    }
                    if (!candidates.isEmpty()) {
                        // Add default constructor to list of optional constructors, as fallback.
                        if (requiredConstructor == null) {
                            if (defaultConstructor != null) {
//往候選方法中加入defaultConstructor
//(好像requiredConstructor 為null,candidates就會(huì)為null,感覺此句永遠(yuǎn)不會(huì)執(zhí)行)
                                candidates.add(defaultConstructor);
                            }
                            else if (candidates.size() == 1 && logger.isInfoEnabled()) {
                                logger.info("Inconsistent constructor declaration on bean with name '" + beanName +
                                        "': single autowire-marked constructor flagged as optional - " +
                                        "this constructor is effectively required since there is no " +
                                        "default constructor to fall back to: " + candidates.get(0));
                            }
                        }
//候選方法不為空的時(shí)候進(jìn)入此處,此時(shí)就一個(gè)@Autowired注解的構(gòu)造方法
                        candidateConstructors = candidates.toArray(new Constructor<?>[0]);
                    }
//當(dāng)獲取的所有構(gòu)造方法只有一個(gè),且不是@autowired注解的(注解的在上面處理了)
//且參數(shù)在一個(gè)以上,該方法作為候選的構(gòu)造方法
                    else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
                        candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
                    }
//后面primaryConstructor 的都不看了
                    else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
                            defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
                        candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};
                    }
                    else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
                        candidateConstructors = new Constructor<?>[] {primaryConstructor};
                    }
                    else {
                        candidateConstructors = new Constructor<?>[0];
                    }
//緩存選定的候選構(gòu)造方法,供原型模式和scope模式第二次實(shí)例化時(shí)使用
                    this.candidateConstructorsCache.put(beanClass, candidateConstructors);
                }
            }
        }
返回選定的構(gòu)造函數(shù)列表
        return (candidateConstructors.length > 0 ? candidateConstructors : null);

上demo

/**首先永遠(yuǎn)不必理會(huì)無參構(gòu)造方法,他最后都是不會(huì)通過autowireConstructor方法實(shí)例化,
如果只有一個(gè)帶參構(gòu)造方法,那他就被選擇為候選構(gòu)造方法,如果有多個(gè)帶參構(gòu)造方法,
則需要通過@Autowired注解其中一個(gè),不能有多個(gè)@Autowired注解的構(gòu)造方法**/

  //  public TestService(){}
//    @Autowired
    public TestService(OrderBean orderBean) {
        this.orderBean = orderBean;
    }


//    @Autowired
//    public TestService(Executor executor,OrderBean orderBean){}


關(guān)于權(quán)重值的計(jì)算

關(guān)于候選方法排序
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容