spring源码 第二个后置处理器方法的使用-推断构造函数

spring一个bean初始化以及销毁的流程中,有九个后置处理器需要执行,这篇博客主要记录第二个后置处理器推断构造函数的后置处理器的源码逻辑

第一个后置处理器的调用

在第一个后置处理器被调用之前,会进行一系列的判断

会先调用第一个后置处理器,判断是否返回了bean,如果返回了bean,且不为null,就直接调用第八个后置处理器,完成bean的代理,然后return了之后,就结束了一个bean的生命周期,也即:一个bean就初始化完成了
第一个后置处理器是:org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation,spring自带的后置处理器,几乎上都是返回的null

第八个后置处理器是:org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization

/**
 * @param beanName
 * @param mbd
 * @return
 * 这里面比较重要的变量:
 *  beforeInstantiationResolved:
 *  isSynthetic():判断当前bean是否是合成bean
 *   synthetic
 *    和spring中的合成类、合成方法、合成属性有关系
 *
 *  这里的意思是:在开始spring生命周期方法之前,先判断是否返回了bean对象,如果返回了,就不再调用bean的生命周期方法
 *   进行判断的前提是:当前bean不是合成类,并且当前spring容器中有InstantiationAwareBeanPostProcessor的实现类
 *   hasInstantiationAwareBeanPostProcessor是在refresh方法中,执行 registerBeanPostProcessors()方法的时候,
 *   会把bean的后置处理器添加到 beanFactory中,添加的时候,会判断当前bean是否是接口的实现类
 */
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    
    
	Object bean = null;
	if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
    
    
		// Make sure bean class is actually resolved at this point.
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    
    
			Class<?> targetType = determineTargetType(beanName, mbd);
			if (targetType != null) {
    
    
				bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
				if (bean != null) {
    
    
					bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
				}
			}
		}
		mbd.beforeInstantiationResolved = (bean != null);
	}
	return bean;
}

第二个后置处理器的调用

