8、推断构造方法

烟雨 3年前 (2022-08-12) 阅读数 15041 #Spring源码
文章标签 Spring

一、构造方法使用

Spring中的一个bean,需要实例化得到一个对象,而实例化就需要用到构造方法。

一般情况下,一个类只有一个构造方法:

  1. 要么是无参的构造方法。

  2. 要么是有参的构造方法。

准备一个UserService类,提供三个构造方法。
@Component
public class UserService {
	@Autowired
	private OrderService orderService;

	public UserService() {
		System.out.println("UserService0");
	}

	public UserService(OrderService orderService) {
		System.out.println("UserService1");
	}

	public UserService(OrderService orderService1, OrderService orderService2) {
		System.out.println("UserService2");
	}

	public void test(){
		System.out.println(orderService);
	}
}

1.1、情况1:默认使用无参构造方法

public static void main(String[] args) {
    // 创建一个Spring容器
    AnnotationConfigApplicationContext applicationContext =
        new AnnotationConfigApplicationContext(AppConfig.class);
	//默认使用无参构造方法创建userService
    UserService userService = (UserService) applicationContext.getBean("userService");
    userService.test();
}

1.2、情况2:指定某个构造方法

首先让UserService懒加载,添加注解@Lazy
@Component
@Lazy
public class UserService {
	// ......
}

由于Spring默认生成的是单例Bean,如果不设置@Lazy懒加载,默认会使用无参构造,这里指定的new UserService()参数则不起作用(单例Bean提前初始化好了)。

public static void main(String[] args) {
    // 创建一个Spring容器
    AnnotationConfigApplicationContext applicationContext =
        new AnnotationConfigApplicationContext(AppConfig.class);

    UserService userService = (UserService) applicationContext.getBean("userService", new UserService());
    userService.test();
}

1.3、情况3:指定某个构造方法

不使用注解@Component自动寻找需要注入的Bean,通过API生成BeanDefinition,手动注入Bean。
public static void main(String[] args) {
    // 创建一个Spring容器
    AnnotationConfigApplicationContext applicationContext =
        new AnnotationConfigApplicationContext();
    applicationContext.register(AppConfig.class);

    //API构建UserService的beanDefinition
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
    beanDefinition.setBeanClass(UserService.class);

    //指定构造函数入参数
    beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(new OrderService());

    applicationContext.registerBeanDefinition("userService", beanDefinition);

    //刷新
    applicationContext.refresh();

    UserService userService = (UserService) applicationContext.getBean("userService", new OrderService());
    userService.test();
}

1.4、Spring选择构造方法的4中情况

  • 情况1:默认情况,用无参构造方法,或者只有一个构造方法就用那一个。

  • 情况2:指定了构造方法参数,通过getBean()或者beanDefinition.getConstructorArgumentValues()指定,那就用所匹配的构造方法。

  • 情况3:想让Spring自动选择构造方法和构造参数的入参值。设置autowire="constructor"。

  • 情况4:通过@autowired注解指定了某个构造方法,Spring自动找到该构造方法的入参值。

二、源码

源码位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
// Shortcut when re-creating the same bean...
// 一个原型BeanDefinition,会多次来创建Bean,那么就可以把该BeanDefinition所要使用的构造方法缓存起来,避免每次都进行推断构造方法
boolean resolved = false;
boolean autowireNecessary = false;
// 没有指定构造参数,才会缓存
if (args == null) {
    synchronized (mbd.constructorArgumentLock) {
        // 该属性表示当前BeanDefinition以前是否使用过一个构造方法的标记,ture:表示不用再去查找构造方法。
        if (mbd.resolvedConstructorOrFactoryMethod != null) {
            resolved = true;
            // autowireNecessary表示有没有必要要进行参数注入
            // 比如当前BeanDefinition用的是无参构造方法,那么autowireNecessary为false
            // 否则为true,表示需要给构造方法参数注入值
            autowireNecessary = mbd.constructorArgumentsResolved;
        }
    }
}
if (resolved) {
    // 如果确定了当前BeanDefinition的构造方法,那么看是否需要进行对构造方法的参数依赖注入
    if (autowireNecessary) {
        // 有参构造方法
        // 方法内会拿到缓存好的构造方法的入参
        return autowireConstructor(beanName, mbd, null, null);
    }
    else {
        // 没有参数的构造方法,直接进行实例化
        return instantiateBean(beanName, mbd);
    }
}

2.1、无参构造方法实例化

