ConstructorArgumentValues 构造器参数值保存器

1
2
private final Map<Integer, ConstructorArgumentValues.ValueHolder> indexedArgumentValues = new LinkedHashMap(0);
private final List<ConstructorArgumentValues.ValueHolder> genericArgumentValues = new LinkedList();

这个保存器的核心就在他的LinkedHashMap和LinkedList

疑问: 这里为什么选用linked来储存呢?

答:

添加有index的关键代码如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    private void addOrMergeIndexedArgumentValue(Integer key, ConstructorArgumentValues.ValueHolder newValue) {
      //拿当前这个位置的值
        ConstructorArgumentValues.ValueHolder currentValue = (ConstructorArgumentValues.ValueHolder)this.indexedArgumentValues.get(key);
      //如果当前位置有值,并且新值是接受合并的话,那合并一波之后作为新值
      //如果不接受合并的话,那扔了之前的value不管了,只用新来的value
        if (currentValue != null && newValue.getValue() instanceof Mergeable) {
            Mergeable mergeable = (Mergeable)newValue.getValue();
            if (mergeable.isMergeEnabled()) {
                newValue.setValue(mergeable.merge(currentValue.getValue()));
            }
        }
		//将新值放入LinkedHashMap
        this.indexedArgumentValues.put(key, newValue);
    }

疑问:在获取vh之前我们不就验证过index不能重复么?为什么这里还有可能搞一波合并?是有其它地方也调用这个方法,那里可以合并?

答: 注意到这个类里有个方法,addArgumentValues(ConstructorArgumentValues other),貌似就是专门和其它CAV搞合并的,用CAV做构造参数的那个构造器调用了此方法,这里会有可能需要合并

添加没有index的参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void addGenericArgumentValue(ConstructorArgumentValues.ValueHolder newValue) {
  //校验不为空
    Assert.notNull(newValue, "ValueHolder must not be null");
  //如果list内已经含有此value则跳过
    if (!this.genericArgumentValues.contains(newValue)) {
        this.addOrMergeGenericArgumentValue(newValue);
    }
}
//添加或合并VH
private void addOrMergeGenericArgumentValue(ConstructorArgumentValues.ValueHolder newValue) {
    if (newValue.getName() != null) {
        Iterator it = this.genericArgumentValues.iterator();
		//如果新VH有name,那么就要遍历之前的list,找出同名的老VH
        //看新VH接不接受合并,接收就合并下再删,不接受就直接删掉.
        while(it.hasNext()) {
            ConstructorArgumentValues.ValueHolder currentValue = (ConstructorArgumentValues.ValueHolder)it.next();
            if (newValue.getName().equals(currentValue.getName())) {
                if (newValue.getValue() instanceof Mergeable) {
                    Mergeable mergeable = (Mergeable)newValue.getValue();
                    if (mergeable.isMergeEnabled()) {
                        newValue.setValue(mergeable.merge(currentValue.getValue()));
                    }
                }
                it.remove();
            }
        }
    }
	//将新VH塞入list里面
    this.genericArgumentValues.add(newValue);
}

发现addGenericArgumentValue(Object value) 和addGenericArgumentValue(Object value, String type)就没这么麻烦,因为他们拿Object构造的肯定没name,也不会为null,直接懒得考虑合并,也不去验证list是否已存在这个VH, 直接塞…有相同的覆盖掉就是了…

只是暂时还没看到在哪这么直接地调用这种塞Object的方法

既然都看完了CAV的赋值,干脆来看看它的取值和其它方法吧!

  1. 获取它的list,map都是 Collections.unmodifiableMap(this.indexedArgumentValues);

    Collections.unmodifiableList(this.genericArgumentValues);这样都是返回只读的集合出去,调用set/add/remove的话会报错

  2.  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
                public int getArgumentCount() {
                    return this.indexedArgumentValues.size() + this.genericArgumentValues.size();
                }
    
                public boolean isEmpty() {
                    return this.indexedArgumentValues.isEmpty() && this.genericArgumentValues.isEmpty();
                }
    
                public void clear() {
                    this.indexedArgumentValues.clear();
                    this.genericArgumentValues.clear();
                }
    
  3. 重头戏,拿参数值,这里又分为3种情况,取Indexed的,取Generic的,拿个index就想直接取的,每种情况里都有不同参数的重载.

    我估计实际使用应该是都用第三种情况吧,因为包括了前两种,

    而且真正构造需要拿参数的时候,肯定是想给个index,按照顺序去CAV里直接拿,不管放入参数时是否indexed

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
                public ConstructorArgumentValues.ValueHolder getArgumentValue(int index, Class<?> requiredType, String requiredName, Set<ConstructorArgumentValues.ValueHolder> usedValueHolders) {
                    Assert.isTrue(index >= 0, "Index must not be negative");
                  //先根据index去Map里面找
                    ConstructorArgumentValues.ValueHolder valueHolder = this.getIndexedArgumentValue(index, requiredType, requiredName);
                    if (valueHolder == null) {
                      //如果没有的话,就再去list里面找, 从这可以看出,我们在XML里写参数的时候,最好是带上index,在这里找的快一点哦!
                        valueHolder = this.getGenericArgumentValue(requiredType, requiredName, usedValueHolders);
                    }
                    return valueHolder;
                }
    