首先第二个后置处理器的调用是在这个方法中
这个方法中,主要完成了以下几个操作
1.对bean的访问权限进行判断
2.处理InstanceSupplier
3.处理factoryMethod
4.调用第二个后置处理器,我们姑且可以认为第二个后置处理器是对手动注入的推断,也就是@Autowired注解加在构造函数上的推断,推断出来哪些构造函数可以使用
这里推断出来的可能是一个,也可能是多个
5.如果推断出来有多个可用或者自动注入模型是3,autowire_construc,那就进行二次推断,也即根据:参数类型,推断出来一个最合适的
6.如果不满足第5个条件,就通过无参构造函数,完成bean的初始化
需要注意的是:这个方法只是完成了实例化,我所理解的实例化的意思是:生成一个对象,还没有完成对象的初始化方法回调、动态代理对象的生成等

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

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    
    
	// Make sure bean class is actually resolved at this point.
	Class<?> beanClass = resolveBeanClass(mbd, beanName);

	//mpy 检测一个类的访问权限,spring默认情况下对于非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());
	}

	/**
	 * 如果在bean对应的beanDefinition中,设置一个InstanceSupplier,那么,spring就不会调用后面推断构造函数的逻辑
	 */
	Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
	if (instanceSupplier != null) {
    
    
		return obtainFromSupplier(instanceSupplier, beanName);
	}

	/**
	 * mpy 如果工厂方法不为空,就通过工厂方法构建bean对象
	 * 需要了解什么是factoryMethod
	 *
	 * 如果我们是通过@Bean注解,来注入bean的话,在将bean对象转换成beanDefinition对象时,会设置一个factoryMethod
	 */
	if (mbd.getFactoryMethodName() != null)  {
    
    
		return instantiateUsingFactoryMethod(beanName, mbd, args);
	}

	// Shortcut when re-creating the same bean...
	/**
	 *
	 * 当多次构建同一个bean的时候,可以使用这个shortcut。
	 * 也就是说,如果第一次解析一个bean,推断出来,后面再来对bean进行推断的时候  就无需再次执行所有代码了
	 * 多次构建同一个bean的话,只有在原型模式下才会调用多次推断构造函数,因为单例方式只会有一个,所以
	 * 下面这个代码我觉得我们可以认为只有在原型模式下,才会走下面从mbd中获取resolvedConstructorOrFactoryMethod的逻辑
	 */
	boolean resolved = false;
	boolean autowireNecessary = false;
	if (args == null) {
    
    
		synchronized (mbd.constructorArgumentLock) {
    
    
			//如果bean是prototype,那么第二次获取bean的时候,会从这里来获取
			if (mbd.resolvedConstructorOrFactoryMethod != null) {
    
    
				resolved = true;
				autowireNecessary = mbd.constructorArgumentsResolved;
			}
		}
	}
	//如果要构造的bean是单实例的,resolved永远是 false
	if (resolved) {
    
    
		if (autowireNecessary) {
    
    
			//mpy  通过构造方法自动装配的方式构造bean对象
			return autowireConstructor(beanName, mbd, null, null);
		}
		else {
    
    
			//mpy 通过默认的无参构造方法构造bean对象
			return instantiateBean(beanName, mbd);
		}
	}

	// Candidate constructors for autowiring?
	/**
	 * mpy 实例化对象里面:第二次调用后置处理器  由后置处理器决定返回哪些构造函数(推断使用哪个构造方法)
	 *  当注入模型为0 的时候 就是autowireMode = 0;无论类中提供了多少个构造函数  这里返回的ctors都是0
	 *
	 *  autowireConstructor()方法确切的说,是从推断出来的构造函数中,选择出一个,判断到底要用哪个来初始化
	 *  	如果没有指定注入模型,推断出来返回了一个或者多个可用的构造函数,也会执行这个方法
	 *  	如果指定了注入模型,即使推断出来的是null(也即使用空参构造函数),也会执行这个方法
	 *
	 *  所以:
	 *  	determineConstructorsFromBeanPostProcessors:可以理解为手动装配的构造函数推断
	 *  	autowireConstructor:可以理解为自动装配的构造函数推断(这样说,也有歧义,如果注入模型是0,但是返回的ctors不是null,也会进到这个方法中)
	 *  因为:determineConstructorsFromBeanPostProcessors在执行之后,推断出来使用空参构造函数、或者使用带参构造函数,如果autowiredMode为3,在autowireConstructor还会推断一次
	 *  autowireConstructor方法中,无论ctors为null还是有值,都会再进行一次推断
	 *
	 *
	 *  根据源码来看,ctors会返回一个或者多个或者返回null的场景,
	 *  	返回一个:要么是加了@Autowired注解且require是true的构造函数 ; 或者是没有@Autowired注解,只有一个构造函数,且入参大于0
	 *  	返回多个是:多个@Autowired注解且require都是false的场景;会返回所有的@Autowired + 无参构造函数(无参构造函数存在就返回,不存在就不返回)
	 *  	返回null:没有@Autowired注解,且没有带参构造函数
	 * 判断条件中的args,一般情况下是null,至于mbd.hasConstructorArgumentValues()这个判断,我还没   * 有搞懂,所以暂时先跳过这个条件的判断
	 */
	Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
	if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
			mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
    
    
		return autowireConstructor(beanName, mbd, ctors, args);
	}

	// No special handling: simply use no-arg constructor.
	/**
	 * 使用默认的构造函数初始化
	 *
	 * 如果bean的autowireMode为0,且没有在构造方法指定@Autowired注解,表示不自动注入,那么,一定是会执行这里的方法的,也即:通过默认的
	 * 无参构造函数执行
	 */
	return instantiateBean(beanName, mbd);
}

无参构造函数的调用

我们先说最后一行代码instantiateBean(beanName, mbd);这个方法比较简单,就是在第一次推断出来可用的构造函数为null,且自动注入模型不是3的时候,上面注释中我有对后面两个判断条件说明,所以就不再解释

org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory)

