Spring源码学习(十)--推断构造方法

前言

 

之前有篇文章是关于Bean的生命周期,Spring在扫描完成生成BeanDefinition后会去创建bean,Spring在加载beanClass后会进行实例化,就是需要利用该类的构造方法来实例化得到一个对象,因为之前学习和调试过程中自己没有在代码中去写构造方法,都是使用默认的无参构造方法,但是如果一个类存在多个构造方法,就需要进行构造方法的推导。

首先,我们先看一下在javaConfig和xml场景中各种情况下Spring最终选择的哪个构造方法来进行实例化的,之后在看源码是如何实现的。

JavaConfig--构造方法选择

使用javaConfig启动Spring容器

//加载spring上下文
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(MyConfig.class);

UserService userService = (UserService) context.getBean("userService");
System.out.println(userService);

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

Spring源码学习(十)--推断构造方法_第1张图片

Spring源码学习(十)--推断构造方法_第2张图片

2.只有一个有参构造方法,不管构造方法参数是一个还是多个,那么Spring会根据构造方法的参数信息去寻找bean,然后传给构造方法(前提是根据参数类型或者名字可以找到唯一的bean)

 构造方法有一个参数Spring源码学习(十)--推断构造方法_第3张图片

Spring源码学习(十)--推断构造方法_第4张图片

 构造方法有两个参数Spring源码学习(十)--推断构造方法_第5张图片

 Spring源码学习(十)--推断构造方法_第6张图片

 3.多个构造方法,并且开发者指定了想使用的构造方法,那么就用这个构造方法

通过@Autowired注解,@Autowired注解可以写在构造方法上,所以哪个构造方法上写了@Autowired注解,表示开发者想使用哪个构造方法。通过@Autowired注解的方式,需要Spring通过byType+byName的方式去找到符合条件的bean作为构造方法的参数值,当然找不到是要报错的

Spring源码学习(十)--推断构造方法_第7张图片

 

 但是只有能一个构造方法标注了@Autowired或@Autowired(required=true),有多个会报错。

Spring源码学习(十)--推断构造方法_第8张图片

Spring源码学习(十)--推断构造方法_第9张图片要注意的是,如果一个bean中的构造方法使用了@AutoWired修饰了,那么这个bean的其他构造方法不能再加@Autowired注解了,因为既然使用@AutoWried来决定使用的构造方法,那么你多个构造方法都用@Autowried修饰,那么你是几个意思?就算@AutoWried的required为false也不行。在一个bean中的构造方法,只能存在一个@AutoWried,属性required为true的一个构造方法,存在了一个@AutoWried,属性为required=true的构造方法,就不能再有其他的@AutoWried,即使其他的required为false也不行。这是spring的设计者规定的
Spring源码学习(十)--推断构造方法_第10张图片

Spring源码学习(十)--推断构造方法_第11张图片

 但是可以有多个@Autowired(required=false),这种情况下,需要Spring从这些构造方法中去自动选择一个构造方法。Spring源码学习(十)--推断构造方法_第12张图片

 Spring源码学习(十)--推断构造方法_第13张图片

至于是如何选择的,我们下面看源码。 

 4.多个构造方法,但是开发者没有指定构造方法,就要看有没有无参构造方法

   有无参构造方法,Spring会使用无参构造方法

Spring源码学习(十)--推断构造方法_第14张图片

Spring源码学习(十)--推断构造方法_第15张图片

没有无参构造方法,则报错

 Spring源码学习(十)--推断构造方法_第16张图片

 Spring源码学习(十)--推断构造方法_第17张图片

当然如果多个构造方法,开发者通过getBean传入参数并且bean是原型或者懒加载的也是可以的 

Spring源码学习(十)--推断构造方法_第18张图片

 Spring源码学习(十)--推断构造方法_第19张图片

那么这个时候会去执行一个参数的构造方法,但是需要你的bean的作用域是原型或者懒加载的,因为单例的在容器启动就会实例化推导构造方法,这个时候是无法确定的,只有原型的或者懒加载的时候每次getBean去获取的时候才行。 

 当然,getbean传入的参数个数和类型要和构造方法对应,否则也会报错