进入instantiateBean(beanName, mbd)方法进行实例化。
/**
* 使用默认的无参构造方法实例化Bean对象
* Instantiate the given bean using its default constructor.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @return a BeanWrapper for the new instance
*/
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
    try {
        Object beanInstance;
        // 获取系统的安全管理接口,JDK标准的安全管理API
        if (System.getSecurityManager() != null) {
            // 这里是一个匿名内置类,根据实例化策略创建实例对象
            beanInstance = AccessController.doPrivileged(
                (PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this),
                getAccessControlContext());
        }
        else {
            // 将实例化的对象封装起来,默认使用是CglibSubclassingInstantiationStrategy,实际使用的SimpleInstantiationStrategy
            beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
        }
        BeanWrapper bw = new BeanWrapperImpl(beanInstance);
        initBeanWrapper(bw);
        return bw;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
    }
}
/**
* 此方法有重载,此方法是用来调用无参构造方法来实例化的
*/
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    // Don't override the class with CGLIB if no overrides.
    // 判断当前BeanDefinition对应的beanClass中是否存在@Lookup的方法
    // bd对象定义里,是否包含MethodOverride列表;Spring有两个标签参数会产生MethodOverrides 分别是lookup-method,replaced-method
    // 没有MethodOverride对象,可以直接实例化
    if (!bd.hasMethodOverrides()) {
        // 实例化对象的构造方法
        Constructor<?> constructorToUse;
        // 锁定对象,使获得实例化构造方法线程安全
        synchronized (bd.constructorArgumentLock) {
            // 查看bd对象里是否含有无参构造方法
            constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
            // 没有就生成无参构造方法
            if (constructorToUse == null) {
                final Class<?> clazz = bd.getBeanClass();
                if (clazz.isInterface()) {
                    throw new BeanInstantiationException(clazz, "Specified class is an interface");
                }
                try {
                    if (System.getSecurityManager() != null) {
                        constructorToUse = AccessController.doPrivileged(
                            (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                    }
                    else {
                        constructorToUse = clazz.getDeclaredConstructor();
                    }
                    // 生成成功后,赋值给bd对象,后面使用
                    bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                }
                catch (Throwable ex) {
                    throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                }
            }
        }
        // 反射生成对象
        return BeanUtils.instantiateClass(constructorToUse);
    }
    else {
        // Must generate CGLIB subclass.
        // 如果存在@Lookup,则会生成一个代理对象
        return instantiateWithMethodInjection(bd, beanName, owner);
    }
}

2.2、推断构造方法,获取构造方法列表

回到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance 中。
无参构造方法没有找到,那么就开始推断构造方法
// Candidate constructors for autowiring?
// Spring提供一个扩展点,可以利用SmartInstantiationAwareBeanPostProcessor来控制用beanClass中的哪些构造方法
// 这里使用了AutowiredAnnotationBeanPostProcessor,它会把加了@Autowired注解的构造方法找出来
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);

// 如果推断出来了构造方法,则需要给构造方法赋值,也就是给构造方法参数赋值,也就是构造方法注入
// 如果没有推断出来构造方法,但是autowiremode为AUTOWIRE_CONSTRUCTOR,则也可能需要给构造方法赋值,因为不确定是用无参的还是有参的构造方法
// 如果通过BeanDefinition指定了构造方法参数值,那肯定就是要进行构造方法注入了:beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(new OrderService());
// 如果调用getBean的时候传入了构造方法参数值,那肯定就是要进行构造方法注入了
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
    return autowireConstructor(beanName, mbd, ctors, args);
}

// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
    return autowireConstructor(beanName, mbd, ctors, null);
}

// No special handling: simply use no-arg constructor.
// 不匹配以上情况,则直接使用无参构造方法
return instantiateBean(beanName, mbd);

第一步,先获可能用到的构造方法列表,取利用AutowiredAnnotationBeanPostProcessor,它会把加了@Autowired注解的构造方法找出来。进入AutowiredAnnotationBeanPostProcessor#determineConstructorsFromBeanPostProcessors 方法

@Nullable
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
    throws BeansException {

    if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            // 推断候选的构造方法列表
            Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);
            if (ctors != null) {
                return ctors;
            }
        }
    }
    return null;
}

determineCandidateConstructors()这个方法会寻找,哪些方法上面加了@Autowired,并返回这些构造方法。