@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    
    
	// Don't override the class with CGLIB if no overrides.
	//检测bean中是否配置了lookup-method或replace-method 如果配置了,就需要用CGLIB来构建对象
	if (!bd.hasMethodOverrides()) {
    
    
		Constructor<?> constructorToUse;
		synchronized (bd.constructorArgumentLock) {
    
    
			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.resolvedConstructorOrFactoryMethod = constructorToUse;
				}
				catch (Throwable ex) {
    
    
					throw new BeanInstantiationException(clazz, "No default constructor found", ex);
				}
			}
		}
		/**
		 * 在下面这个方法中,就会调用ctor.newInstance(args)完成初始化
		 */
		return BeanUtils.instantiateClass(constructorToUse);
	}
	else {
    
    
		// Must generate CGLIB subclass.
		return instantiateWithMethodInjection(bd, beanName, owner);
	}
}

第一次推断构造函数

这里所说的第一次推断构造函数,就是第二个后置处理器所做的事情,我自己的理解,第二个后置处理器,就是对手动注入,且@Autowired注解是加载构造函数上的,对这些构造函数进行一个推断

这是我们所要关心的后置处理器对应的代码

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..
	/**
	 * 这里是检查lookUp注解,或者说是对@LookUp注解的一个解析判断
	 */
	if (!this.lookupMethodsChecked.contains(beanName)) {
    
    
		try {
    
    
			ReflectionUtils.doWithMethods(beanClass, method -> {
    
    
				Lookup lookup = method.getAnnotation(Lookup.class);
				if (lookup != null) {
    
    
					Assert.state(this.beanFactory != null, "No BeanFactory available");
					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");
					}
				}
			});
		}
		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.
	/**
	 * 每个类的构造函数,spring都会解析一遍,解析之后,保存下来,下次再使用时,无需重复解析;定义一个存放构造函数的数组
	 * candidateConstructorsCache 解析完之后,就把推断出来的构造函数存到了这个map中
	 * key是对应的class,value是一个数组,数组中存放的是该bean推断出来的所有合适的构造函数
	 *
	 * 这里的map缓存,其实有一个点一直没有搞明白:
	 * 	这里在什么场景下,会进行第二次解析?
	 * 	因为在前面调用构造函数的时候,已经进行了一次缓存的判断:在第一次执行完这些逻辑之后,会把最终推断出来的构造函数存入到一个map集合中,原型的bean
	 * 	再次初始化的时候,直接在前面就拦截了,不会进入到这里,
	 *
	 * 	所以这里这个map的使用场景没有想到:获取是在bean中通过@Bean注解注入的时候会用到?
	 *
	 * 这里用的是DCL模式
	 */
	Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
	if (candidateConstructors == null) {
    
    
		// Fully synchronized resolution now...
		synchronized (this.candidateConstructorsCache) {
    
    
			//synchronized锁住之后,再获取一遍
			candidateConstructors = this.candidateConstructorsCache.get(beanClass);
			/**
			 * 这里可以看到,如果candidateConstructorsCache中根据beanName获取到了对应的构造函数,就不会进入到推断的方法里面
			 */
			if (candidateConstructors == null) {
    
    
				Constructor<?>[] rawCandidates;
				try {
    
    
					//拿到beanClass 所有的构造函数
					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);
				}
				/**
				 * 这里代码写的挺巧妙的,rawCandidates是当前bean所有的构造函数;candidates是推断出来可用的构造函数,
				 * candidates的长度最大不会超过当前bean所有构造函数的和
				 *
				 * candidates是当前可用的构造函数(推断出来的合格的构造函数),也就是最终要返回的
				 */
				List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
				/**
				 * requiredConstructor: 必要的,存放的就是加了@Autowired注解,且require是false的构造函数
				 * defaultConstructor: 默认的无参构造函数(如果没有推断出来,就用空参构造函数),如果程序员没有声明无参构造函数,该变量就为null
				 * primaryConstructor: 这个和kotlin有点关系,这里的primary我们姑且认为是null,因为这里没看懂
				 */
				Constructor<?> requiredConstructor = null;
				Constructor<?> defaultConstructor = null;
				//这里应该和kotlin有关系
				Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
				/**
				 * nonSyntheticConstructors:这个变量是用来表示有多少个非合成构造函数
				 */
				int nonSyntheticConstructors = 0;
				/**
				 * 遍历当前bean的所有构造函数
				 * 这个for循环干的事情:
				 * 	如果在构造方法上,没有加@Autowired或者@Value注解,那这个遍历循环,就是找到了默认的构造函数(也即:参数为空的构造函数)
				 * 	如果有@Autowired注解且require是true,就解析出来加了该注解的constructor,并赋值给requiredConstructor
				 * 	但是在解析的时候,有多种常见
				 */
				for (Constructor<?> candidate : rawCandidates) {
    
    
					/**
					 * 判断当前构造方法是否是合成构造方法
					 */
					if (!candidate.isSynthetic()) {
    
    
						nonSyntheticConstructors++;
					}
					else if (primaryConstructor != null) {
    
    
						continue;
					}
					/**
					 * 判断当前构造方法是否有@Autowired注解或者@Value注解;
					 * AnnotationAttributes表示注解属性信息
					 */
					AnnotationAttributes ann = findAutowiredAnnotation(candidate);
					if (ann == null) {
    
    
						Class<?> userClass = ClassUtils.getUserClass(beanClass);
						/**
						 * 判断构造方法对应的类和bd的类是否是同一个
						 * 	userClass:构造方法对应的类
						 * 	beanClass:是从上面方法中入参来的,网上追溯代码,可以发现,是从beanDefinition对象中获取的beanClass属性
						 * 这里应该是防止当前bean是一个被代理的对象
						 * 上面的userClass中判断beanClass是否包含 $$;如果包含,说明是代理对象
						 *
						 * 这里:beanClass和userClass不相同的场景,可能是factoryBean、或者内部类吧
						 */
						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) {
    
    
						/**
						 * 如果ann不为null(表示当前bean中的构造函数中有加@Autowired),如果有多个构造函数都加了@Autowired,
						 * 只要多个@Autowired注解中,有一个required是true,就会抛出异常
						 * 从下面的判断中可以看出来,只要有一个@Autowired注解的required是true,即使后面的@Autowired注解的required是false,
						 * 也会抛出下面这个异常
						 * 这里其实也好解释,既然加了@Autowired注解,就表示是要使用这个构造函数完成注入的,但是指定了多个,那spring也不知道要用哪个构造函数去初始化
						 */
						if (requiredConstructor != null) {
    
    
							throw new BeanCreationException(beanName,
									"Invalid autowire-marked constructor: " + candidate +
									". Found constructor with 'required' Autowired annotation already: " +
									requiredConstructor);
						}
						//应该是获取到@Autowired注解中required属性的值
						boolean required = determineRequiredStatus(ann);
						/**
						 * 如果required为true,表示当前构造方法上加了@Autowired注解或者是@Value注解,且required属性是true;说明当前的构造
						 * 函数是必要的
						 *
						 * 如果推断出来的可用的构造函数已经不是null了,就会抛出异常,因为只有@Autowired注解对应的构造函数会被添加到candidates中
						 * 无论required是false,还是true,都会添加到candidates中
						 *
						 * 这里这个判断,和上面 requiredConstructor != null的判断我觉得是相同的作用,只是一个判断@Autowired中required为true,
						 * 一个判断的是@Autowired中required是false的
						 */
						if (required) {
    
    
							//candidates 存储的是当前推断出来的可用的构造函数
							if (!candidates.isEmpty()) {
    
    
								throw new BeanCreationException(beanName,
										"Invalid autowire-marked constructors: " + candidates +
										". Found constructor with 'required' Autowired annotation: " +
										candidate);
							}
							requiredConstructor = candidate;
						}
						candidates.add(candidate);
					}
					/**
					 * 如果构造函数的参数为空,表示空参构造函数,将空参构造函数设置为defaultConstructor
					 * 如果当前bean所提供的构造函数中没有空参构造函数,那defaultConstructor就是null
					 */
					else if (candidate.getParameterCount() == 0) {
    
    
						defaultConstructor = candidate;
					}
				}
				/**
				 * 结合代码可以发现,如果执行到这里,candidates不为null,
				 * 只有两种情况
				 * 	1、当前bean中的构造函数有且只有一个构造函数,且加了@Autowired注解,且required是true,这种情况只有一个构造函数
				 * 	2、当前bean的构造函数中有多个或者一个,且required都是false,这种情况可以有一个,也可以有多个
				 * 	但是:一个@Autowired是true,一个@Autowired对应的require是false的场景不可能出现,因为会在上面报错,不会执行到这里
				 *
				 * 这里的判断是:
				 * 	如果bean中存在添加了@Autowired注解的构造函数
				 * 		但是required是false,那就返回默认的空参构造函数,如果没有空参构造函数,打印一行日志,返回多个@Autowired注解中require是false的构造函数
				 * 		如果required是true,就返回对应的添加了@Autowired注解的构造函数
				 */
				if (!candidates.isEmpty()) {
    
    
					// Add default constructor to list of optional constructors, as fallback.
					if (requiredConstructor == null) {
    
    
						if (defaultConstructor != null) {
    
    
							candidates.add(defaultConstructor);
						}
						else if (candidates.size() == 1 && logger.isWarnEnabled()) {
    
    
							logger.warn("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));
						}
					}
					candidateConstructors = candidates.toArray(new Constructor<?>[0]);
				}
				/**
				 * 如果只有一个构造函数,并且构造函数的入参大于0,就用这一个
				 */
				else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
    
    
					candidateConstructors = new Constructor<?>[] {
    
    rawCandidates[0]};
				}
				/**
				 * 下面这几个判断都和primaryConstructor有关系
				 * primaryConstructor和kotlin好像有点关系,所以就没有深入研究
				 */
				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];
				}
				this.candidateConstructorsCache.put(beanClass, candidateConstructors);
			}
		}
	}
	return (candidateConstructors.length > 0 ? candidateConstructors : null);
}

