@Service
public class HelloService {
@Autowired(required = false)
public HelloService(ApplicationContext applicationContext) { // 1⃣️
}
@Autowired(required = false)
public HelloService(Environment environment) { // 2⃣️
}
}
快告诉我、Spring 将选择哪个构造函数用来实例化 HelloService ? 1⃣️ 还是 2⃣️ ?文末给出答案
Spring 海选一
第一场海选的场地为 : AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors
Constructor<?>[] rawCandidates;
try {
// 反射获取所有构造函数
rawCandidates = beanClass.getDeclaredConstructors();
}
catch (Throwable ex) {
throw new BeanCreationException(xxxxxxxxx);
}
// 构造函数候选列表
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
// 构造函数种子候选
Constructor<?> requiredConstructor = null;
// 默认构造函数
Constructor<?> defaultConstructor = null;
// 这里非 kotlin 语言、返回 null
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
// 非合成构造函数
int nonSyntheticConstructors = 0;
for (Constructor<?> candidate : rawCandidates) {
if (!candidate.isSynthetic()) // 可以忽略
nonSyntheticConstructors++;
}
else if (primaryConstructor != null) {
continue;// 可以忽略
}
// 判断该构造函数是否被 @Autowire 注解修饰
MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
if (ann == null) {
Class<?> userClass = ClassUtils.getUserClass(beanClass);
if (userClass != beanClass) {
try {
Constructor<?> superCtor =
userClass.getDeclaredConstructor(candidate.getParameterTypes());
ann = findAutowiredAnnotation(superCtor);
}
catch (NoSuchMethodException ex) {
// Simply proceed, no equivalent superclass constructor found...
}
}
}
// 被注解修饰
if (ann != null) {
if (requiredConstructor != null) { // 已经有种子候选了、不允许再有注解修饰的构造函数
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructor: " + candidate +
". Found constructor with 'required' Autowired annotation already: " +
requiredConstructor);
}
boolean required = determineRequiredStatus(ann);
if (required) { // 当前构造函数为种子候选、同理不允许再有注解修饰的构造函数
if (!candidates.isEmpty()) {
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructors: " + candidates +
". Found constructor with 'required' Autowired annotation: " +
candidate);
}
// 成为种子候选
requiredConstructor = candidate;
}
// 加入到候选列表中
candidates.add(candidate);
}
else if (candidate.getParameterCount() == 0) {
// 成为默认构造函数
defaultConstructor = candidate;
}
}
if (!candidates.isEmpty()) {
// 候选列表不为空
if (requiredConstructor == null) {
if (defaultConstructor != null) {
// 如果种子候选为 null 、并且默认构造函数不为 null、将默认构造函数加入到候选列表中作为后备方案
candidates.add(defaultConstructor);
}
}
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
// 如果候选列表为 null 并且该类只申明了一个构造函数且它存在入参、那么将它加入到候选列表中
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
.....省略掉 kotlin 相关的
else {
candidateConstructors = new Constructor<?>[0];
}
// 加入缓存中
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
........
// 选妃结束
return (candidateConstructors.length > 0 ? candidateConstructors : null);
代码不方便看的话、这里列一下几个重要的变量
rawCandidates // 该类声明的所有构造函数
// 候选列表(构造函数)
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
// 种子候选(构造函数)
Constructor<?> requiredConstructor = null;
// 默认构造函数
Constructor<?> defaultConstructor = null;
得出的结论
- 如果存在 Autowired 修饰的构造函数、且 required 为 true 、那么有且仅有一个被 Autowired 修饰的构造函数。并且只返回其作为候选结果
- 如果存在一个或多个被 Autowired 修饰的构造函数、required 肯定都是 false的、这个时候如果声明了默认构造函数、则默认构造函数会加入到候选列表中、整个候选列表作为候选结果。
- 如果该类的构造函数都没有使用 Autowired 修饰、并且该类只存在一个构造函数、并且不是无参构造函数、那么则将其作为候选结果返回。
所以开篇中的 HelloService 中将会返回两个构造函数作为候选结果
Spring 海选二
第一场海选的场地为 : ConstructorResolver#autowireConstructor
// 冠军构造函数
Constructor<?> constructorToUse = null;
// 冠军构造函数的参数
ArgumentsHolder argsHolderToUse = null;
// 先按方法修饰符进行排序、public 优先级最高。然后按照参数个数排序、入参个数越多、优先级越高
AutowireUtils.sortConstructors(candidates);
// 最小的类型差异权重
int minTypeDiffWeight = Integer.MAX_VALUE;
// 模凌两可的构造函数列表
Set<Constructor<?>> ambiguousConstructors = null;
// 筛选过程中出现的异常
LinkedList<UnsatisfiedDependencyException> causes = null;
// candidates 海选一选出的构造函数
for (Constructor<?> candidate : candidates) {
// 入参个数
int parameterCount = candidate.getParameterCount();
// 冠军构造函数已经产生、并且冠军构造函数入参的个数大于当前构造函数的入参个数了、那么就没必要继续往下了
// 因为 Spring 总想给你它能给的最多的爱
if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
break;
}
// minNrOfArgs 这里正常都是 0 、可以忽略
if (parameterCount < minNrOfArgs) {
continue;
}
// 尝试从 Spring 中找出构造函数需要的参数、如果找不出、不中断、而是继续下一个构造函数
ArgumentsHolder argsHolder;
Class<?>[] paramTypes = candidate.getParameterTypes();
if (resolvedValues != null) {
try {
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(candidate);
}
}
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
}
catch (UnsatisfiedDependencyException ex) {
// Swallow and try next constructor.
if (causes == null) {
causes = new LinkedList<>();
}
// 保存异常、在 Spring 中找不到依赖的构造函数参数
causes.add(ex);
continue;
}
}
else {
、 if (parameterCount != explicitArgs.length) {
continue;
}
argsHolder = new ArgumentsHolder(explicitArgs);
}
// 成功从 Spring 中找出构造函数参数、这里去进行比较找出的对象和参数的类型 (类型差异权重)
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// 哪个构造函数的类型差异更小、哪个就能成为冠军构造函数
if (typeDiffWeight < minTypeDiffWeight) {
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;
}
else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
.... 加入到模凌两可的构造函数列表中
ambiguousConstructors.add(candidate);
}
}
if (constructorToUse == null) {
// 流选了、冠军构造函数一个都没有
}
else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
// 因为构造函数创建 bean 使用的宽松模式即 mbd.isLenientConstructorResolution() 一直为true
// 所以即使中途出现模凌两可的构造函数列表、也不会抛出异常
throw new BeanCreationException(xxx);
}
// 使用 constructorToUse, argsToUse 创建 bean
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
代码不方便看的话、这里列一下几个重要的变量
// 冠军构造函数
Constructor<?> constructorToUse = null;
// 冠军构造函数的参数
ArgumentsHolder argsHolderToUse = null;
// 先按方法修饰符进行排序、public 优先级最高。然后按照参数个数排序、入参个数越多、优先级越高
AutowireUtils.sortConstructors(candidates);
// 最小的类型差异权重
int minTypeDiffWeight = Integer.MAX_VALUE;
// 模凌两可的构造函数列表
Set<Constructor<?>> ambiguousConstructors = null;
回到我们文章开头的问题、ApplicationContext 和 Environment 毫无疑问都能从 BeanFactory 中找出来、那么它的差异权重就成为选择哪个构造函数的关键了。
因为是构造函数实例化 bean、所以采用的是宽松模式、也就是 mbd.isLenientConstructorResolution()
返回的是 true。如果是配置类(也就是 FactoryMethod ) 就是严格模式、也就是返回 false。
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
arguments 和 rawArguments 转换之后的参数和原始的参数、我们认为它们是一样的在大多数情况下
public int getTypeDifferenceWeight(Class<?>[] paramTypes) {
int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments);
int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024;
return Math.min(rawTypeDiffWeight, typeDiffWeight);
}
类型差异权重越小、优先级就越高、即能成为冠军构造函数。
public static int getTypeDifferenceWeight(Class<?>[] paramTypes, Object[] args) {
int result = 0;
for (int i = 0; i < paramTypes.length; i++) {
if (!ClassUtils.isAssignableValue(paramTypes[i], args[i])) {
return Integer.MAX_VALUE;
}
if (args[i] != null) {
Class<?> paramType = paramTypes[i];
Class<?> superClass = args[i].getClass().getSuperclass();
while (superClass != null) {
if (paramType.equals(superClass)) {
result = result + 2;
superClass = null;
}
else if (ClassUtils.isAssignable(paramType, superClass)) {
result = result + 2;
superClass = superClass.getSuperclass();
}
else {
superClass = null;
}
}
if (paramType.isInterface()) {
result = result + 1;
}
}
}
return result;
}
我们回到文章的问题、
public int getTypeDifferenceWeight(Class<?>[] paramTypes) {
int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments);
int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024;
return Math.min(rawTypeDiffWeight, typeDiffWeight);
}
ApplicationContext 从 Spring 中找出来的类型为 AnnotationConfigServletWebServerApplicationContext
result = 2 + 2 + 2 + 2 + 1 = 9 、最终返回结果为 9 - 1024 = -1015
AbstractApplicationContext 的 父类 DefaultResourceLoader 不是 ApplicationContext 的子类或实现、所以终止于此
而 Environment 从 Spring 中找出来的类型为 StandardServletEnvironment
result = 2 + 2 + 1 = 5 最终返回结果为 5 - 1024 = -1019
AbstractEnvironment 的父类为 Object 、没有实现 Environment 接口、所以终止于此。
所以你知道选 1⃣️ 还是 2⃣️ 了吗?