// 这个方法会寻找,哪些方法上面加了@Autowired
@Override
@Nullable
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
    throws BeanCreationException {

    // Let's check for lookup methods here...
    if (!this.lookupMethodsChecked.contains(beanName)) {
        // 判断beanClass是不是java.开头的类,比如String
        if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
            try {
                Class<?> targetClass = beanClass;
                do {
                    // 遍历targetClass中的method,查看是否写了@Lookup方法
                    ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                        Lookup lookup = method.getAnnotation(Lookup.class);
                        if (lookup != null) {
                            Assert.state(this.beanFactory != null, "No BeanFactory available");

                            // 将当前method封装成LookupOverride并设置到BeanDefinition的methodOverrides中
                            LookupOverride override = new LookupOverride(method, lookup.value());
                            try {
                                RootBeanDefinition mbd = (RootBeanDefinition)
                                    this.beanFactory.getMergedBeanDefinition(beanName);
                                mbd.getMethodOverrides().addOverride(override);
                            }
                            catch (NoSuchBeanDefinitionException ex) {
                                throw new BeanCreationException(beanName,
                                                                "Cannot apply @Lookup to beans without corresponding bean definition");
                            }
                        }
                    });
                    targetClass = targetClass.getSuperclass();
                }
                while (targetClass != null && targetClass != Object.class);

            }
            catch (IllegalStateException ex) {
                throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
            }
        }
        this.lookupMethodsChecked.add(beanName);
    }

    // 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 {
                    // 拿到所有的构造方法
                    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);

                // 用来记录required为true的构造方法,一个类中只能有一个required为true的构造方法
                Constructor<?> requiredConstructor = null;
                // 用来记录默认无参的构造方法
                Constructor<?> defaultConstructor = null;
                // kotlin相关,不用管
                Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
                int nonSyntheticConstructors = 0;

                // 遍历每个构造方法
                for (Constructor<?> candidate : rawCandidates) {
                    if (!candidate.isSynthetic()) {
                        // 记录一下普通的构造方法
                        nonSyntheticConstructors++;
                    }
                    else if (primaryConstructor != null) {
                        continue;
                    }

                    // 当前遍历的构造方法是否写了@Autowired
                    MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
                    if (ann == null) {
                        // 如果beanClass是代理类,则得到被代理的类的类型
                        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...
                            }
                        }
                    }

                    // 当前构造方法上加了@Autowired
                    if (ann != null) {
                        // 整个类中如果有一个required为true的构造方法,那就不能有其他的加了@Autowired的构造方法
                        if (requiredConstructor != null) {
                            throw new BeanCreationException(beanName,
                                                            "Invalid autowire-marked constructor: " + candidate +
                                                            ". Found constructor with 'required' Autowired annotation already: " +
                                                            requiredConstructor);
                        }
                        // 判断唯一一个required为true的构造方法,并记录,多个则保存
                        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);
                            }
                            // 记录唯一一个required为true的构造方法
                            requiredConstructor = candidate;
                        }
                        // 记录所有加了@Autowired的构造方法,不管required是true还是false
                        // 如果默认无参的构造方法上也加了@Autowired,那么也会加到candidates中
                        candidates.add(candidate);

                        // 从上面代码可以得到一个结论,在一个类中,要么只能有一个required为true的构造方法,要么只能有一个或多个required为false的方法
                    }
                    else if (candidate.getParameterCount() == 0) {
                        // 记录唯一一个无参的构造方法
                        defaultConstructor = candidate;
                    }

                    // 有可能存在有参、并且没有添加@Autowired的构造方法
                }


                if (!candidates.isEmpty()) {
                    // Add default constructor to list of optional constructors, as fallback.
                    // 如果不存在一个required为true的构造方法,则所有required为false的构造方法和无参构造方法都是合格的
                    if (requiredConstructor == null) {
                        if (defaultConstructor != null) {
                            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));
                        }
                    }
                    // 如果只存在一个required为true的构造方法,那就只有这一个是合格的
                    candidateConstructors = candidates.toArray(new Constructor<?>[0]);
                }
                // 没有添加@Autowired注解的构造方法,并且类中只有一个构造方法,并且是有参的
                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};
                }
                // primaryConstructor不用管
                else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
                    candidateConstructors = new Constructor<?>[] {primaryConstructor};
                }
                else {
                    // 如果有多个有参、并且没有添加@Autowired的构造方法,是会返回空的
                    candidateConstructors = new Constructor<?>[0];
                }
                this.candidateConstructorsCache.put(beanClass, candidateConstructors);
            }
        }
    }
    return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
  1. 校验缓存,以前是否找过这个类的构造方法,缓存没有,拿到所有构造方法,进行遍历。

  2. 当前遍历的构造方法是否写了@Autowired,如果是代理类则获取被代理的类型,获取代理类的构造方法是否写了@Autowired。

  3. 如果遍历当前构造方法上加了@Autowired,无论required=false/true都添加到要返回的candidates列表中,如果required为true,记录到requiredConstructor中。

    1. 如果默认无参的构造方法上也加了@Autowired,那么也会加到candidates中。

    2. 结论:在一个类中,要么只能有一个required为true的构造方法,要么只能有一个或多个required为false的方法

  4. 返回candidates构造方法列表不为空时

    1. 判断requiredConstructor是null?为null,则所有required为false的构造方法和无参构造方法都是合格的。

      1. 判断defaultConstructor(默认构造方法)是null?非null,添加到candidates构造方法列表中。

    2. 如果只存在一个required为true的构造方法,那就只有这一个是合格的。

  5. candidates构造方法列表为空时

    1. 返回没有添加@Autowired注解的构造方法,并且类中只有一个构造方法,并且是有参的。

    2. 如果有多个有参、并且没有添加@Autowired的构造方法,返回空。