也可以通过BeanDefinition来设置构造方法的入参来选择对应的构造方法

Spring源码学习(十)--推断构造方法_第20张图片

Spring源码学习(十)--推断构造方法_第21张图片 Spring源码学习(十)--推断构造方法_第22张图片

 如果通过beanDefinition设置的构造方法参数和UserService中的构造方法无法相对应,则会报错

Spring源码学习(十)--推断构造方法_第23张图片

Spring源码学习(十)--推断构造方法_第24张图片

Spring源码学习(十)--推断构造方法_第25张图片

但是beanDefinition设置注入方式为构造方法注入,spring会自动选择构造方法

Spring源码学习(十)--推断构造方法_第26张图片 Spring源码学习(十)--推断构造方法_第27张图片

xml--构造方法选择

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

UserService userService = (UserService) context.getBean("userService");
System.out.println(userService);

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

public class UserService{

	private OrderService orderService;

	public UserService() {
		System.out.println(0);
	}

}

Spring源码学习(十)--推断构造方法_第28张图片

Spring源码学习(十)--推断构造方法_第29张图片

2.只有一个有参构造方法,要么在XML中指定构造方法的参数值(手动指定),要么配置autowire=constructor让Spring自动去寻找bean做为构造方法参数值。

public class UserService{

	private OrderService orderService;

	//public UserService() {
	//	System.out.println(0);
	//}

	//public UserService(OrderService orderService) {
	//	this.orderService = orderService;
	//	System.out.println(1);
	//}
	//
	public UserService(OrderService orderService,OrderService orderService1) {
		this.orderService = orderService;
		System.out.println(2);
	}
}

a)XML中指定构造方法的参数值(手动指定)


    
	



Spring源码学习(十)--推断构造方法_第30张图片

 同样,xml设置的参数个数和引用类型要和构造方法相对应

b)配置autowire=constructor

Spring源码学习(十)--推断构造方法_第31张图片

  3.多个构造方法,并且开发者指定了想使用的构造方法,那么就用这个构造方法

xml中的标签,这个标签表示构造方法参数,所以可以根据这个确定想要使用的构造方法的参数个数,从而确定想要使用的构造方法

Spring源码学习(十)--推断构造方法_第32张图片


	



 xml配置中通过构造方法注入一个参数OrderService,所以使用一个参数的构造方法

Spring源码学习(十)--推断构造方法_第33张图片

  4.多个构造方法,但是开发者没有指定构造方法,则看开发者有没有让Spring自动去选择构造方法

在xml中指定某个bean的autowire为constructor,虽然这个属性表示通过构造方法自动注入,所以需要自动的去选择一个构造方法进行自动注入,因为是构造方法,所以顺便是进行实例化。

Spring源码学习(十)--推断构造方法_第34张图片


	


spring会选择一个最多参数的构造方法作为注入点

Spring源码学习(十)--推断构造方法_第35张图片

 4.多个构造方法,没有指定构造方法,也没有让Spring自动去选择构造方法,则Spring利用无参构造方法,如果没有无参构造方法,则报错

推断构造方法源码解析

推断构造方法就在spring实例化bean的时候进行的,实例化bean是在bean的生命周期中的,在spring调用doCreateBean的过程中

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

Spring源码学习(十)--推断构造方法_第36张图片

 在这里对bean进行实例化,在实例化前就需要推到构造方法,实例化都是对构造方法进行推断,推断出一个最符合的构造方法进行实例化

createBeanInstance实例化方法

