mybatis源码分析(一) 配置解析过程
mybatis源码分析(一) 配置解析过程
一丶从加载mybatis-config.xml开始
使用SqlSessionFactoryBuilder解析mybatis-config.xml, 构造SqlSession
String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); this.sqlSession=sqlSessionFactory.openSession(); this.userMapper=sqlSession.getMapper(UserMapper.class);
mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 配置外部属性 --> <properties resource="datasource.properties" /> <!-- 添加日志实现 --> <settings> <setting name="logImpl" value="LOG4J"/> <!-- 是否开启下划线和驼峰式的自动转换, http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Auto-mapping --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <environments default="development"> <environment id="development" > <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/ttx/example/mapper/UserMapper.xml"/> <mapper resource="com/ttx/example/mapper/UserResultMapper.xml"/> <mapper resource="com/ttx/example/mapper/DynamicSqlUserMapper.xml"/> <mapper resource="com/ttx/example/mapper/UserSqlProviderMapper.xml"/> <mapper resource="com/ttx/example/mapper/UserCacheMapper.xml"/> </mappers> </configuration>
二丶SqlSessionFactoryBuilder#build() 解析配置构建SqlSessionFactory
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try {
// 委托给XMLConfigBuilder解析mybatis-config.xml XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 调用parser.parse()解析Configuration return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }
三丶XMLConfigBuilder#parse() 解析xml配置
// 解析方法, 解析配置 public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); //根节点为configuration return configuration; }
private void parseConfiguration(XNode root) { // 解析配置过程, 代码写的有点像spring中的某个方法, // 分出多个配置元素的解析方法
// 这里的方法对应mybatis-config.xml的各个子元素
// 将mybatis-config.xml中的各个元素解析后放进Configuration中
try { //issue #117 read properties first propertiesElement(root.evalNode("properties")); // settings Properties settings = settingsAsProperties(root.evalNode("settings")); // 加载虚拟文件系统 loadCustomVfs(settings); // 加载日志实现 loadCustomLogImpl(settings); // 类型别名 typeAliasesElement(root.evalNode("typeAliases")); // 插件 pluginElement(root.evalNode("plugins")); // 对象工厂 objectFactoryElement(root.evalNode("objectFactory")); // 对象包装工厂 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // 反射器工厂 reflectorFactoryElement(root.evalNode("reflectorFactory")); // ------------------------ // 设置Configuration中的属性, // settings, typeAliases, plugins, objectFactory, objectWrapperFactory, reflectorFactory 对应Configuration中的属性 settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // 环境配置 environmentsElement(root.evalNode("environments")); // 数据库id databaseIdProviderElement(root.evalNode("databaseIdProvider")); // 类型处理器 typeHandlerElement(root.evalNode("typeHandlers")); // 重点 // mappers配置路径 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
四丶重点分析XMLConfigBuilder#mapperElement()解析mapper.xml, 包装成mappedStatement
// 加载mapper // ============================================= // mybatis加载Mapper的总入口 // 1. 通过配置包查找对应的Mapper接口添加, mapper.xml配置文件应在同一个包下 // 2. 通过配置resource= "mapper.xml" 路径来加载 // ============================================= private void mapperElement(XNode parent) throws Exception { // mappers if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { // 1. 可以指定 "包" 级别 String mapperPackage = child.getStringAttribute("name"); // 会加载包名下的所有类, 并使用注解构建器解析, 先解析mapper.xml配置文件, 后再解析mapper.class// 只能通过指定xml配置文件来查找, 如果指定类, 则对应的配置文件需要放在和类同一个包下, 或者classpath路径下 configuration.addMappers(mapperPackage); } else { // 单个指定 // resource, url 指定对应的xml文件, class指定对应的类 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."); } } } } }
五丶XMLMapperBuilder#parse()
/** * 主要方法, parse */ public void parse() { if (!configuration.isResourceLoaded(resource)) { // 加载解析mapper.xml中各个元素, 包括解析statement, resultMap // warning 在MapperProxy中使用MapperMethod封装调用, 会将调用参数转成合适的类型,绑定对应数据 configurationElement(parser.evalNode("/mapper")); // 设置已加载解析的资源 configuration.addLoadedResource(resource); // 加载解析对应的mapper接口, 将接口放到mapper注册表中 bindMapperForNamespace(); } // 加载解析尚未加载完的数据 parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
private void bindMapperForNamespace() { String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { 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添加已加载资源 configuration.addMapper(boundType); // 在configuration添加mapper接口, 然后在mapperRegistry中解析添加 } } } }
六丶将mapper添加进configuration中的mapperRegistry中, 期间由mapperRegistry解析
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, 值为对应的代理工厂(MapperProxyFactory)的类 knownMappers.put(type, new MapperProxyFactory<>(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. // 使用注解构建器构建, 会先解析mapper.xml配置文件, 然后再解析mapper.class文件中的注解 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
人生没有彩排,每一天都是现场直播