本篇博客是接着上一篇博客的《Mybatis初始化加载流程—-配置文件解析》,里面使用到的接口和配置文件是一样对的。这里的Mapper接口注册,也只是注册即将用来生成MapperProxy对象的MapperProxyFactory实例,在后面获取接口代理的时候会直接使用MapperProxyFactory的getObject方法,MapperProxyFactory类实现了FactoryBean<T>接口。
下面看一下如何在系统注册MapperProxyFactory相关信息的。
public void parse() {
//判断当前mapper.xml文件是否已经被解析过
if (!configuration.isResourceLoaded(resource)) {
//在configurationElement完成mapper.xml中所有元素的解析,并将解析出来的相关属性全部保存在configuration中
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
//将mapper配置文件对应对的接口注册到configuration中。
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
直接看注册信息的那一部分,也就是绑定Namespace的那一部分。
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
//这里获取的boundType实际上就是java中定义的接口名
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
//添加接口类型到Configuration中的MapperRegistery中
configuration.addMapper(boundType);
}
}
}
}
在Configuration对象中存在一个MapperRegistry对象,实际保存接口是mapperRegistry对象
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
在MapperRegistry对象中保存接口信息,并且保存用来生成接口代理的MapperProxyFactory的实体对象。
//使用一个Mapper保存相关信息,用接口的类型作为key,根据接口类型创建一个MapperProxyFactory对象
//这个对象很重要,在后面获取接口的代理对象就是通过这个对象获取的,MapperProxyFactory实现了FactoryBean接口
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//现在在knownMappers这个集合中已经存在一个以接口类名为key,value为MapperProxyFactory的对象了
//其中将接口类型传入到MapperProxyFactory对象中
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
到目前为止,已经生成了MapperProxyFactory对象了,并且可以根据接口的名称获取MapperProxyFactory对象了。下一步需要知道系统是如何注册根据接口名生成的beanName为名字的BeanDefinition,并且如何确定beanName与具体的beanClass关系。实际上在测试代码中声明的接口为com.interfaces.CityMapper,所以beanName应该是为cityMapper,但是实际上beanClass为MapperFactoryBean<T>,下一篇博客将分析如何确定二者之间的关系的。