您的位置:首页 > 移动开发

mybatis如何加载mapper并解析的

2014-03-04 15:08 330 查看
1.什么是我们知道的:

我们知道的是mybatis的主配置文件是mybatis-config.xml,而在mybatis-config.xml 里关联了许多具体的sqlMapper.xml文件.

2.什么是我们想知道的:

我们想知道的是这些配置文件是怎么在启动的时候被读取并被工厂类消化的。

3. 怎么办?debug!

接下来我通过一些错误信息找到了一些有用的类信息,并且打上了断点,查找到了方法堆栈,打印如下

XMLMapperBuilder.parse() line: 87

XMLConfigBuilder.mapperElement(XNode) line: 317

XMLConfigBuilder.parseConfiguration(XNode) line: 104

XMLConfigBuilder.parse() line: 89

SqlSessionFactoryBean.buildSqlSessionFactory() line: 424

SqlSessionFactoryBean.afterPropertiesSet() line: 336

DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).invokeInitMethods(String, Object, RootBeanDefinition) line: 1541

DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(String, Object, RootBeanDefinition) line: 1479

DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 521

DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 458

AbstractBeanFactory$1.getObject() line: 295

DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory) line: 223

DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 292

DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 194

DefaultListableBeanFactory.preInstantiateSingletons() line: 608

ClassPathXmlApplicationContext(AbstractApplicationContext).finishBeanFactoryInitialization(ConfigurableListableBeanFactory) line: 932

ClassPathXmlApplicationContext(AbstractApplicationContext).refresh() line: 479

ClassPathXmlApplicationContext.<init>(String[], boolean, ApplicationContext) line: 139

ClassPathXmlApplicationContext.<init>(String) line: 83

ActivityTest.init() line: 20

SqlSessionFactoryBean.java 是一个神奇的类 在mybatis启动中扮演了重要的角色

下面我将对这段代码进行单补调试,并在我能力范围之内给出一些说明,如有错误和不足望指教:

/**
* Build a {@code SqlSessionFactory} instance.
*
* The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
* {@code SqlSessionFactory} instance based on an Reader.
*
* @return SqlSessionFactory
* @throws IOException if loading the config file failed
*/
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

Configuration configuration;

XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}

if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}

if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}

if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}

if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Registered type alias: '" + typeAlias + "'");
}
}
}

if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Registered plugin: '" + plugin + "'");
}
}
}

if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}

if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Registered type handler: '" + typeHandler + "'");
}
}
}

if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();

if (this.logger.isDebugEnabled()) {
this.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();
}
}

if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}

Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
configuration.setEnvironment(environment);

if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}

if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}

try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}

if (this.logger.isDebugEnabled()) {
this.logger.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}

return this.sqlSessionFactoryBuilder.build(configuration);
}


①代码16行处

mybatis-config.xml经过xmlConfigBuilder生成了configuration.(里面用到了parser模块,好像有xml配置文件的开源代码都有这么一个类似的模块)

具体过程:将文件流转成xml的document放入到一个新建的XPathParser对象中 ,再贮存在XMLConfigBuilder 对象中

流程如下

SqlSessionFactoryBean->XMLConfigBuilder->XPathParser->@return Document builder.parse(inputSource){

com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl }

SqlSessionFactoryBean的xmlConfigBuilder对应此xmlConfigBuilder

经过这一波configuration的parser变量会指向XPathParser ,XPathParser 的document就贮存了上面返回的document。

XPathParser.createDocument(InputSource) line: 251

XPathParser.<init>(InputStream, boolean, Properties, EntityResolver) line: 122

XMLConfigBuilder.<init>(InputStream, String, Properties) line: 72

SqlSessionFactoryBean.buildSqlSessionFactory() line: 354

SqlSessionFactoryBean.afterPropertiesSet() line: 336

具体代码如下

XPathParser.java

private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);

factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);

DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
public void error(SAXParseException exception) throws SAXException {
throw exception;
}

public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}

public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance.  Cause: " + e, e);
}
}


②代码17行处

SqlSessionFactoryBean的configuration对应此xmlConfigBuilder的configuration

③代码34行(可以略过)

处默认的${mybatis.alias.basepackage}基础package

④86行

xmlConfigBuilder.parse();

-> parseConfiguration(parser.evalNode("/configuration")); in XMLConfigBuilder.java @89

XMLConfigBuilder.parseConfiguration(XNode) line: 104
解析了configuration节点下

mappers底下的各个mapper

config/mapper/UserMapper.xml

这里会使用javax.xml.xpath.XPath;来解析各个节点 类似于 /foo/bar/@id 对应于 <foo><bar id="barId"/></foo>的id属性

这里对我来说是一个新的知识点,通过javax.xml.xpath.XPath 来找到对应的xml dom 的节点/foo/bar/@id指的就是

parseConfiguration 对<configuration>xml节点进行解析

<configuration>

<typeAliases>

<typeAlias alias="User" type="cola.machine.calendar.user.bean.User"/>

<typeAlias alias="Activity" type="cola.machine.calendar.activity.bean.Activity"/>

</typeAliases>

<mappers>

<mapper resource="config/mapper/UserMapper.xml"/>

<mapper resource="config/mapper/ActivityMapper.xml"/>

</mappers>

</configuration>

下面是XmlConfigBuilder里的一段代码

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);

}

}

⑤120行

针对<mapper>元素会调用XmlConfigBuilder 的mapperElement方法

这一处代码是解析resource->inputStream->InputSource

此之前 configuration中会有含有由mybatis-config.xml解析成的document

⑥86行 xmlConfigBuilder.parse();

会对configuration里的document进行解析

parseConfiguration(parser.evalNode("/configuration"));

这里涉及的configuration是mybatis的一大核心。

从mybatis主页上的flow图来看configration对应了mybatis主要的xml配置文件。 configuration的loadResources是一个hashset 里面存储了所有的xml配置文件地址。

XMLMapperBuilder 是解析sqlMapper的主要类.MapperBuilderAssistant

代码1处会去判断改资源是否加载过 resource 是String 类型的uri资源地址, 比如“config/mapper/UserMapper.xml”。

SqlSessionFactoryBean是第一个觉得是处理mybatis-config.xml的地方

SqlSessionFactoryBean.afterPropertiesSet() line: 336

this.sqlSessionFactory = buildSqlSessionFactory();

SqlSessionFactoryBean包含了

private Resource configLocation变量 实际值为 class path resource [config/xml/mybatis-config.xml]

.buildSqlSessionFactory() 是第一次加载

他会对mybatis-config.xml进行解析 对配置的sqlMapper.xml

昨天因为几个错误去debug了下mybatis的代码,有些心得。

首先,因为mybatis和spring 进行了整合,用到了spring mybatis的整合jar包,让spring管理了mybatis的类的生命周期。

ClassPathXmlApplicationContext 首先会对applicationContext进行解析 事先装载所需要的类

DefaultListableBeanFactory

public void parse() {

if (!configuration.isResourceLoaded(resource)) {//1

configurationElement(parser.evalNode("/mapper"));//2

configuration.addLoadedResource(resource);//3

bindMapperForNamespace();//4

}

parsePendingResultMaps();

parsePendingChacheRefs();

parsePendingStatements();

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: