版本:spring-framework-4.1

一概述

在看AbstractBeanDefinition源码时,注意到lenientConstructorResolution属性有诸多不疑,现在通过示例及源码分析,一步步揭开面纱。

二、本文希望能解释清楚的几个问题

  1. lenientConstructorResolution属性的作用?
  2. lenientConstructorResolution的值ture与false有什么区别?
  3. 源码中是如何实现的,为何使用权重设计?

三、示例

3.1 applicationContext-Lenient.xml

<bean id="zoo" class="com.bean.Zoo" scope="prototype" factory-method="create"><constructor-arg><bean class="com.bean.Cat"/></constructor-arg>
</bean>

3.2 Animal.java

public abstract class Animal {public void run() {System.out.println("[abstract class Animal] run ! ");}
}

3.3 Animal.java

public class Cat extends Animal {@Overridepublic void run() {System.out.println("[class Cat] run !");}
}

3.4 Zoo.java

public class Zoo {private final Animal animal;public Animal getAnimal() {return animal;}public Zoo(){System.out.println("Zoo() executed!");animal=null;}public Zoo(Animal animal){System.out.println("Zoo(Animal animal) executed!");this.animal=animal;}public Zoo(Cat cat){System.out.println("Zoo(Cat cat) executed!");this.animal=cat;}public static Zoo create(){return new Zoo();}public static Zoo create(Animal animal){return new Zoo(animal);}public static Zoo create(Cat cat){return new Zoo(cat);}
}

3.5 宽松模式测试:

@Test
public void testLenient() {DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();new XmlBeanDefinitionReader(xbf).loadBeanDefinitions("applicationContext-Lenient.xml");AbstractBeanDefinition bd = (AbstractBeanDefinition) xbf.getBeanDefinition("zoo");bd.setLenientConstructorResolution(true);try {Zoo zoo=(Zoo)xbf.getBean("zoo");zoo.getAnimal().run();}catch (BeanCreationException ex) {ex.printStackTrace();}
}打印结果:
Zoo(Cat cat) executed!
[class Cat] run !

3.6 非宽松模式测试:

@Test
public void testNonLenient() {DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();new XmlBeanDefinitionReader(xbf).loadBeanDefinitions("applicationContext-Lenient.xml");AbstractBeanDefinition bd = (AbstractBeanDefinition) xbf.getBeanDefinition("zoo");bd.setLenientConstructorResolution(false);try {Zoo zoo=(Zoo)xbf.getBean("zoo");zoo.getAnimal().run();}catch (BeanCreationException ex) {ex.printStackTrace();}
}打印结果:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'zoo' defined in class path resource [applicationContext-Lenient.xml]: Ambiguous factory method matches found in bean 'zoo' (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): [public static com.bean.Zoo com.bean.Zoo.create(com.bean.Animal), public static com.bean.Zoo com.bean.Zoo.create(com.bean.Cat)]

两个测试结果,宽松模式下能够正常构建实例,而在非宽松模式下却抛出BeanCreationException,大致意思就是在zoo中发现了存在歧义的工厂方法。带着问题,分析内部源码。

四、源码分析

在追踪源码时经过了很多的实现,我们只看最关键的几个实现:

4.1 ConstructorResolver.java