根据构造方法是否加@Autowired,分别如下情况:

image.png

2.3、Spring帮我们选择构造方法

如果@Autowired注解没法确定使用的那个构造方法,通过上一个步骤返回的构造方法列表进行分析,使用autowireConstructor()方法选择其中一个符合要求的构造方法使用。
// explicitArgs表示getBean传递的参数。
// chosenCtors上一个步骤选出来的构造方法列表。
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
                                       @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
    // BeanWrapperImpl是BeanWrapper的实现类
    // 并且这个类用来存储Bean的名称,class,实例对象等.
    BeanWrapperImpl bw = new BeanWrapperImpl();
    this.beanFactory.initBeanWrapper(bw);

    // 这个代表的是我们实际上使用的构造器(最终选择的构造方法),因为传进来的构造器一看见就知道是个数组
    // 那么Spring需要筛选出来最适合的构造器,那么筛选出来最适合的构造器就会赋值给这里的constructorToUse
    Constructor<?> constructorToUse = null;
    // 用来存储用到的构造器的参数(最终选择的构造方法所对应的参数),下面的argsToUse的值也是从这个argsHolderToUse中取出来的
    ArgumentsHolder argsHolderToUse = null;
    // 构造方法中使用到的参数列表实际的值列表
    Object[] argsToUse = null;

    // 如果getBean()传入了args参数,那构造方法要用的入参就直接确定好了。
    if (explicitArgs != null) {
        argsToUse = explicitArgs;
    }
    else {
        // 如果这个bean是原型的,那么说明此方法肯定进入过,那么也肯定找到过合适的构造方法和构造参数值列表,然后缓存起来。
        // 那么此处如果不是第一次进入的话,那么缓存里面已经有了不用再次去获取。
        Object[] argsToResolve = null;
        synchronized (mbd.constructorArgumentLock) {
            constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
            if (constructorToUse != null && mbd.constructorArgumentsResolved) {
                // Found a cached constructor...
                argsToUse = mbd.resolvedConstructorArguments;
                // explicitArgs(argsToUse)为空的时候,构造器才会被缓存
                if (argsToUse == null) {
                    argsToResolve = mbd.preparedConstructorArguments;
                }
            }
        }
        // 如果缓存中存在则去拿出来赋值
        if (argsToResolve != null) {
            argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
        }
    }

    // 如果没有确定要使用的构造方法或者传入的参数值没有确定
    if (constructorToUse == null || argsToUse == null) {

        // Take specified constructors, if any.
        // 如果没有指定构造方法,那就获取beanClass中的所有构造方法所谓候选者数组
        Constructor<?>[] candidates = chosenCtors;
        // 如果传入的指定构造方法类别为空
        if (candidates == null) {
            Class<?> beanClass = mbd.getBeanClass();
            try {
                // 获取类里面的所有构造方法
                candidates = (mbd.isNonPublicAccessAllowed() ?
                              beanClass.getDeclaredConstructors() : beanClass.getConstructors());
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                "Resolution of declared constructors on bean Class [" + beanClass.getName() +
                                                "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
            }
        }

        // 如果只有一个候选构造方法,并且没有指定所要使用的构造方法参数值
        // 并且该构造方法是无参的,那就直接用这个无参构造方法进行实例化了
        if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
            Constructor<?> uniqueCandidate = candidates[0];
            if (uniqueCandidate.getParameterCount() == 0) {
                synchronized (mbd.constructorArgumentLock) {
                    mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
                    mbd.constructorArgumentsResolved = true;
                    mbd.resolvedConstructorArguments = EMPTY_ARGS;
                }
                bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
                return bw;
            }
        }

        // Need to resolve the constructor.
        boolean autowiring = (chosenCtors != null ||
                              mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
        ConstructorArgumentValues resolvedValues = null;

        // 确定要选择的构造方法的参数个数的最小值,后续判断候选构造方法的参数个数如果小于minNrOfArgs,则直接pass掉
        int minNrOfArgs;
        if (explicitArgs != null) {
            // 如果直接传了构造方法参数值,那么所用的构造方法的参数个数肯定不能少于这个值
            minNrOfArgs = explicitArgs.length;
        }
        else {
            // 如果通过BeanDefinition传了构造方法参数值,因为有可能是通过下标指定了
            // 比如0位置的值,2位置的值,虽然只指定了2个值,但是构造方法的参数个数至少得是3个
            ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
            resolvedValues = new ConstructorArgumentValues();
            // 处理RuntimeBeanReference
            minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
        }

        // 对候选构造方法进行排序,public的方法排在最前面,都是public的情况下参数个数越多越靠前
        AutowireUtils.sortConstructors(candidates);
        int minTypeDiffWeight = Integer.MAX_VALUE;
        Set<Constructor<?>> ambiguousConstructors = null;
        Deque<UnsatisfiedDependencyException> causes = null;

        // 遍历每个构造方法,进行筛选
        for (Constructor<?> candidate : candidates) {
            // 参数个数
            int parameterCount = candidate.getParameterCount();

            // 本次遍历时,之前已经选出来了所要用的构造方法和入参对象
            // 并且入参对象个数比当前遍历到的这个构造方法的参数个数多,则不用再遍历,退出循环
            if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
                // Already found greedy constructor that can be satisfied ->
                // do not look any further, there are only less greedy constructors left.
                break;
            }
            // 如果参数个数小于所要求的参数个数,则遍历下一个,这里考虑的是同时存在public和非public的构造方法
            if (parameterCount < minNrOfArgs) {
                continue;
            }

            ArgumentsHolder argsHolder;
            Class<?>[] paramTypes = candidate.getParameterTypes();
            // 没有通过getBean()指定构造方法参数值
            if (resolvedValues != null) {
                try {
                    // 如果在构造方法上使用了@ConstructorProperties,那么就直接取定义的值作为构造方法的参数名
                    String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);

                    // 获取构造方法参数名
                    if (paramNames == null) {
                        ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                        if (pnd != null) {
                            paramNames = pnd.getParameterNames(candidate);
                        }
                    }

                    // 根据参数类型、参数名找到对应的bean对象(也就是参数值)
                    argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
                                                     getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
                }
                catch (UnsatisfiedDependencyException ex) {
                    // 当前正在遍历的构造方法找不到可用的入参对象,记录一下
                    if (logger.isTraceEnabled()) {
                        logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
                    }
                    // Swallow and try next constructor.
                    if (causes == null) {
                        causes = new ArrayDeque<>(1);
                    }
                    causes.add(ex);
                    continue;
                }
            }
            else {
                // Explicit arguments given -> arguments length must match exactly.
                // 没有通过BeanDefinition指定构造方法参数值,但是在调getBean方法是传入了参数值,那就表示只能用对应参数个数的构造方法
                if (parameterCount != explicitArgs.length) {
                    continue;
                }
                // 不用再去BeanFactory中查找bean对象了,已经有了,同时当前正在遍历的构造方法就是可用的构造方法
                argsHolder = new ArgumentsHolder(explicitArgs);
            }

            // 当前遍历的构造方法所需要的入参对象都找到了,根据参数类型和找到的参数对象计算出来一个匹配值,值越小越匹配
            // Lenient表示宽松模式
            int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
                                  argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
            // Choose this constructor if it represents the closest match.
            // 值越小越匹配
            if (typeDiffWeight < minTypeDiffWeight) {
                constructorToUse = candidate;
                argsHolderToUse = argsHolder;
                argsToUse = argsHolder.arguments;
                minTypeDiffWeight = typeDiffWeight;
                ambiguousConstructors = null;
            }
            // 值相等的情况下,记录一下匹配值相同的构造方法
            else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
                if (ambiguousConstructors == null) {
                    ambiguousConstructors = new LinkedHashSet<>();
                    ambiguousConstructors.add(constructorToUse);
                }
                ambiguousConstructors.add(candidate);
            }
        }
        // 遍历结束   x

        // 如果没有可用的构造方法,就取记录的最后一个异常并抛出
        if (constructorToUse == null) {
            if (causes != null) {
                UnsatisfiedDependencyException ex = causes.removeLast();
                for (Exception cause : causes) {
                    this.beanFactory.onSuppressedException(cause);
                }
                throw ex;
            }
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                            "Could not resolve matching constructor on bean class [" + mbd.getBeanClassName() + "] " +
                                            "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
        }
        // 如果有可用的构造方法,但是有多个
        else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                            "Ambiguous constructor matches found on bean class [" + mbd.getBeanClassName() + "] " +
                                            "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
                                            ambiguousConstructors);
        }

        // 如果没有通过getBean方法传入参数,并且找到了构造方法以及要用的入参对象则缓存
        if (explicitArgs == null && argsHolderToUse != null) {
            argsHolderToUse.storeCache(mbd, constructorToUse);
        }
    }

    Assert.state(argsToUse != null, "Unresolved constructor arguments");
    bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
    return bw;
}
  1. 先检查是否指定了具体的构造方法和构造方法参数值,或者在BeanDefinition中缓存了具体的构造方法或构造方法参数值,如果存在那么则直接使用该构造方法进行实例化

  2. 如果没有确定的构造方法或构造方法参数值,那么

    1. 如果没有确定的构造方法,那么则找出类中所有的构造方法

    2. 如果只有一个无参的构造方法,那么直接使用无参的构造方法进行实例化

    3. 如果有多个可用的构造方法或者当前Bean需要自动通过构造方法注入

    4. 根据所指定的构造方法参数值,确定所需要的最少的构造方法参数值的个数

    5. 对所有的构造方法进行排序,参数个数多的在前面

    6. 遍历每个构造方法

    7. 如果不是调用getBean方法时所指定的构造方法参数值,那么则根据构造方法参数类型找值

    8. 如果时调用getBean方法时所指定的构造方法参数值,就直接利用这些值

    9. 如果根据当前构造方法找到了对应的构造方法参数值,那么这个构造方法就是可用的,但是不一定这个构造方法就是最佳的,所以这里会涉及到是否有多个构造方法匹配了同样的值,这个时候就会用值和构造方法类型进行匹配程度的打分,找到一个最匹配的

