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都带过来??? 还搞些双引号””这种怪事情???