这个方法的参数这里简单说明下:
beanName:这个是实例化的Bean的名字,也就是beanName
mbd:bean对应的BeanDefinition;
args:这个参数很多情况下都是没有的,只有你通过getBean的时候才会传入,简单说下就是getBean如果传入了构造方法的参数,但是这个bean是懒加载的单例bean或者是原型的bean才会有效果,否则单例bean在实例化的时候是没有参数的,会直接放入单例池。

	/**
	 * Create a new instance for the specified bean, using an appropriate instantiation strategy:
	 * factory method, constructor autowiring, or simple instantiation.
	 * @param beanName the name of the bean
	 * @param mbd the bean definition for the bean
	 * @param args explicit arguments to use for constructor or factory method invocation
	 * @return a BeanWrapper for the new instance
	 * @see #obtainFromSupplier
	 * @see #instantiateUsingFactoryMethod
	 * @see #autowireConstructor
	 * @see #instantiateBean
	 * 创建bean的实例,这里也是spring推断构造方法的核心所在
	 * args:表示程序员通过getBean传入的参数,如果使用getBean(Class reuqireType,Object[]args)
	 * 那么传入的参数就会传入到这里
	 */
	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		//加载class,将Beanclass中的类的全限定名加载到jvm中,得到class对象,使用了的是默认的类加载器或者程序员自己设置的类加载器
		Class beanClass = resolveBeanClass(mbd, beanName);

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

		// BeanDefinition中添加了Supplier,则调用Supplier来得到对象
		Supplier instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}

		// @Bean对应的BeanDefinition
		/**
		 * 这里是处理@Bean,就是说如果在配置类中加了@Bean注解的方法在上一步的配置类解析注册的时候会注册
		 * factoryBean 和factoryMethod,比如Appconfig中有一个@Bean方法test,那么factoryBean就是Appconfig
		 * 而FactoryMethod就是test,这里的逻辑是说如果factoryMethod不为空,那么就是@Bean来创建的,而在上一步的BeanDefinition
		 * 注册的时候是没有去调用@Bean注解的方法,所以@Bean注册的BeanDefinition的beanClass是等于空的,所以这里通过
		 * FactoryBean和factoryMethod去调用方法将返回值作为bean返回对象
		 */
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// Shortcut when re-creating the same bean...
		// 一个原型BeanDefinition,会多次来创建Bean,那么就可以把该BeanDefinition所要使用的构造方法缓存起来,避免每次都进行构造方法推断
		boolean resolved = false;
		boolean autowireNecessary = false;
		//这里表示当你使用getBean传入的参数为空的时候进入下面的这个逻辑
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				// resolvedConstructorOrFactoryMethod 缓存的已经推断出来的构造方法
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					// 缓存的构造方法是否需要注入参数
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		if (resolved) {
			// 如果确定了当前BeanDefinition的构造方法,那么看是否需要进行对构造方法进行参数的依赖注入(构造方法注入)
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				// 如果不需要注入,则表示用的是默认无参构造方法,直接进行实例化
				return instantiateBean(beanName, mbd);
			}
		}

		// Candidate constructors for autowiring?
		// 提供一个扩展点,可以利用SmartInstantiationAwareBeanPostProcessor来控制用beanClass中的哪个构造方法
		/**
		 * 1.bean的后置处理器使用的是SmartInstantiationAwareBeanPostProcessor中的determineCandidateConstructors方法
		 * 这个方法只有AutoWiredBeanPostProcessor实现了它的逻辑,其他子类都没有实现;
		 * 2.在这个后置处理器中去找到构造方法的候选者,简单总结就是:
		 *  a.bean中的构造方法只能有一个@autoWired的构造;
		 *  b.如果有两个构造加了@AutoWired,直接报错;
		 *  c.如果有一个构造@AutoWired=false,加入后续列表,如果这个时候有一个无参数构造,也加入到后续列表,如果只有一个@AutoWired,则返回这个构造;
		 *  d.如果构造方法大于1个,但是都没有@AutoWired,那么返回null;
		 */
		Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		//下面的条件进入autowireConstructor的逻辑为:
		/**
		 * 1.找到的构造方法不为空;
		 * 2.bean的注入模式是通过构造方法注入;
		 * 3.BeanDefinition中添加了构造方法的参数和值;
		 * 4.或者说开发者通过getBean传入了参数的值;
		 * 上面的4个条件只要满足一个就进入autowireConstructor
		 */
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			/**
			 * 构造方法推导
			 * ctors:上面找到的构造方法集合,在这里这个值可能为空null,可能为一个,可能为多个,
			 * 如果有2个以上的构造没有加@AutoWired,那么返回null;
			 * 如果有有@AutoWried的注解,如果包含了required为ture的情况,就只能有一个构造返回,
			 * 如果说@AutoWried都是否false的情况下,那么可能返回多个,在都是false的情况
			 * 下,如果还有一个默认的构造,那么ctors返回的就是@AutoWired的数量+默认的无参数构造函数
			 * args:就是开发者通过getBean传入的参数args
			 * 就是说如果ctors不为null 或者注入模式为构造方法或者xml中《Bean》
			 * 设置了构造参数或者getBean传入的参数不为空都会进入这里。
			 */
			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.
		// 如果没有@Autowired注解的构造方法,当前BeanDefinition的autowiremode也不是AUTOWIRE_CONSTRUCTOR,也没有指明所要用的构造方法参数值
		// 则直接使用无参构造方法
		/**
		 * 这个最后的这个逻辑是spring用来保证bean的实例化在一定程度上能够成功,也就是最后的选择
		 * 如果上面的都不能满足的情况下,用默认的无参数构造方法来实例化,因为上面的筛选条件中如果你定义了2个以上的构造方法,但是没有加@AutoWired,spring就不知道用那个了
		 * 所以返回的ctors为null,比如一个空构造,一个有参数的构造,都没有加@AutoWired
		 * 而且也不能满足一定的条件进入autowireConstructor,所以代码逻辑就到了这里,就调用一个默认的构造
		 */
		return instantiateBean(beanName, mbd);
	}
  1. AbstractAutowireCapableBeanFactory类中的createBeanInstance()方法会去创建一个Bean实例
  2. 根据BeanDefinition加载类得到Class对象
  3. 如果BeanDefinition绑定了一个Supplier,那就调用Supplier的get方法得到一个对象并直接返回
  4. 如果BeanDefinition中存在factoryMethodName,那么就调用该工厂方法得到一个bean对象并返回
  5. 如果BeanDefinition已经自动构造过了,那就调用autowireConstructor()自动构造一个对象
  6. 调用SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors()方法得到哪些构造方法是可以用的
  7. 如果存在可用得构造方法,或者当前BeanDefinition的autowired是AUTOWIRE_CONSTRUCTOR,或者BeanDefinition中指定了构造方法参数值,或者创建Bean的时候指定了构造方法参数值,那么就调用**autowireConstructor()**方法自动构造一个对象
  8. 最后,如果不是上述情况,就根据无参的构造方法实例化一个对象

