zoukankan      html  css  js  c++  java
  • AbstractBeanDefinition:lenientConstructorResolution属性源码分析

    版本: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 {
        @Override
        public 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=false
            boolean 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是转换后的参数,相对于rawArguments
            if (!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_VALUE
            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) {
                    //本身类 权重+2
                    if (paramType.equals(superClass)) {
                        result = result + 2;
                        superClass = null;
                    }
                    //父子、接口实现类 权重+2
                    else if (ClassUtils.isAssignable(paramType, superClass)) { 
                        result = result + 2;
                        superClass = superClass.getSuperclass();
                    }
                    else {
                        superClass = null;
                    }
                }
                //是接口权重+1
                if (paramType.isInterface()) {
                    result = result + 1;
                }
            }
        }
        return result;
    }
    

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

    五、结论

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

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

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

      注:文章中难免有不足之处,欢迎评论、互动、指正。

    作者: i-nine
    原创不易,本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    沙漠之王(0/1分数规划+ 最小生成树)
    野餐规划(最小生成树性质)⭐
    走廊泼水节(最小生成树定理)⭐
    兄弟选择器+否定伪类
    子元素的伪类
    属性选择器
    伪元素
    伪类选择器
    Java连接Mysql由于版本更新报错
    Mac下安装SQL
  • 原文地址:https://www.cnblogs.com/ninth/p/6339498.html
Copyright © 2011-2022 走看看