这个方法中的注释我觉得写得还算比较详细,可以仔细看下,再坐下总结
1.首先处理LoopUp注解相关信息
2.接着推到构造函数,会遍历所有的构造函数
该方法的返回场景有以下几个(不考虑kotlin的场景)
1.只有一个构造函数加了@Autowired注解,且require是true,就会返回这个
2.有多个构造函数加了@Autowired注解,但是require全部是false,
2.1 如果默认的无参构造函数存在,就会返回这些所有是false的构造函数 + 无参构造函数
2.2 如果无参构造函数不存在,就会返回这些所有是false的构造函数
3.如果所有的构造函数都没有添加@Autowired注解
3.1 如果只有一个构造函数,且入参大于0,就会返回这个
3.2 反之,返回null
其他场景不存在,比如:一个@Autowired注解的require是true,一个@Autowired注解的require是false;或者说多个@Autowired注解的require是true这些场景都不存在,因为spring会抛异常,在下面可以看下

这个方法,只是简单的推断一下,并没有去推断构造函数的入参是否满足要求等,只是评估下,哪些构造函数可以使用
我们也可以简单认为,这里的构造函数推断,只是对@Autowired手动注入方式进行简单的推断

再次推断构造函数

Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
		mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
    
    
	return autowireConstructor(beanName, mbd, ctors, args);
}

