spring中实例化对象的情况,通常分为两种,一种是通用的实例化,另一种是带有参数的实例化。带参的实例化存在着不确定性,在判断对应参数上做了大量工作,同样的实例化过程也是相当复杂。
一、autowireConstructor
autowireConstructor方法在ConstructorResolver类中,老规矩说明一下入参和返回值。
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs)
参数:
beanName:bean的名称
mbd:bean的说明类,与xml配置文件一一对应
chosenCtors:候选的构造方法
explicitArgs:用户指定的传参,如果有值,可以通过这个参数直接确定用于初始化的构造函数
返回值:
BeanWrapper:bean的包装类,包含bean的实例对象和bean的详细参数
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {//实例化一个beanWrapperImpl类对象BeanWrapperImpl bw = new BeanWrapperImpl();//初始化bw,这里的BeanFactory来自于AbstractAutowireCapableBeanFactorythis.beanFactory.initBeanWrapper(bw);Constructor<?> constructorToUse = null;ArgumentsHolder argsHolderToUse = null;Object[] argsToUse = null;//explicitArgs不为空,说明用户指定了构造方法的参数,直接拿来使用if (explicitArgs != null) {argsToUse = explicitArgs;}else {Object[] argsToResolve = null;synchronized (mbd.constructorArgumentLock) {//尝试从mbd的缓存中拿取构造方法constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;//构造方法不为空并且构造方法已经解析 if (constructorToUse != null && mbd.constructorArgumentsResolved) {// Found a cached constructor...//直接从mbd缓存中拿取构造方法的参数argsToUse = mbd.resolvedConstructorArguments;if (argsToUse == null) {//如果没有拿到构造方法的参数,就获取缓存中的配置文件的参数argsToResolve = mbd.preparedConstructorArguments;}}}if (argsToResolve != null) {//正确拿到配置文件的参数之后,对参数进行解析,最后生成构造方法的参数argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);}}//如果没有拿到构造方法,就说明没有bean进行过解析,需要去关联对应的bean的构造器if (constructorToUse == null || argsToUse == null) {// Take specified constructors, if any.//获取传入的构造器Constructor<?>[] candidates = chosenCtors;//如果构造器不为空if (candidates == null) {//获取bean的类型Class<?> beanClass = mbd.getBeanClass();try {//判断是否允许非公开访问,如果允许就获取所有的构造方法,如果不允许就获取public的构造方法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];//判断这个构造方法的参数个数为0,这个构造方式 就是默认的构造方法if (uniqueCandidate.getParameterCount() == 0) {synchronized (mbd.constructorArgumentLock) {//记录到mbd的缓存中去mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;mbd.constructorArgumentsResolved = true;mbd.resolvedConstructorArguments = EMPTY_ARGS;}//初始化bean, 并且设置到bw(bean 的包装对象)中去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;int minNrOfArgs;if (explicitArgs != null) {//获取用户传入参数的个数minNrOfArgs = explicitArgs.length;}else {//从配置文件中拿到参数的值ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();resolvedValues = new ConstructorArgumentValues();//获取到构造方法中参数的方法的参数个数minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);}//对构造方法进行排序,public的排在前面,参数多的排在前面AutowireUtils.sortConstructors(candidates);int minTypeDiffWeight = Integer.MAX_VALUE;Set<Constructor<?>> ambiguousConstructors = null;LinkedList<UnsatisfiedDependencyException> causes = null;//逐一遍历所有的构造方法for (Constructor<?> candidate : candidates) {//获取构造方法的参数类型Class<?>[] paramTypes = candidate.getParameterTypes();//这里的判断构造方法和构造方法参数 都不是空,又由于之前对构造方法做了排序。所以在使用的参数的个数已经大于当前构造方法的参数个数的时候,实际上已经取到了想要的构造方法。if (constructorToUse != null && argsToUse != 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;}//通过构造方法的参数个数 快速的做一个判断if (paramTypes.length < minNrOfArgs) {continue;}ArgumentsHolder argsHolder;if (resolvedValues != null) {try {String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);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) {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);}//isLenientConstructorResolution()判断策略是否宽松//宽松策略下,使用spring构造的参数数组的类型和获取到的构造方法的参数类型进行对比。//严格策略下,还需要检查能否将构造方法的参数复制到对应的属性中//会返回一个数值,作为构造方法和参数的差异值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 " +"(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);}//做一个缓存,方便下次的使用if (explicitArgs == null && argsHolderToUse != null) {argsHolderToUse.storeCache(mbd, constructorToUse);}}//用上面得到的构造器和参数来反射创建bean实例,并放到BeanWrapperImpl对象中然后返回Assert.state(argsToUse != null, "Unresolved constructor arguments");bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));return bw;
}
这个方法的代码量很大,逻辑也比较复杂。这个函数的写法与以往spring的风格有些不同,spring一贯的做法都是讲复杂的逻辑分解,成为多个小函数嵌套在其中,使得每一个方法的逻辑都简单易懂。我们将逻辑简单拆分,整理成以下几个方面。
1.构造方法参数的确定
1)根据explicitArgs参数判断。
如果参入的参数explicitArgs不为空,就可以直接确定下参数,因为explicitArgs参数是在调用Bean的时候用户指定的,在BeanFactory类中有这样一个方法:
Object getBean(String name, Object... args) throws BeansException;
在获取bean的时候,用户不但可以指定bean的名称还可以指定bean所对应类的构造方法或者工厂方法的参数,主要用于静态工厂方法的调用,这里则是需要给定完全匹配的参数,所以,可以判定explicitArgs不为空,就是构造方法的参数就是它。
2)缓存中获取
除此之外,确定参数的办法如果之前已经分析过,构造方法参数已经有记录在缓存中,那么便可以直接拿来使用。但是这里缓存的参数可以是最终的类型也可能是初始的类型。例如:构造方法需要的参数是int类型的1,但是原始参数可能是String类型的“1”,那么即使从缓存中获取到了参数,也需要经过类型转换器来保证与构造方法的参数类型一致。
3)配置文件获取
在前两个方法都没法获取到参数的时候,就只能开始新一轮的分析。
从配置文件中获取配置到构造方法的信息开始,经过之前的分析,spring中的配置文件的信息会被转换成通用的BeanDefinition实例,也就是参数mbd,通过调用mbd.getConstructorArgumentValues()来获取配置的构造函数信息。拿到配置中的信息便可以获取到每个参数对应得值。
2.构造方法的确定
经过第一个步之后确定了构造方法的参数,接下来就是要根据构造方法的参数来找到对应的构造方法,匹配的方法就是根据参数的个数对比,在匹配之前需要对构造方法按照public构造方法优先、参数数量降序排列、非public构造方法参数降序排列。这样可以在遍历的情况下迅速的判断出构造方法参数个数是否符合条件。
由于在配置文件中并不是唯一限制使用参数位置索引的方式去创建,还同时支持指定参数名进行设定参数值的情况,如<constructor-arg name="aa">,这种情况下就需要先确定构造方法中的参数名。
获取参数名可以用两种方式,一种是通用注解的方式直接获取,另一种就是使用Spring中提供的工具类ParameterNameDiscoverer来获取。构造方法、参数名、参数类型、参数值确定之后却可以确定构造方法。
3.根据参数类型转换对应参数的类型。
4.构造函数不确定性验证
有的时候根据之前的筛选并无法直接确定需要的构造方法,最后根据匹配度做一次验证
5.根据实例化策略以及得到的构造方法参数对bean进行实例化。
二、instantiateBean
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {try {Object beanInstance;final BeanFactory parent = this;//判断安全管理器if (System.getSecurityManager() != null) {//用特权访问方式,并通过特定策略创建bean实例对象beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->getInstantiationStrategy().instantiate(mbd, beanName, parent),getAccessControlContext());}else {//直接通过特定策略创建bean实例对象beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);}BeanWrapper bw = new BeanWrapperImpl(beanInstance);initBeanWrapper(bw);return bw;}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);}
}
这里基本上没有任何的逻辑,没有参数的构造是非常简单的一件事情,直接使用实例化策略进行实例化就可以了。