IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 每天一点点,Spring选择合适的构造方法执行创建 -> 正文阅读

[Java知识库]每天一点点,Spring选择合适的构造方法执行创建

Spring 得到候选构造方法之后,如何选择使用哪个构造方法?

ConstructorResolver#autowireConstructor

1、定义三个变量

// 最后决定使用的构造方法
Constructor<?> constructorToUse = null;
// 构造函数需要使用的参数持有器
ArgumentsHolder argsHolderToUse = null;
// 最后需要使用的构造方法的参数
Object[] argsToUse = null;

2、explicitArgs 参数代表的是外面getBean传入的参数

AnnotationConfigApplicationContext
				context = new AnnotationConfigApplicationContext(MyTest.class);
// 此处getBean后面的参数就是 explicitArgs 参数值
TestService testService = context.getBean("testService", new UserService(), new UserService());

3、如果传入了 explicitArgs值则构造函数的参数就是用传入的参数

if (explicitArgs != null) {
	argsToUse = explicitArgs;
}

4、看一下缓存中是否存在
4.1、如果缓存中存在需要使用的构造方法,且当前构造方法需要的参数已经解析过,取出缓存的构造方法需要的参数列表,如果当前参数列表为null,再判断一下是否有需要解析的参数,如果存在需要解析的参数,需要去解析。

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

4.2、 解析参数
4.2.1、ConstructorResolver#resolvePreparedArguments
得到解析器,如果存在则使用指定的解析器,如果不存在则使用默认的解析器

TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
TypeConverter converter = (customConverter != null ? customConverter : bw);

4.2.2、遍历所有需要解析的参数数组,判断每个需要解析的参数的类型,执行不同的代码逻辑

// 
if (argValue == autowiredArgumentMarker) {
	argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
}
// 针对特殊类型需要特殊处理
else if (argValue instanceof BeanMetadataElement) {
	argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
}
else if (argValue instanceof String) {
//是否需要执行El表达式
	argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
}

4.2.3、得到对应的参数的参数类型

Class<?> paramType = paramTypes[argIndex];

4.2.4、使用解析器执行解析

resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);

5、如果没有确定的构造参数或者没有可使用的构造方法参数
5.1、判断是否传入了候选的构造函数数组,如果没有的话,根据类的设置信息来判断获取全部包含私有的构造函数还是只获取公共的构造函数。

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

6、如果只有一个候选的构造函数,且没有传入构造参数值,且没有构造参数的时候,(空参构造),直接使用此无参构造

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

7、判断当前类需要不需要自动注入
7.1、如果存在候选的构造函数或者BeanDefinition.autowireMode= AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT则为true
8、定义一个参数记录需要使用的构造函数的最小值
8.1、 如果传入的有参数,则设置最小的构造参数个数为 传入的参数个数
8.2、处理BeanDefinition指定参数得到最小的参数个数

int minNrOfArgs;
if (explicitArgs != null) {
	minNrOfArgs = explicitArgs.length;
}else {
// 处理其他BeanDefinition指定了指的构造函数参数
	ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
	resolvedValues = new ConstructorArgumentValues();
	minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}

9、对得到的构造函数执行排序

// 排序规则是先按照是否Public和参数个数排序
AutowireUtils.sortConstructors(candidates);

10、定义三个变量

// 最小的类型差别
int minTypeDiffWeight = Integer.MAX_VALUE;
// 如果两个构造函数的上面值一样,记录下来
Set<Constructor<?>> ambiguousConstructors = null;
//无法找到当前构造函数的参数的异常构造函数集合
Deque<UnsatisfiedDependencyException> causes = null;

11、循环当前的所有候选构造函数集合
11.1、 得到当前构造函数的参数个数

int parameterCount = candidate.getParameterCount();

11.2、如果开始循环之前已经得到了可使用的构造函数,且需要使用的参数个数不是null,并且当前构造函数的参数小于 已找到的参数个数,直接结束。

// 因为在执行循环的时候已经排过序了,如果当前需要的构造函数参数个数已经小于了当前需要的参数个数,后面的构造函数就没没有必要再执行了
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;
}

11.3、如果当前构造参数个数小于最小的构造函数参数就继续

if (parameterCount < minNrOfArgs) {
// 因为排序的时候是按照 先public 再参数个数,所以此处不是结束而是继续
	continue;
}

例如 下面代码

@Component
public class MyService {

	@Autowired(required = false)
	public MyService (UserBService userService) {

	}
	@Autowired(required = false)
	public MyService (UserService userService, OrderService orderService) {

	}
	@Autowired(required = false)
	private MyService (OrderService orderService, UserService userService) {

	}
}

排序之后的构造函数为

public com.test.MyService (UserService ,OrderService )
public com.test.MyService (OrderService )
private com.test.MyService (OrderService ,UserService )

11.4、得到当前构造参数的类型集合

Class<?>[] paramTypes = candidate.getParameterTypes();

11.5、如果getBean的时候没有传入参数(explicitArgs== null),则 resolvedValues != null 为真
11.5.1、判断当前构造方法上面是否加了@ConstructorProperties注解,获取参数名称

String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);

11.5.2、如果paramNames == null 获取默认的构造方法的参数名

if (paramNames == null) {
	ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
	if (pnd != null) {
		paramNames = pnd.getParameterNames(candidate);
	}
}

11.5.3、找到这个构造方法需要的参数ConstructorResolver#createArgumentArray

// 如果出现异常比如找不到对应的参数,则添加此构造方法到causes中 
ArgumentsHolder argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);

12、如果判断当前类设置的宽松模式,得到一个值,此值越低代表当前构造函数越符合

int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));

13、如果已经存在了可使用的构造方法,且本次处理的构造方法得到的 typeDiffWeight == 已存在的构造方法计算出来的值,则添加到 ambiguousConstructors 中
14、如果循环完所有的构造方法,如果没有得到可使用的构造方法,且异常的构造集合不为null,则跑出异常

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

15、如果已找到了一个可用的构造方法,且存在其他可用的构造方法,且类设置的是严格模式解析,则抛出异常

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

16、如果getBean没有传入参数,且找到了可用的构造方法参数,则进行缓存

//ConstructorResolver.ArgumentsHolder#storeCache
if (explicitArgs == null && argsHolderToUse != null) {
	argsHolderToUse.storeCache(mbd, constructorToUse);
}

17、执行创建

//ConstructorResolver#instantiate(String, RootBeanDefinition, Constructor<?>, Object[])
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:16:56  更:2022-02-26 11:18:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:07:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码