2.4、为什么分越少优先级越高?

主要是计算找到的bean和构造方法参数类型匹配程度有多高。

假设bean的类型为A,A的父类是B,B的父类是C,同时A实现了接口D

  • 如果构造方法的参数类型为A,那么完全匹配,得分为0

  • 如果构造方法的参数类型为B,那么得分为2

  • 如果构造方法的参数类型为C,那么得分为4

  • 如果构造方法的参数类型为D,那么得分为1/Spring推断构造方法底层执行流程

image.png

三、@Lookup

我们都知道,Spring 的Bean默认是单例的,当在不同的地方获取这个Bean的时候都是同一个。
但是如果我们指定了"prototype"原型模式呢。结果是两个变量在不同的方法中使用的时候还是同一个。例如:
@Component
@Scope("prototype")
public class OrderService {

}

@Component
public class UserService {
	@Autowired
	private OrderService orderService;

	public void test(){
		System.out.println(orderService);
	}
}

public static void main(String[] args) {
    // 创建一个Spring容器
    AnnotationConfigApplicationContext applicationContext =
        new AnnotationConfigApplicationContext(AppConfig.class);

    UserService userService = (UserService) applicationContext.getBean("userService");
    System.out.println(userService);
    userService.test();
    userService.test();
    userService.test();
}