根据这段代码,我们可以知道,如果当前后置处理器,返回的ctors不为null,或者是自动注入模型是3,那就会执行autowireConstructor()方法,这个方法可以认为是二次推断
ctors返回不为null,就表示当前有@Autowired注解,指定了要使用的构造函数
如果我既加了@Autowired注解,自动注入模型又设置为3,那会优先使用ctors中的构造函数
也就是说,在进行二次构造函数推断的时候:
1.如果指定了@Autowired注解在构造函数上,就会优先从这些构造函数中进行二次推断,决定要使用哪个构造函数进行初始化
2.如果没有指定@Autowired注解,那就会在二次推断构造函数的方法中,获取到所有的构造函数,再次推断
3.二次推断的逻辑中,远比第一次推断逻辑要复杂,会根据参数和要注入属性的匹配度来决定要使用哪个构造函数注入


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

	//这就是要返回的对象  因为这里的bw是BeanWrapper的实现类
	BeanWrapperImpl bw = new BeanWrapperImpl();
	this.beanFactory.initBeanWrapper(bw);

	/**
	 * constructorToUse 这个变量保存的就是 后置处理器推断出来的要用哪个构造函数进行初始化
	 *
	 * argsHolderToUse是保存构造方法的值,注意不是参数
	 * 在调用反射的时候,需要把具体的值传过去,这里的变量就是来记录这些值的
	 *
	 * argsToUse 是最终确定的参数值列表,应该就是constructorToUse构造函数对应的参数值列表
	 */
	Constructor<?> constructorToUse = null;
	ArgumentsHolder argsHolderToUse = null;
	Object[] argsToUse = null;

	/**
	 * explicitArgs:在创建bean的时候,这个参数基本上100%为null
	 */
	if (explicitArgs != null) {
    
    
		argsToUse = explicitArgs;
	}
	else {
    
    
		Object[] argsToResolve = null;
		synchronized (mbd.constructorArgumentLock) {
    
    
			/**
			 * resolvedConstructorOrFactoryMethod:
			 * 如果bean是原型的,那么会用到这个参数;如果bean是单实例的,永远不会进入到下面的if判断里面
			 * 因为单实例的会在初始化之后,放入到单实例池中
			 */
			constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
			if (constructorToUse != null && mbd.constructorArgumentsResolved) {
    
    
				// Found a cached constructor...
				argsToUse = mbd.resolvedConstructorArguments;
				if (argsToUse == null) {
    
    
					argsToResolve = mbd.preparedConstructorArguments;
				}
			}
		}
		/**
		 * 下面的这个方法,待学习,应该是推断当前bean完成初始化,需要用到的参数列表
		 */
		if (argsToResolve != null) {
    
    
			argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
		}
	}

	/**
	 * 这里的判断也一样,如果已经解析过一次,原型的bean在第二次尝试初始化一个bean的时候,就不会进入if的判断逻辑了,而是直接从缓存中获取到对应的可用的构造函数,然后
	 * 完成初始化,不需要再次推断
	 * 所以:if里面的逻辑,就是去推断构造函数的详细逻辑
	 */
	if (constructorToUse == null) {
    
    
		/**
		 * chosenCtors 参数就是前面后置处理器推断出来要使用的构造函数 为null表示后置处理器没有推断出 需要自己去解析判断
		 *
		 * 	这里:如果推断出来可用的构造函数不为null;或者自动注入模型是AUTOWIRE_CONSTRUCTOR,就将autowiring设置为true
		 */
		// Need to resolve the constructor.
		boolean autowiring = (chosenCtors != null ||
				mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
		ConstructorArgumentValues resolvedValues = null;

		/**
		 * minNrOfArgs 是表示构建bean最少需要的参数个数,默认是0,如果说指定了需要使用两个参数,那explicitArgs就等于2
		 * 这里获取到的是 给构造函数添加的参数  mbd.getConstructorArgumentValues().addGenericArgumentValue("com.test.UserDao");
		 * 一般的业务场景下,我们不会指定
		 */
		int minNrOfArgs;
		if (explicitArgs != null) {
    
    
			minNrOfArgs = explicitArgs.length;
		}
		else {
    
    
			ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
			resolvedValues = new ConstructorArgumentValues();
			minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
		}

		// Take specified constructors, if any.
		/**
		 * 如果上一个后置处理器推断出来的构造函数是null,这里会获取到所有的构造方法
		 */
		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);
			}
		}
		/**
		 *
		 * 截止到这里,上面的逻辑我们就认为:是为了给candidates这个变量赋值
		 * 有可能是后置处理器推断出来的可以使用的构造函数,也有可能是在这里重新获取的所有构造函数
		 *
		 *
		 * 这里会根据构造方法的访问权限级别和参数数量进行排序
		 *  先根据构造方法权限排序,权限相同的 根据参数数量来排序
		 *  public XXXX(a,b,c)
		 *  public XXXX(A,B)
		 *  protected xxxx(a,b,c)
		 *  protected xxxx(a,b)
		 *
		 *  这里的排序是非常重要的
		 */
		AutowireUtils.sortConstructors(candidates);
		/**
		 * 定义了一个变异差量,默认为int的最大值,这个值应该是相似度的一个概念
		 * ambiguousConstructors:spring推断出来两个bean的权重都一样之后,会模糊不清,就会存入到这个集合中
		 * causes:异常的集合类
		 */
		int minTypeDiffWeight = Integer.MAX_VALUE;
		Set<Constructor<?>> ambiguousConstructors = null;
		LinkedList<UnsatisfiedDependencyException> causes = null;

		/**
		 * 循环所有的构造方法
		 */
		for (Constructor<?> candidate : candidates) {
    
    
			//获取当前构造函数的参数列表
			Class<?>[] paramTypes = candidate.getParameterTypes();

			/**
			 * 这一行判断 是spring推断使用哪个构造函数的核心方法
			 * constructorToUse的意思是:
			 *   这个变量是用来保存已经解析过了并且在使用的构造方法,只有在这个参数为空的情况下才有推算的必要性
			 *   如果已经解析到一个符合的构造方法,就会赋值给这个变量;所以,如果这个变量不为null,就直接返回,说明spring已经找到了一个合适的构造方法,可以直接使用
			 *
			 * argsToUse:我觉得可以理解为最为相似的构造函数对应的参数个数
			 *  argsToUse.length > paramTypes.length  paramTypes在第一次取出来的时候,是public参数最多的构造函数(因为前面有排序),
			 *  所以,如果排序后的第一个构造函数都比argsToUse小 ,那么后面就没有判断的必要了
			 *
			 *  可以理解为 argsToUse是满足构造条件需要的参数,那下一个构造函数的参数列表比我需要的参数数量少  就不需要在判断了  肯定不满足
			 *
			 */
			if (constructorToUse != null && argsToUse.length > paramTypes.length) {
    
    
				// Already found greedy constructor that can be satisfied ->
				// do not look any further, there are only less greedy constructors left.
				break;
			}
			/**
			 * 如果当前构造函数的参数列表长度小于最小需要的参数长度,那么就跳过本次循坏;
			 * 举例:假如说最少需要2个参数,但是当前构造函数有一个参数,那就没必要进行本次判断了,肯定不满足,直接遍历下一个
			 * 当然,这个判断的前提是上面一个if条件不满足,如果上面一个if条件满足,就直接终止了,就无需再进行下面的判断
			 */
			if (paramTypes.length < minNrOfArgs) {
    
    
				continue;
			}

			/**
			 * argsHolder:最终调用构造函数,需要用的参数
			 * 下面的if判断,就是根据当前正在被处理的构造函数,解析出当前构造函数需要用到的参数值列表
			 */
			ArgumentsHolder argsHolder;
			if (resolvedValues != null) {
    
    
				try {
    
    
					String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
					if (paramNames == null) {
    
    
						ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
						if (pnd != null) {
    
    
							/**
							 * 获取构造函数参数名称列表
							 * 如果说有个构造函数(String luban,Object zilu)
							 * 那么 paramNames就是 [luban,zilu]
							 */
							paramNames = pnd.getParameterNames(candidate);
						}
					}
					/**
					 * 对paramNames进行转换, 因为spring只能提供字符串的参数值  所以需要进行转换
					 */
					argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
							getUserDeclaredConstructor(candidate), autowiring);
				}
				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 LinkedList<>();
					}
					causes.add(ex);
					continue;
				}
			}
			else {
    
    
				// Explicit arguments given -> arguments length must match exactly.
				if (paramTypes.length != explicitArgs.length) {
    
    
					continue;
				}
				argsHolder = new ArgumentsHolder(explicitArgs);
			}

			/**
			 * typeDiffWeight 表示差异量
			 * 每个参数值的类型和构造函数参数列表类型的差异
			 * 第一次进来的时候,肯定是小于minTypeDiffWeight的(因为默认的minTypeDiffWeight为Integer,MAX_VALUE)
			 *
			 * 如果进入到了else if 分支,代表什么意思呢?
			 * 意思是两个构造函数都符合我们的要求(两个构造函数的差异量一样),那spring如何来判断使用哪个?
			 *  首先把这个构造函数添加到 存放有异议的set中
			 *   在下面进行了判断,如果这个set不等于空,会抛出异常
			 *   也即:如果差异量最小的构造函数有多个,就抛出异常,因为不知道要用哪个
			 * 如果在遍历了下一个构造函数,发现下一个构造函数的差异量比前面有差异的两个构造函数的差异量小,就会使用最新的这个构造函数,也即:上面推断出来的两个差异量相同的构造函数就没有用了
			 * 因为我已经找到了一个更优的构造函数
			 *
			 * 这里在获取差异量的时候 ,有两种取值方法,后面在判断多个构造函数权重一致抛出异常的时候,也和其中某一种方式有关系
			 * 从注释看是从严还是从宽模式
			 */
			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);
			}
		}

		/**
		 * 对解析之后的结果进行判断,constructorToUse是最终找到的最符合、相似度最高的一个构造函数
		 * 如果为null,就抛出异常
		 * 如果不为null,但是推断出来了两个差异量一样的构造函数,且当前bean指定的是从严模式,那就抛出异常
		 */
		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 " +
					"(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 in bean '" + beanName + "' " +
					"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
					ambiguousConstructors);
		}

		/**
		 * 这里是缓存相关信息:
		 *  1.已解析出的构造方法对象,  resolvedConstructorOrFactoryMethod
		 *  2.构造方法参数列表是否已经解析标志  constructorArgumentsResolved
		 *  3.参数值列表  preparedConstructorArguments 或者 resolvedConstructorArguments
		 *
		 *  等相同的bean在第二次进来的时候,就不需要再次解析了,直接从缓存中获取即可
		 *  explicitArgs是入参中指定的参数值,我们前面说了,大部分场景下都是null
		 */
		if (explicitArgs == null) {
    
    
			argsHolderToUse.storeCache(mbd, constructorToUse);
		}
	}

	try {
    
    
		/**
		 * 通过反射创建实例,然后将生成的实例放到包装类中
		 */
		final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy();
		Object beanInstance;

		if (System.getSecurityManager() != null) {
    
    
			final Constructor<?> ctorToUse = constructorToUse;
			final Object[] argumentsToUse = argsToUse;
			beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
					strategy.instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse),
					beanFactory.getAccessControlContext());
		}
		else {
    
    
			beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
		}

		bw.setBeanInstance(beanInstance);
		return bw;
	}
	catch (Throwable ex) {
    
    
		throw new BeanCreationException(mbd.getResourceDescription(), beanName,
				"Bean instantiation via constructor failed", ex);
	}
}