public BeanWrapper instantiateUsingFactoryMethod(final String beanName, final RootBeanDefinition mbd, final Object[] explicitArgs) {... ... if (factoryMethodToUse == null || argsToUse == null) {//寻找匹配类型的工厂方法,factoryClass=com.bean.Zoo(配置文件中bean定义的)factoryClass = ClassUtils.getUserClass(factoryClass);//获取factoryClass所有方法,也包括继承来的方法Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);//过滤出factoryClass中factory-method指定的方法List<Method> candidateSet = new ArrayList<Method>();for (Method candidate : rawCandidates) {if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {candidateSet.add(candidate);}}Method[] candidates = candidateSet.toArray(new Method[candidateSet.size()]);//排序,public构造函数优先参数数量降序、非public构造函数参数数量降序AutowireUtils.sortFactoryMethods(candidates);ConstructorArgumentValues resolvedValues = null;//构造器装配,显然我们使用的工厂方法构造,这里autowiring=falseboolean autowiring = (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);int minTypeDiffWeight = Integer.MAX_VALUE;Set<Method> ambiguousFactoryMethods = null; //引起歧义的方法列表int minNrOfArgs;if (explicitArgs != null) {minNrOfArgs = explicitArgs.length;}else {//提取配置文件中定义的构造函数参数(constructor-arg)ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();//解析后的参数值resolvedValues = new ConstructorArgumentValues();//参数的数量minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);}List<Exception> causes = null;for (int i = 0; i < candidates.length; i++) {//工厂方法函数,遍历匹配Method candidate = candidates[i];  Class<?>[] paramTypes = candidate.getParameterTypes();if (paramTypes.length >= minNrOfArgs) {ArgumentsHolder argsHolder;if (resolvedValues != null) {// Resolved constructor arguments: type conversion and/or autowiring necessary.try {String[] paramNames = null;//获取参数名称探索器ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();if (pnd != null) {paramNames = pnd.getParameterNames(candidate);}argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring);}catch (UnsatisfiedDependencyException ex) {if (this.beanFactory.logger.isTraceEnabled()) {this.beanFactory.logger.trace("Ignoring factory method [" + candidate +"] of bean '" + beanName + "': " + ex);}if (i == candidates.length - 1 && argsHolderToUse == null) {if (causes != null) {for (Exception cause : causes) {this.beanFactory.onSuppressedException(cause);}}throw ex;}else {// Swallow and try next overloaded factory method.if (causes == null) {causes = new LinkedList<Exception>();}causes.add(ex);continue;}}}else {// Explicit arguments given -> arguments length must match exactly.if (paramTypes.length != explicitArgs.length) {continue;}//无参构造的情况 argsHolder = new ArgumentsHolder(explicitArgs);}//重点!!!//计算权重,宽松模式下执行getTypeDifferenceWeight方法,计算细节请看4.3//非宽松模式下执行int typeDiffWeight = (mbd.isLenientConstructorResolution() ?argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));// Choose this factory method if it represents the closest match.if (typeDiffWeight < minTypeDiffWeight) {factoryMethodToUse = candidate;argsHolderToUse = argsHolder;argsToUse = argsHolder.arguments;minTypeDiffWeight = typeDiffWeight;ambiguousFactoryMethods = null;}//确定歧义函数的逻辑else if (factoryMethodToUse != null //至少找到一个匹配的方法&& typeDiffWeight == minTypeDiffWeight  //两个参数类型权重相同&& !mbd.isLenientConstructorResolution()  //必须是非宽松模式//参数类型、长度相等&& paramTypes.length == factoryMethodToUse.getParameterTypes().length && !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {if (ambiguousFactoryMethods == null) {ambiguousFactoryMethods = new LinkedHashSet<Method>();ambiguousFactoryMethods.add(factoryMethodToUse);}//到这一步,基本确定已找到有歧义的方法ambiguousFactoryMethods.add(candidate);}}}//未匹配到可用的工厂方法if (factoryMethodToUse == null) {List<String> argTypes = new ArrayList<String>(minNrOfArgs);if (explicitArgs != null) {for (Object arg : explicitArgs) {argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");}}else {Set<ValueHolder> valueHolders = new LinkedHashSet<ValueHolder>(resolvedValues.getArgumentCount());valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());valueHolders.addAll(resolvedValues.getGenericArgumentValues());for (ValueHolder value : valueHolders) {String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) :(value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null"));argTypes.add(argType);}}String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);throw new BeanCreationException(mbd.getResourceDescription(), beanName,"No matching factory method found: " +(mbd.getFactoryBeanName() != null ?"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +"Check that a method with the specified name " +(minNrOfArgs > 0 ? "and arguments " : "") +"exists and that it is " +(isStatic ? "static" : "non-static") + ".");}//返回类型为void,抛异常else if (void.class.equals(factoryMethodToUse.getReturnType())) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Invalid factory method '" + mbd.getFactoryMethodName() +"': needs to have a non-void return type!");}//当存在歧义函数的情况下,抛异常,(也就是3.4示例“非宽松模式测试”抛出的异常)else if (ambiguousFactoryMethods != null) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Ambiguous factory method matches found in bean '" + beanName + "' " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +ambiguousFactoryMethods);}if (explicitArgs == null && argsHolderToUse != null) {argsHolderToUse.storeCache(mbd, factoryMethodToUse);}}
}

4.2 非宽松模式权重计算

public int getAssignabilityWeight(Class<?>[] paramTypes) {for (int i = 0; i < paramTypes.length; i++) {//父子类、接口实现类或基本数据类型返回true,arguments是转换后的参数,相对于rawArgumentsif (!ClassUtils.isAssignableValue(paramTypes[i], this.arguments[i])) {return Integer.MAX_VALUE;}}for (int i = 0; i < paramTypes.length; i++) {//与原始参数对比,父子类或原始类型返回ture,这里的rawArguments是原始参数if (!ClassUtils.isAssignableValue(paramTypes[i], this.rawArguments[i])) {return Integer.MAX_VALUE - 512;}}return Integer.MAX_VALUE - 1024;
}

非宽松模式的权重计算,重点是只要是父子类或实现类返回Ture,也就意味着这个函数在多个构造函数参数为父子类或实现类的关系时,会全部返回(Integer.MAX_VALUE - 1024),这样返回统一的数字相等,spring就会认为存在有歧义的函数,不能确定使用哪一个。

4.3 宽松模式权重计算

public int getTypeDifferenceWeight(Class<?>[] paramTypes) {//先与转换后的参数做计算int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments);//再与原始参数做计算int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024;//返回最小的那个值,值越小越接近本身的类型。return (rawTypeDiffWeight < typeDiffWeight ? rawTypeDiffWeight : typeDiffWeight);
}//MethodInvoker.java
public static int getTypeDifferenceWeight(Class<?>[] paramTypes, Object[] args) {int result = 0;for (int i = 0; i < paramTypes.length; i++) {//非父子或实现类或基本数据类型 返回 Integer.MAX_VALUEif (!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) {//本身类 权重+2if (paramType.equals(superClass)) {result = result + 2;superClass = null;}//父子、接口实现类 权重+2else if (ClassUtils.isAssignable(paramType, superClass)) { result = result + 2;superClass = superClass.getSuperclass();}else {superClass = null;}}//是接口权重+1if (paramType.isInterface()) {result = result + 1;}}}return result;
}

看以上计算方式,宽松模式的权重计算,主要是查找最接近本身类型的那个函数。

五、结论

  1. lenientConstructorResolution属性的作用:确定构造函数是是否使用宽松构造的方式。

  2. lenientConstructorResolution的值ture与false有什么区别:这个属性默认值是true,在大部分情况下都是使用宽松模式,即使多个构造函数的参数数量相同、类型存在父子类、接口实现类关系也能正常创建bean。非宽松模式则相反,的使用场景还需要继续探索。

  3. 源码中是如何实现的,使用权重设计的目的是:第四小节为源码的实现,还有不足之处。至于使用权重的设计,在查看其他源码时,会发现,spring中大量使用位运算,权重计算等数值运算,我想这应该是数值运算类型效率最高的原因。

转载于:https://www.cnblogs.com/ninth/p/6339498.html

AbstractBeanDefinition:lenientConstructorResolution属性源码分析相关推荐

  1. 框架源码专题:springIOC的加载过程,bean的生命周期,结合spring源码分析

    文章目录 1.BeanFactory和ApplicationContext的区别? 2. IOC与 Bean的加载过程 ①:初始化容器DefaultListableBeanFactory ②:创建读取 ...

  2. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

  3. Spring Ioc源码分析 之 Bean的加载(6):属性填充(populateBean())

    "属性填充",也是在populateBean()方法中. 首先回顾下CreateBean的主流程: 如果是单例模式,从factoryBeanInstanceCache 缓存中获取B ...

  4. 案例:演示pageContext对象的使用及源码分析获取属性方法

    一.创建pageContext.jsp <%@ page language="java" contentType="text/html; charset=UTF-8 ...

  5. Android属性动画赏析,Android源码分析—属性动画的工作原理

    前言 本文为Android动画系列的最后一篇文章,通过对源码的分析,能够让大家更深刻地理解属性动画的工作原理,这有助于我们更好地使用属性动画.但是,由于动画的底层实现已经深入到jni层,并且涉及到显示 ...

  6. Vue.js 源码分析(五) 基础篇 方法 methods属性详解

    methods中定义了Vue实例的方法,官网是这样介绍的: 例如:: <!DOCTYPE html> <html lang="en"> <head&g ...

  7. Android源码分析—属性动画的工作原理

    转载请注明出处: http://blog.csdn.net/singwhatiwanna/article/details/17853275 前言 本文为Android动画系列的最后一篇文章,通过对源码 ...

  8. openstack-nova源码分析(五)flavor extra_specs 扩展属性

    Flavors extra-specs (flavors, os-flavor-extra-specs) Flavor 扩展属性设置, 扩展属性可以用来对虚拟机做一些额外的限制设置,具体的参数,将在后 ...

  9. spring源码分析之BeanDefinition相关

    目录 前言: BeanDefinition的家族系列 1.BeanDefintion的UML类图 2.BeanDefintion家族类详解 2.1.通用接口 2.2.BeanDefintion接口 2 ...

最新文章

  1. Leetcode 剑指 Offer 09. 用两个栈实现队列 (每日一题 20210915)
  2. 奎屯电信助力智慧城市光网建设
  3. Spring依赖注入–字段vs设置器vs构造函数注入
  4. Android JNI入门第五篇——基本数据类型使用
  5. c语言编程顺序查找例题,C语言典型编程例题.doc
  6. 运用OpenMP提速图像处理速度
  7. CentOS6最小化安装所需的常用软件(未完待更新)
  8. 实现electron-bridge
  9. 360 小程序来了,进攻 PC 端!
  10. HDU2187 老人是真饿了【贪心】
  11. 用swift写的一款小游戏,模仿的僵尸危机
  12. 【北京迅为】i.MX6ULL终结者Linux RS232/485驱动实验RS232驱动
  13. 使用 aspose 框架实现ex转pdf,图片。word转pdf,图片。pdf转图片
  14. 电子厂计算机维修周记,电子厂实习周记【三篇】【完整版】
  15. 关于vue3 的vue-router.mjs:3434 ReferenceError: parcelRequire is not defined
  16. 有的放矢-电气工程师的工作重心
  17. 用python画皮卡丘的代码-利用Python绘制萌萌哒的皮卡丘
  18. 回归测试(Regression Test)
  19. coursera 吴恩达 -- 第一课 神经网络和深度学习 :第三周课后习题 Shallow Neural Networks Quiz, 10 questions
  20. shl微型计算机,微型计算机技术-中国大学mooc-题库零氪

热门文章

  1. ZIP,一个没落天才的故事
  2. Windows 7 PE RAM 引导盘及WIM 镜像制作
  3. PYTHON1.day01
  4. 微信小程序开发流程介绍
  5. select 下拉菜单Option对象使用add(elements,index)方法动态添加
  6. HDOJ 1214 圆桌会议
  7. 学习Windows2008——设计活动目录
  8. PHP命名规范【转】
  9. 基本类型和引用类型的传值
  10. C#中自定义PictureBox控件