image.png

由于UserService是单例类,只生成了一次,导致OrderService也只生成一次、且UserService没有被CGLIB动态代理。可以通过@Lookup来解决

@Component
public class UserService {
	@Autowired
	private OrderService orderService;

	public void test(){
		System.out.println(a());
	}

	@Lookup
	public OrderService a(){
		return null;
	}
}

image.png

使用注解@Lookup后问题解决,并且UserService被被CGLIB动态代理。

源码分析

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors方法中会遍历方法,查看是否写了@Lookup,利用@Lookup里面配置的值和当前方法封装成LookupOverride并设置到BeanDefinition的methodOverrides中。
// Let's check for lookup methods here...
if (!this.lookupMethodsChecked.contains(beanName)) {
    // 判断beanClass是不是java.开头的类,比如String
    if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
        try {
            Class<?> targetClass = beanClass;
            do {
                // 遍历targetClass中的method,查看是否写了@Lookup方法
                ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                    Lookup lookup = method.getAnnotation(Lookup.class);
                    if (lookup != null) {
                        Assert.state(this.beanFactory != null, "No BeanFactory available");

                        // 将当前method封装成LookupOverride并设置到BeanDefinition的methodOverrides中
                        LookupOverride override = new LookupOverride(method, lookup.value());
                        try {
                            RootBeanDefinition mbd = (RootBeanDefinition)
                                this.beanFactory.getMergedBeanDefinition(beanName);
                            mbd.getMethodOverrides().addOverride(override);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(beanName,
                                                            "Cannot apply @Lookup to beans without corresponding bean definition");
                        }
                    }
                });
                targetClass = targetClass.getSuperclass();
            }
            while (targetClass != null && targetClass != Object.class);

        }
        catch (IllegalStateException ex) {
            throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
        }
    }
    this.lookupMethodsChecked.add(beanName);
}

