一、思路
- MapperRegister初始化过程;
- MapperRegister使用过程;
二、MapperRegister初始化过程
我们依然以Mybatis结合Spring的背景来分析MapperRegister。首先回顾一下之前SqlSessionFactory初始化过程,我们分析mybats-spring-1.2.5-sources.jar包中初始化代码。
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
// ...省略部分代码
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
// ...省略部分代码
return this.sqlSessionFactoryBuilder.build(configuration);
}
关注其中的xmlConfigBuilder.parse();这里就开始对XX-Mapper.XML的内容进行解析,往下走。
private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
这里就是解析整个XML Config文件,其中的mapperElement(root.evalNode("mappers"));就是对我们定义的XX-Mapper.XML进行解析;
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
解析出接口名后就将该接口注册到configuration中的MapperRegister中,configuration.addMappers(mapperPackage),最终还是调用了MapperRegister中的AddMapper(...)方法。
至此,MapperRegister的初始化流程就简单介绍到这里,下面我们看一下MapperRegister的使用。
三、MapperRegister使用过程
MapperRegister使用过程主要是生成对应MapperProxy实例交给Spring进行管理。我们以一个Spring获取Bean的例子来讲解MapperRegister在其中起到了怎样的作用。
这段代码中,我们主要是解析了spring配置文件后,使用ApplicationContext来获取PersonMapper这个bean;
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:spring.xml");
PersonMapper mapper = (PersonMapper) context.getBean("personMapper");
一路debug跟踪下去,我们会发现最终是调用MapperFactoryBean这个对象。(这里就忽略spring的过程了,关注点在mybatis上)
/**
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
继续往下,spring使用的SqlSession是SqlSessionTemplate,我们再看看SqlSessionTemplate中是怎么实现的。
/**
* {@inheritDoc}
*/
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
发现最终还是调用的Configuration的getMapper方法。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
继续看,我们发现最终还是调用了MapperRegister自己的getMapper方法。
public class MapperRegistry {
private Configuration config;
// 之前xmlConfigBuilder.parse()解析完成注册的对象。
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public MapperRegistry(Configuration config) {
this.config = config;
}
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
// 使用jdk的动态代理方法生成MapperProxy代理对象。
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
// ... 省略部分代码
}
最终我们看到了还是调用了JDK自身的动态代理方法来生成一个MapperProxy代理对象。
好了,今天就到这了~~晚安