从map里面取Indexed的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
           public ConstructorArgumentValues.ValueHolder getIndexedArgumentValue(int index, Class<?> requiredType, String requiredName) {
               Assert.isTrue(index >= 0, "Index must not be negative");
               ConstructorArgumentValues.ValueHolder valueHolder = (ConstructorArgumentValues.ValueHolder)this.indexedArgumentValues.get(index);
             //这里一波||&&!简直了...总结一下有哪些情况返回null吧
             /**
   			 *  1. 根据这个index取出来的VH就是null
      		 *  2. VH的type不为null,可是( requiredType为null 或者 两者type不匹配)
      		 *  3. VH的name不为null,requiredName不为"" 可是( requiredName为null 或者 两者name不匹配)  
    	*/
     return valueHolder == null || valueHolder.getType() != null && (requiredType == null || !ClassUtils.matchesTypeName(requiredType, valueHolder.getType())) || valueHolder.getName() != null && !"".equals(requiredName) && (requiredName == null || !requiredName.equals(valueHolder.getName())) ? null : valueHolder;
    }

注意到一个细节,在判断name的时候, requiredName如果给了"" 这个值,就不会拿去判断name是否匹配…不知这是何用意…

从list里面取

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
   public ConstructorArgumentValues.ValueHolder getGenericArgumentValue(Class<?> requiredType, String requiredName, Set<ConstructorArgumentValues.ValueHolder> usedValueHolders) {
       Iterator var4 = this.genericArgumentValues.iterator();

       ConstructorArgumentValues.ValueHolder valueHolder;
     //这里的4层嵌套循环和疯狂的||&&!更是简直了........从里向外一层层看吧
     
       do {//最后看这个最外层,如果同时满足下列条件,说明这个VH还不是我们要的
       /**
         *	1.requiredType不是null
         *	2.VH的Type却是null
         *	3.VH的name也是null
         *	4.type校验通不过   
         *	(第4步校验要通过需要满足以下情况之一 
         *  a. value==null 且 requiredType不是基本数据类型
         *	b. value不等于null 且 (valueClass继承或实现requiredType 
         *						 或valueClass包装或解包装后能等于requiredType..)
         */
         
           while(true) {//再次 看这层,如果匹配到类型相同的,就说明有可能找到我们需要的参数啦,跳出来到最外层再接受一次校验.
             //如果不匹配的话,继续执行里面的循环找下一个VH吧.
             
               do {//其次看这层循环,如果VH有name,requiredName不为"",但rqName为null或者两者不相等,跳过此VH,这说明VH有name的话就很关键,必须相同啊,除非rqName==""
                 
                   do {//首先看这里的循环, 取下一个VH,如果取完了,就返回null
                       if (!var4.hasNext()) {
                           return null;
                       }
                       valueHolder = (ConstructorArgumentValues.ValueHolder)var4.next();
                   //如果set不为空,并且set里已经包含这个VH了就跳过此VH
                   } while(usedValueHolders != null && usedValueHolders.contains(valueHolder));
               } while(valueHolder.getName() != null && !"".equals(requiredName) && (requiredName == null || !valueHolder.getName().equals(requiredName)));
                 
               if (valueHolder.getType() == null || requiredType != null && ClassUtils.matchesTypeName(requiredType, valueHolder.getType())) {
                   break;
               }
           }
       } while(requiredType != null && valueHolder.getType() == null && valueHolder.getName() == null && !ClassUtils.isAssignableValue(requiredType, valueHolder.getValue()));

       return valueHolder;
   }

总而言之,从list里面找到我们需要的那个参数真是非常麻烦,需要这个VH满足下列条件:

  1. 不在usedVH的那个set里
  2. 有name且rqName不是"" 就必须name匹配
  3. 有rqType的话,说明必须来满足这个type的匹配,那么type怎么确认是匹配的呢?
    1. type不是null,那就直接去匹配
    2. type是null,如果name也是null,(说明之前用不写name方式绕过了上面的第2步name验证,如果上面第二步用的是"“方式就不用管了,直接算通过), 那就必须要value.getClass能去真正的和rqType进行匹配 (继承,实现,包装,解包各种方式能匹配上就OK)

这里是真的麻烦,再换个角度再梳理一遍!

  1. 如果rqName写了比如"age”,VH里name也写了的话,那就必须name也是"age"才通过
  2. 如果rqName写个"",那就表示name属性完全不管了,都算通过
  3. 如果rqName是null,那有name的VH都不行, 没name的VH可以通过
  4. 如果rqType没写,那就表示type不管了,都算通过(估计实际调用不会name和type全部不管吧…….)
  5. 如果rqType写了,那就表示要验证type啦,从上面放行过来的VH需要校验了
    1. 如果VHtype为null,但是有name,说明通过1.2放过来的,都算ok (那个"“就很坑了,VH随便填个name,不填type就可能全部通过了)
    2. 如果VHtype为null,name也是null,那就去ClassUtils.isAssignableValue校验VHvalue的本质class能否对得上.
    3. 如果VHtype不是null,那就必须type也完全一致

我预测正常调用情况应该是rqName和rqType都有明确要求吧,那么正常流程就应该是

  1. VH有name最好,匹配上直接通过!
  2. VH如果没name,有type,那就去匹配type!
  3. VH如果啥都没有,那就拿VHvalue的class去跟rqType匹配,这总跑不了了吧!

这个地方盯着看了几个小时,不知道作者写这么复杂是何居心,调用的时候也准备做这么复杂么?拿构造器参数的时候就不能把requiredName和requiredType都带过来??? 还搞些双引号"“这种怪事情???