寻找到以后,Spring会通过org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateBean方法中

调用默认使用是CglibSubclassingInstantiationStrategy的instantiate方法,实际使用的SimpleInstantiationStrategy的的instantiate方法。
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    // Don't override the class with CGLIB if no overrides.
    // 判断当前BeanDefinition对应的beanClass中是否存在@Lookup标记的方法
    // bd对象定义里,是否包含MethodOverride列表;Spring有两个标签参数会产生MethodOverrides 分别是lookup-method,replaced-method
    // 没有MethodOverride对象,可以直接实例化
    if (!bd.hasMethodOverrides()) {
        // 实例化对象的构造方法
        Constructor<?> constructorToUse;
        // 锁定对象,使获得实例化构造方法线程安全
        synchronized (bd.constructorArgumentLock) {
            // 查看bd对象里是否含有无参构造方法。
            constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
            // 没有就生成无参构造方法
            if (constructorToUse == null) {
                final Class<?> clazz = bd.getBeanClass();
                if (clazz.isInterface()) {
                    throw new BeanInstantiationException(clazz, "Specified class is an interface");
                }
                try {
                    if (System.getSecurityManager() != null) {
                        constructorToUse = AccessController.doPrivileged(
                            (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                    }
                    else {
                        constructorToUse = clazz.getDeclaredConstructor();
                    }
                    // 生成成功后,赋值给bd对象,后面使用
                    bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                }
                catch (Throwable ex) {
                    throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                }
            }
        }
        // 反射生成对象
        return BeanUtils.instantiateClass(constructorToUse);
    }
    else {
        // Must generate CGLIB subclass.
        // 如果存在@Lookup,则会生成一个代理对象
        return instantiateWithMethodInjection(bd, beanName, owner);
    }
}

判断当前BeanDefinition对应的beanClass中是否存在@Lookup标记的方法。如果有进入CglibSubclassingInstantiationStrategy#instantiateWithMethodInjection方法生成代理对象。

@Override
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    return instantiateWithMethodInjection(bd, beanName, owner, null);
}

@Override
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
                                                @Nullable Constructor<?> ctor, Object... args) {

    // 生成代理对象
    return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
}

InstantiationStrategy这个接口有2个实现类

  • CglibSubclassingInstantiationStrategy

  • SimpleInstantiationStrategy

CglibSubclassingInstantiationStrategy 继承 SimpleInstantiationStrategy 继承 InstantiationStrategy。
CglibSubclassingInstantiationStrategy 可以产生一个Cglib代理对象。
SimpleInstantiationStrategy 就是通常的实例化,也就是利用构造方法来创建对象。

image.png

public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
    Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
    Object instance;
    if (ctor == null) {
        instance = BeanUtils.instantiateClass(subclass);
    }
    else {
        try {
            Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
            // 得到代理对象
            instance = enhancedSubclassConstructor.newInstance(args);
        }
        catch (Exception ex) {
            throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
                                                 "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
        }
    }
    // SPR-10785: set callbacks directly on the instance instead of in the
    // enhanced class (via the Enhancer) in order to avoid memory leaks.
    Factory factory = (Factory) instance;
    factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
                                         new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
                                         new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
    return instance;
}

这里的LookupOverrideMethodInterceptor该类中的intercept方法就是对@Lookup的处理。

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
    // Cast is safe, as CallbackFilter filters are used selectively.
    // method代表当前执行的方法,有没有对应的LookupOverride对象,如果有要进行处理。
    LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
    Assert.state(lo != null, "LookupOverride not found");
    Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
    if (StringUtils.hasText(lo.getBeanName())) {
        // 获取LookupOverride对象中的BeanName.去容器中获取Bean,然后返回
        Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
                       this.owner.getBean(lo.getBeanName()));
        // Detect package-protected NullBean instance through equals(null) check
        return (bean.equals(null) ? null : bean);
    }
    else {
        // Find target bean matching the (potentially generic) method return type
        ResolvableType genericReturnType = ResolvableType.forMethodReturnType(method);
        return (argsToUse != null ? this.owner.getBeanProvider(genericReturnType).getObject(argsToUse) :
                this.owner.getBeanProvider(genericReturnType).getObject());
    }
}

