MYBATIS初始化流程 加载配置文件关键类
XMLConfigBuilder、 XMLMapperBuilder、 XMLStatementBuilder 这三个类在配置文件加载 过程中非常重要,具体分工如下图所示:
映射器关键类
Configuration
Mybatis启动初始化的核心就是将所有xml配置文件信息加载到Configuration对象中, Configuration是单例的,生命周期是应用级的;
MapperRegistry
mapper接口动态代理工厂类的注册中心。在MyBatis中,通过mapperProxy实现InvocationHandler接口,MapperProxyFactory用于生成动态代理的实例对象;
ResultMap
用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id,result等子元素;
MappedStatement
用于存储mapper.xml文件中的select、insert、update和delete节点,同时还包含了这些节点的很多重要属性;
SqlSource
mapper.xml文件中的sql语句会被解析成SqlSource对象,经过解析SqlSource包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行;
Configuration 对象
实例化并初始化 Configuration 对象是第一个阶段的最终目的,所以熟悉 configuration 对 象是理解第一个阶段代码的核心; configuration 对象的关键属性解析如下
用于解析 mapper.xml 文件中的 resultMap 节点,使用 ResultMapping 来封装id, result 等子元素;
用于创建 BoundSql, mapper.xml 文件中的 sql 语句会被解析成 BoundSql 对 象,经过解析 BoundSql 包含的语句最终仅仅包含?占位符,可以直接提交给数据库执 行;
需要特别注意的是 Configuration 对象在 MyBatis 中是单例的,生命周期是应用级的,换句话说只要 MyBatis 运行 Configuration 对象就会独一无二的存在;在 MyBatis 中仅在 org.apache.ibatis.builder.xml.XMLConfigBuilder.XMLConfigBuilder(XPathParser, String, Properties)中有实例化 configuration 对象的代码。
代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 public class Configuration { protected Environment environment; protected boolean safeRowBoundsEnabled; protected boolean safeResultHandlerEnabled = true ; protected boolean mapUnderscoreToCamelCase; protected boolean aggressiveLazyLoading; protected boolean multipleResultSetsEnabled = true ; protected boolean useGeneratedKeys; protected boolean useColumnLabel = true ; protected boolean cacheEnabled = true ; protected boolean callSettersOnNulls; protected boolean useActualParamName = true ; protected boolean returnInstanceForEmptyRow; protected String logPrefix; protected Class<? extends Log > logImpl; protected Class<? extends VFS > vfsImpl; protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION; protected JdbcType jdbcTypeForNull = JdbcType.OTHER; protected Set<String> lazyLoadTriggerMethods = new HashSet <>(Arrays.asList("equals" , "clone" , "hashCode" , "toString" )); protected Integer defaultStatementTimeout; protected Integer defaultFetchSize; protected ResultSetType defaultResultSetType; protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE; protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL; protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE; protected Properties variables = new Properties (); protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory (); protected ObjectFactory objectFactory = new DefaultObjectFactory (); protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory (); protected boolean lazyLoadingEnabled = false ; protected ProxyFactory proxyFactory = new JavassistProxyFactory (); protected String databaseId; protected Class<?> configurationFactory; protected final MapperRegistry mapperRegistry = new MapperRegistry (this ); protected final InterceptorChain interceptorChain = new InterceptorChain (); protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry (); protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry (); protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry (); protected final Map<String, MappedStatement> mappedStatements = new StrictMap <MappedStatement>("Mapped Statements collection" ) .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and " + targetValue.getResource()); protected final Map<String, Cache> caches = new StrictMap <>("Caches collection" ); protected final Map<String, ResultMap> resultMaps = new StrictMap <>("Result Maps collection" ); protected final Map<String, ParameterMap> parameterMaps = new StrictMap <>("Parameter Maps collection" ); protected final Map<String, KeyGenerator> keyGenerators = new StrictMap <>("Key Generators collection" ); protected final Set<String> loadedResources = new HashSet <>(); protected final Map<String, XNode> sqlFragments = new StrictMap <>("XML fragments parsed from previous mappers" ); protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList <>(); protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList <>(); protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList <>(); protected final Collection<MethodResolver> incompleteMethods = new LinkedList <>(); protected final Map<String, String> cacheRefMap = new HashMap <>();
配置加载过程
Mybatis初始化流程,其实就是组装重量级All-In-One对象Configuration的过程,主要分为系统环境参数初始化和Mapper映射初始化,其中Mapper映射初始化尤为重要。
配置文件解析 1 2 inputStream = Resources.getResourceAsStream("mybatis-config.xml" ); sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream)
进入方法看下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public SqlSessionFactory build (InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder (inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession." , e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { } } } public SqlSessionFactory build (Configuration config) { return new DefaultSqlSessionFactory (config); }
parser.parse()方法,已经返回了组装完毕的Configuration对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public Configuration parse () { if (parsed) { throw new BuilderException ("Each XMLConfigBuilder can only be used once." ); } parsed = true ; parseConfiguration(parser.evalNode("/configuration" )); return configuration; } private void parseConfiguration (XNode root) { try { propertiesElement(root.evalNode("properties" )); 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" )); settingsElement(settings); environmentsElement(root.evalNode("environments" )); 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); } }
以上代码,对mybatis-config.xml配置文件内的元素,使用XPath进行逐一读取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <?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 ="jdbc.properties" > <property name ="username" value ="root" /> <property name ="password" value ="123" /> </properties > <settings > <setting name ="localCacheScope" value ="STATEMENT" /> <setting name ="cacheEnabled" value ="false" /> <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="multipleResultSetsEnabled" value ="true" /> <setting name ="useColumnLabel" value ="true" /> <setting name ="useGeneratedKeys" value ="false" /> <setting name ="defaultExecutorType" value ="REUSE" /> <setting name ="defaultStatementTimeout" value ="25000" /> </settings > <typeAliases > <typeAlias alias ="Student" type ="com.mybatis3.domain.Student" /> <typeAlias alias ="Teacher" type ="com.mybatis3.domain.Teacher" /> </typeAliases > <typeHandlers > <typeHandler handler ="com.mybatis3.typehandlers.PhoneTypeHandler" /> </typeHandlers > <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/mybatis3/mappers/StudentMapper.xml" /> <mapper resource ="com/mybatis3/mappers/TeacherMapper.xml" /> </mappers > </configuration >
mapper配置文件解析
其中最关键的一行代码 mapperElement(root.evalNode(“mappers”));
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 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." ); } } } } } public void parse () { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper" )); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
配置configuration 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private void configurationElement (XNode context) { try { String namespace = context.getStringAttribute("namespace" ); if (namespace == null || namespace.equals("" )) { throw new BuilderException ("Mapper's namespace cannot be empty" ); } builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref" )); cacheElement(context.evalNode("cache" )); parameterMapElement(context.evalNodes("/mapper/parameterMap" )); resultMapElements(context.evalNodes("/mapper/resultMap" )); sqlElement(context.evalNodes("/mapper/sql" )); buildStatementFromContext(context.evalNodes("select|insert|update|delete" )); } catch (Exception e) { throw new BuilderException ("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } }
逐一读取Mapper.xml文件内的各个元素。为了更为直观的了解xml元素至Mybatis的内部数据结构。
这些Xml配置元素,Mybatis将它们分别封装成了ParameterMap、ParameterMapping、ResultMap、ResultMapping、MappedStatement、BoundSql等内部数据结构对象。
这些数据库结构对象,均放置于Configuration内部保存起来。
1 2 3 protected final Map<String, MappedStatement> mappedStatements = new StrictMap <MappedStatement>("Mapped Statements collection" );protected final Map<String, ResultMap> resultMaps = new StrictMap <ResultMap>("Result Maps collection" );protected final Map<String, ParameterMap> parameterMaps = new StrictMap <ParameterMap>("Parameter Maps collection" );