Spring5源码(23)-Spring通过工厂方法实例化bean

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Spring5源码(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类的所有方法),如下图


    image.png

其次,从获取到的所有方法中筛选出可能符合条件的方法,这里也有一个小技巧也提现了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的实例化。代码很简单,不在分析了




目录
相关文章
|
13天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
67 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
1月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
80 1
|
5月前
|
Java 开发者 Spring
解析Spring中Bean的生命周期
解析Spring中Bean的生命周期
55 2
|
6月前
|
缓存 Java Spring
Spring 框架中 Bean 的生命周期
Spring 框架中 Bean 的生命周期
71 1
|
5月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
62 0
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
85 1
|
4月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
103 11
|
3月前
|
前端开发 Java 开发者