method代表当前执行的方法,有没有对应的LookupOverride对象,如果有要进行处理。获取LookupOverride对象中的BeanName.去容器中获取Bean,然后返回。

被@Lookup标记方法本身是不会被执行的,拿到对应的Bean后就会返回。
使用@Lookup方法注入的方法需要满足以下语法要求
  • public|protected:要求方法必须是可以被子类重写和调用的。

  • abstract:可选,如果是抽象方法,CGLIB的动态代理类就会实现这个方法,如果不是抽象方法,不会真正的调用这个方法,而是进行覆盖。

  • return-type:是非单例的类型。

  • no-arguments:不允许有参数。

四、总结

Spring中的一个bean,需要实例化得到一个对象,而实例化就需要用到构造方法。

一般情况下,一个类只有一个构造方法:

  1. 要么是无参的构造方法

  2. 要么是有参的构造方法

4.1、一个构造方法的情况

如果只有一个无参的构造方法,那么实例化就只能使用这个构造方法了。

如果只有一个有参的构造方法,那么实例化时能使用这个构造方法吗?要分情况讨论:

  1. 使用AnnotationConfigApplicationContext,会使用这个构造方法进行实例化,那么Spring会根据构造方法的参数信息去寻找bean,然后传给构造方法。

  2. 使用ClassPathXmlApplicationContext,表示使用XML的方式来使用bean,要么在XML中指定构造方法的参数值(手动指定),要么配置autowire=constructor让Spring自动去寻找bean做为构造方法参数值。

4.2、多个构造方法的情况

一个类存在多个构造方法,那么Spring进行实例化之前,该如何去确定到底用哪个构造方法呢?

  1. 如果开发者指定了想要使用的构造方法,那么就用这个构造方法。

  2. 如果开发者没有指定想要使用的构造方法,则看开发者有没有让Spring自动去选择构造方法。

  3. 如果开发者也没有让Spring自动去选择构造方法,则Spring利用无参构造方法,如果没有无参构造方法,则报错。

针对第一点,开发者可以通过什么方式来指定使用哪个构造方法呢?

  1. xml中的<constructor-arg>标签,这个标签表示构造方法参数,所以可以根据这个确定想要使用的构造方法的参数个数,从而确定想要使用的构造方法。

  2. 通过@Autowired注解,@Autowired注解可以写在构造方法上,所以哪个构造方法上写了@Autowired注解,表示开发者想使用哪个构造方法,当然,它和第一个方式的不同点是,通过xml的方式,我们直接指定了构造方法的参数值,而通过@Autowired注解的方式,需要Spring通过byType+byName的方式去找到符合条件的bean作为构造方法的参数值。

第二点,如果开发者没有指定想要使用的构造方法呢?

则看开发者有没有让Spring自动去选择构造方法,对于这一点,只能用在ClassPathXmlApplicationContext,因为通过AnnotationConfigApplicationContext没有办法去指定某个bean可以自动去选择构造方法,而通过ClassPathXmlApplicationContext可以在xml中指定某个bean的autowire为constructor,虽然这个属性表示通过构造方法自动注入,所以Spring需要自动的去选择一个构造方法进行自动注入,因为是构造方法,所以顺便是进行实例化。

还有一种情况就是多个构造方法上写了@Autowired注解,那么此时Spring会报错。

但是,因为@Autowired还有一个属性required,默认为ture,所以一个类中,只有能一个构造方法标注了@Autowired或@Autowired(required=true),有多个会报错。但是可以有多个@Autowired(required=false),这种情况下,需要Spring从这些构造方法中去自动选择一个构造方法。

版权声明

非特殊说明,本文由Zender原创或收集发布,欢迎转载。

评论列表
  •   唐宁府  发布于 2023-06-08 11:17:58  回复该评论
    @Lookup有一点没看明白,调用UserService的a()方法,实际是调用的是LookupOverrideMethodInterceptor.intercept()去spring容器中去创建原型bean吗?
    •   zender  发布于 2023-06-10 09:39:46  回复该评论
      是的,方法内部调用a()方法就会调用LookupOverrideMethodInterceptor.intercept()获取新的原型bean。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

作者文章
热门