Mybatis的Configuration类中使用了大量的Map,但是它没有使用基础的HashMap,而是创建了一个内部类StrictMap
protected static class StrictMap<V> extends HashMap<String, V>;
可以发现该Map的key数据类型被固定了下来,必须是String类型。
protected static class StrictMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
private BiFunction<V, V, String> conflictMessageProducer;
public StrictMap(String name, int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
this.name = name;
}
public StrictMap(String name, int initialCapacity) {
super(initialCapacity);
this.name = name;
}
public StrictMap(String name) {
super();
this.name = name;
}
public StrictMap(String name, Map<String, ? extends V> m) {
super(m);
this.name = name;
}
}
相比于HashMap,StrictMap多了一个私有的name属性,它重写了父类的所有初始化方法,要求必须给name赋值。
protected static class StrictMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
private BiFunction<V, V, String> conflictMessageProducer;
/**
* Assign a function for producing a conflict error message when contains value with the same key.
*
* function arguments are 1st is saved value and 2nd is target value.
* @param conflictMessageProducer A function for producing a conflict error message
* @return a conflict error message
* @since 3.5.0
*/
public StrictMap<V> conflictMessageProducer(BiFunction<V, V, String> conflictMessageProducer) {
this.conflictMessageProducer = conflictMessageProducer;
return this;
}
BiFunction
protected static class StrictMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
private BiFunction<V, V, String> conflictMessageProducer;
protected static class Ambiguity {
private final String subject;
public Ambiguity(String subject) {
this.subject = subject;
}
public String getSubject() {
return subject;
}
}
StrictMap类中也定义一个Ambiguity类型,它很简单,持有一个String类型的字段,它的作用在后面的put和set方法中有体现。
protected static class StrictMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
private BiFunction<V, V, String> conflictMessageProducer;
public StrictMap(String name, int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
this.name = name;
}
public StrictMap(String name, int initialCapacity) {
super(initialCapacity);
this.name = name;
}
public StrictMap(String name) {
super();
this.name = name;
}
public StrictMap(String name, Map<String, ? extends V> m) {
super(m);
this.name = name;
}
@Override
@SuppressWarnings("unchecked")
public V put(String key, V value) {
if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + key
+ (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
}
if (key.contains(".")) {
final String shortKey = getShortName(key);
if (super.get(shortKey) == null) {
super.put(shortKey, value);
} else {
super.put(shortKey, (V) new Ambiguity(shortKey));
}
}
return super.put(key, value);
}
private String getShortName(String key) {
final String[] keyParts = key.split("\\.");
return keyParts[keyParts.length - 1];
}
}
先看put方法,相比于HashMap,它在插入一个新的键值对时确实更为严格。只要Map中包含了这个key,立即抛出一个Exception,这里也可以看到conflicMessageProducer生成错误信息。
如果集合中没有key,但是key中包含了 “ . ” ,比如org.apache.User 那么首先通过私有方法getShortName拿到最后一个" . "对应的部分,根据例子我们拿到的就是User这个key。此时查询字典中是否包含该key,如果不包含就直接插入,如果包含就就插入Ambiguity对象,该对象表示该key存在二义性,相当于一个标识。
此外,还需要将完整的key插入,也就是org.apache.User插入。
protected static class StrictMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
private BiFunction<V, V, String> conflictMessageProducer;
@Override
public V get(Object key) {
V value = super.get(key);
if (value == null) {
throw new IllegalArgumentException(name + " does not contain value for " + key);
}
if (value instanceof Ambiguity) {
throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
+ " (try using the full name including the namespace, or rename one of the entries)");
}
return value;
}
最后看get方法,它比原生get方法确实也更为严苛,如果集合中不存在要查询的key就报错,还会判断是否是Ambiguity的子类,也就是是否在插入时存在二义性,如果是也会报错