mybatis如何加载mapper并解析的
来源:互联网 发布:在淘宝被骗了怎么办 编辑:程序博客网 时间:2024/05/21 07:14
我们知道的是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();
}
- mybatis如何加载mapper并解析的
- mybatis通用mapper的方法解析
- mybatis mapper参数解析
- Mybatis热加载Mapper.xml
- mybatis中加载mapper文件
- Mybatis 加载 Mapper配置的四种方式
- mybatis配置文件(mapper)属性解析
- mybatis的mapper
- mybatis mapper的配置
- mybatis的mapper XML
- MyBatis mapper的理解
- Mybatis的Mapper配置
- 加载mapper 的方法
- mybatis热部署加载*Mapper.xml文件
- Mybatis源码解析-MapperRegistry注册mapper接口
- Mybatis源码解析-MapperRegistry注册mapper接口
- Mybatis 源码解析二、Mapper接口的代理实现过程 MapperScannerConfigurer 解析
- mybatis的mapper文件注意事项
- uva 1531 & poj 1518 Problem Bee(几何计算+贪心)
- jsp登陆界面实现过程
- 2010Execl冻结多行
- android hal
- 想你的365天
- mybatis如何加载mapper并解析的
- linux mmap 详解
- 百度:41亿打造搜索“机械人心”
- 生成模型与判别模型
- 核方法原理
- 简单的签到程序
- LOG4J COMMON-LOGGING 简单认识
- Linux 下安装、配置、汉化JIRA6.1.5
- 【Unity3D自学记录】AssetBundles的使用