对实例化要使用的构造方法进行二次推断
1.判断上一个后置处理器推断出来可用的构造函数是否为null;如果为null,就重新获取所有的构造函数
2.对构造函数进行排序:这里的排序,很重要,会根据public的构造函数参数从多到少排序,然后private的构造函数进行排序,根据参数从多到少依次排序
3.循环遍历排序后的构造函数集合,进行判断
3.1 如果当前推断出来的构造函数不为null,且推断出来的这个构造函数的入参长度大于当前遍历的构造函数的入参长度,就无需进行循环遍历,因为构造函数的数组是排序之后的
3.2 如果3.1不满足,就判断当前构造函数的参数个数,是否小于最小入参长度
3.3 根据算法,推断当前构造函数需要的最少参数集合
3.4 根据当前构造函数和最少参数集合,获取到当前构造函数的差异量
3.5 如果差异量小于上一个值,就将当前构造函数设置为最优的构造函数,如果差异值相同,就放入到专门的集合中
4.如果遍历完成之后,最优的构造函数有两个,且当前bean是从严模式,就报错;如果没有推断出来合适的构造函数也报错
如果正常推断出来了,就通过constructor.newInstance()完成初始化

总结

所以:在spring通过构造函数完成bean的初始化的时候,要么是通过空参构造函数去初始化,要么是通过两次推断,推断出一个最优的构造函数完成初始化
我这里所说的初始化就是指:只是把class对象先创建出来,还没有完成class对象的实例化方法回调、代理对象生成、属性注入等操作,这里只是初始化了一个class对象

猜你喜欢

转载自blog.csdn.net/CPLASF_/article/details/111312746