这里的源码只关注下大概的流程解析,太细的地方需要下面细细研究。 

Supplier用法

上面的代码中有一个Supplier的用法,这里单独来说明一下,就是说我们可以通过手动注册一个bean,但是这个bean我不想要spirng来给实例化,需要我自己来实例化,也就是我们不想要spring去推断构造方法了,开发者自己去实例化,用法简单了解一下:

Spring源码学习(十)--推断构造方法_第37张图片

就是可手动注册一个bean,在BeanDefinition中设置了一个属性InstanceSupplier,当spring执行到

Spring源码学习(十)--推断构造方法_第38张图片

这里的时候判断你是否设置了Supplier类型的bean,如果是的话,那么就会调用Supplier中的get方法直接返回一个实例化后的对象,也就不需要spring进行推断了。 

@AutoWired后置处理器推断构造方法

在推断构造方法的时候,spring会先通过bean的后置处理器先去确定一些符合条件的构造方法,然后再对这些构造方法进行判断;

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors

@Override
@Nullable
public Constructor[] determineCandidateConstructors(Class beanClass, final String beanName)
		throws BeanCreationException {

	// Let's check for lookup methods here...
	//这个是Lockup的逻辑,现在这里暂时不用管
	if (!this.lookupMethodsChecked.contains(beanName)) {
		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.
	//决定一组构造方法的逻辑代码,candidateConstructorsCache是一个构造方法候选者的集合,就是说如果找到了符合条件的构造方法
	//都会缓存到这个集合中,表示是符合条件的构造方法的候选者集合
	//这里先从缓存中去取,如果缓存中没有,就去推断出符合的记录添加到缓存中
	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 {
					//这个是得到一个Bean中的声明的所有的构造方法列表,是一个数组,数组里面是bean中的所有构造方法
					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> candidates = new ArrayList<>(rawCandidates.length);
				// 用来记录@AutoWired标记并且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是代理类,则得到被代理类的类型
						// 然后去看被代理类中对应的构造方法是否有@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...
							}
						}
					}
					// 当前构造方法加了@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);
						}
						boolean required = determineRequiredStatus(ann);
						// required为true
						if (required) {
							// 如果已经有@AutoWired注解标注的构造方法,则抛错
							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);
					}
					// 如果没有被@AutoWired注解标记,并且是无参的,则赋值给defaultConstructor
					else if (candidate.getParameterCount() == 0) {
						// 记录唯一一个无参的构造方法
						defaultConstructor = candidate;
					}
					// 从以上代码可以看出,一个类中要么只能有一个required=true的构造方法,要么只能有一个或多个required=false的构造方法
				}
				// 循环完所有的构造方法,
				// 1.candidates要么为空,也就是没有被@Autowired标记的构造方法
				// 2.candidates中只有一个required=true的构造方法
				// 3.candidates中是所有required=fals的构造方法

				// 如果candidates不为空,那么就是有@Autowired标记的构造方法
				if (!candidates.isEmpty()) {
					// Add default constructor to list of optional constructors, as fallback.
					// 如果不存在一个required=true的构造方法,那么candidates中都是required=false的构造方法
					if (requiredConstructor == null) {
						// 如果存在无参构造方法将无参构造方法放到candidates中
						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));
						}
					}
					// 1.如果只存在一个required=true的构造方法,那么只有这一个是合格的
					// 2.如果有多个required=false的构造方法,那么所有的required=false的构造方法都是合格的,
					//   此时如果有无参构造方法,那么所有required=false和无参构造方法都是合格的
					candidateConstructors = candidates.toArray(new Constructor[0]);
				}
				// 如果没有加了@AutoWired注解的构造方法,并且类中只有一个构造方法,并且该构造方法是有参的,这个构造方法也是合格的
				else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
					candidateConstructors = new Constructor[] {rawCandidates[0]};
				}
				// primaryConstructor 不用管kotlin
				else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
						defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
					candidateConstructors = new Constructor[] {primaryConstructor, defaultConstructor};
				}
				// primaryConstructor 不用管kotlin
				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);
}

Spring源码学习(十)--推断构造方法_第39张图片

Spring源码学习(十)--推断构造方法_第40张图片

当返回的ctors为null时,假如没有设置构造注入,beanDefinition也没有设置构造方法参数,并且getBean也没有传入参数,那么会执行instantiateBean(beanName, mbd);这个方法就是根据默认无参构造方法去实例化,

上面分析ctors为null有两种情况:只有一个无参构造方法,这种情况就直接使用无参构造方法;

有多个构造方法,这种情况有无参构造方法则实例化返回,没有无参构造方法直接报错

如果返回的ctors不为null,或者有其他设置的话,就会走autowireConstructor(beanName, mbd, ctors, args)方法,这个方法就是Spring去根据筛选出来的某些构造方法以及程序员的某些设置去选择一个构造方法去实例化。

构造方法自动注入autowireConstructor

org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor

这个方法的参数这里简单说明下:
beanName:这个是实例化的Bean的名字,也就是beanName
mbd:bean对应的BeanDefinition;
chosenCtors:通过Autowired后置处理器筛选出来的构造方法,如果不为null,Spring会从这些构造方法中去选择一个实例化
explicitArgs:这个参数很多情况下都是没有的,只有你通过getBean的时候才会传入

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

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

	//构造方法,真正使用的构造方法,最后使用的
	Constructor constructorToUse = null;
	//参数holder对象
	ArgumentsHolder argsHolderToUse = null;
	//参数的对象列表,真正调用构造方法传进去的参数数组
	Object[] argsToUse = null;

	// 如果getBean()传入了args,那构造方法要用的入参就直接确定好了
	if (explicitArgs != null) {
		argsToUse = explicitArgs;
	}
	else {
		// 从缓存中获取构造方法和参数列表,如果第一次肯定是没有缓存的,
		// 如果是第二次,相同的这个bd肯定是可以从缓存获取的,这里如果没有缓存,本次执行完成过后会放入到缓存中
		Object[] argsToResolve = null;
		synchronized (mbd.constructorArgumentLock) {
			constructorToUse = (Constructor) mbd.resolvedConstructorOrFactoryMethod;
			if (constructorToUse != null && mbd.constructorArgumentsResolved) {
				// Found a cached constructor...
				argsToUse = mbd.resolvedConstructorArguments;
				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.
		//chosenCtors是这个方法传入的参数,就是前面的方法中通过bean的后置处理器去决定构造方法得到的构造方法列表,是可能为空的
		Constructor[] candidates = chosenCtors;
		//这个if就是为了构建我们的构造方法列表,如果没有,就重新从beanclass中获取构造方法列表
		if (candidates == null) {
			/**
			 * 在bean的后置处理器中得到的构造方法为空的情况下,就是你的bean中的构造方法定义了至少两个,都没有@AutoWired注解,
			 * 或者,只有一个无参构造方法,而这个时候有可能是自动注入的或者开发者getBean传入了参数
			 * 或者xml中在BeanDefinition中设置了入参的参数,所以会进到这里
			 */
			Class beanClass = mbd.getBeanClass();
			try {
				//那么这里得到beanclass中的所有构造方法列表,isNonPublicAccessAllowed表示是获取声明的构造方法列表,否则就是默认所有的构造方法列表
				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);
			}
		}

		//如果候选的构造方法列表中只有一个并且开发者没有传入参数并且在BeanDefinition中没有设置参数(典型的就是在xml中没有设置
		//constructor-arg,什么意思呢??就是说spring给我们找到了一个构造方法,而且开发者没有传入参数,在定义bean的
		//过程中也没有设置参数,那么就进入下面的逻辑

		// 如果只有一个候选构造方法,并且没有指定所要使用的构造方法参数值,并且该构造方法是无参的,那就直接用这个无参构造方法进行实例化了
		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;
				}
				// 使用该无参构造方法实例化
				/**
				 * 简单来说就是spring找到了一个构造方法,这个构造方法是一个默认的无参数构造,开发者没有传入参数
				 * BeanDefinition中也没有设置参数,就直接调用默认的构造去实例化
				 */
				bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
				return bw;
			}
		}

		// Need to resolve the constructor.
		//如果上面的没有满足,代码逻辑到这里,如果说获取的构造方法不为空,或者设置的注入模式是构造方法注入
		// autowiring=true:1.多个构造方法但是开启了构造注入 2.只有一个有参构造方法
		// 3.多个required=false有参构造方法+无参
		boolean autowiring = (chosenCtors != null ||
				mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
		ConstructorArgumentValues resolvedValues = null;

		// 确定要选择的构造方法的参数个数的最小值,后续判断候选构造方法的参数个数如果小于minNrOfArgs,则直接pass掉

		// 如果开发者传入了参数,那么这个值就是参数的个数,如果没有传入,那么spring就要去推导怎么取值
		// 比如在xml中设置了constructor-arg 中的index=‘1’,那么spring就知道这个构造函数的参数至少为2,所以这个
		// 值就是2

		int minNrOfArgs;
		if (explicitArgs != null) {
			// 如果直接传了构造方法参数值,这个值就是开发者传入的参数的个数,所用的构造方法的参数个数肯定不能少于这个值
			minNrOfArgs = explicitArgs.length;
		}
		else {
			// 如果通过BeanDefinition传了构造方法参数值,因为有可能是通过下标指定了,比如0位置的值,2位置的值,虽然只指定了2个值,但是构造方法的参数个数至少得是3个
			// beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(2,new OrderService());
			/**
			 * 比如bean中有两个构造,一个构造2个参数,一个构造3个参数,其中第二个参数的类型一致,那么你在xml中注入的时候
			 * ,那么spring到底是注入第二个构造,还是第三个呢?
			 * 所以这段代码就是spring去得到参数个数的最小值,就是说至少有必须要有几个参数
			 * getConstructorArgumentValues就是都是可以设置构造方法参数值的
			// resolveConstructorArguments除了计算构造方法需要的最少参数个数,也会解析设置的构造方法参数得到bean对象
			// new RuntimeBeanReference("orderService") 就是在spring容器中找beanName=orderService的bean对象
			minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
		}
		// 以上会得到一个构造方法参数的最小值,也就是说最终确定的构造方法的参数,是要大于等于这个值的

		// 对候选构造方法进行排序,public的方法排在最前面,都是public的情况下参数个数越多越靠前
		AutowireUtils.sortConstructors(candidates);
		int minTypeDiffWeight = Integer.MAX_VALUE;
		Set> ambiguousConstructors = null;
		Deque 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();
			// resolvedValues这个值是在上面设置的,设置的条件就是开发者没有传入参数设置的一个对象,换句话说就是
			// 下面的if进入的条件就是开发者没有传入参数
			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);
						}
					}

					// 通过方法参数的名字和类型和构造方法去获取每个参数对应的value,也就是这些value都在bean工厂中
					/// 最后返回一个argsHolder对象,作为整个构造方法,构造参数的一个对象,最后会根据权重来计算
					// 该使用哪个构造方法和参数,这里面也是先byType,再byName
					// 如果通过BeanDefinition指定构造方法参数值,就优先使用指定的参数值
					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.
				// 在调getBean方法是传入了参数值,那就表示只能用对应参数个数的构造方法
				// 如果getBean传入了两个参数,那么只能用有两个参数的构造方法
				// 如果循环完一个都没有,constructorToUse=null,下面会报错
				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);
			}
		}
		// 遍历结束

		// 如果没有可用的构造方法,就取记录的最后一个异常并抛出
		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");
	//最后通过确定的构造方法,参数调用实例化完成Bean对象的赋值
	bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
	return bw;
}

Spring源码学习(十)--推断构造方法_第41张图片

@Bean的情况

首先,Spring会把@Bean修饰的方法解析成BeanDefinition:

  1. 如果方法不是static的,那么解析出来的BeanDefinition中:
    1. factoryBeanName为AppConfig所对应的beanName,比如"appConfig"
    2. factoryMethodName为对应的方法名,比如"aService"
    3. factoryClass为AppConfig.class
  2. 如果方法是static的,那么解析出来的BeanDefinition中:
    1. factoryBeanName为null
    2. factoryMethodName为对应的方法名,比如"aService"
    3. factoryClass也为AppConfig.class

在由@Bean生成的BeanDefinition中,有一个重要的属性isFactoryMethodUnique,表示factoryMethod是不是唯一的,在普通情况下@Bean生成的BeanDefinition的isFactoryMethodUnique为true,但是如果出现了方法重载,那么就是特殊的情况,比如:

	@Bean
	public static AService aService(){
		return new AService();
	}

	@Bean
	public AService aService(BService bService){
		return new AService();
	}

虽然有两个@Bean,但是肯定只会生成一个aService的Bean,那么Spring在处理@Bean时,也只会生成一个aService的BeanDefinition,比如Spring先解析到第一个@Bean,会生成一个BeanDefinition,此时isFactoryMethodUnique为true,但是解析到第二个@Bean时,会判断出来beanDefinitionMap中已经存在一个aService的BeanDefinition了,那么会把之前的这个BeanDefinition的isFactoryMethodUnique修改为false,并且不会生成新的BeanDefinition了。​

并且后续在根据BeanDefinition创建Bean时,会根据isFactoryMethodUnique来操作,如果为true,那就表示当前BeanDefinition只对应了一个方法,那也就是只能用这个方法来创建Bean了,但是如果isFactoryMethodUnique为false,那就表示当前BeanDefition对应了多个方法,需要和推断构造方法的逻辑一样,去选择用哪个方法来创建Bean。

你可能感兴趣的:(Spring源码,spring,java,源码)