mybatis source analysis (a) parsing configuration
Dian starting from a load mybatis-config.xml
Use SqlSessionFactoryBuilder parsing mybatis-config.xml, configured 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 the Configuration the PUBLIC "- // mybatis.org//DTD Config 3.0 // EN" "http://mybatis.org/dtd/mybatis config.dtd--3 " > < configuration > <-! configuration external properties -> < properties Resource =" datasource.properties " /> <-! Add log implemented -> < Settings > < Setting name =" logImpl " value =" LOG4J " /> <-! whether to open an underscore and camel automatic conversion, 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>
Two Dian SqlSessionFactoryBuilder # build () parsing configuration building 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. } } }
Wed and XMLConfigBuilder # the parse () parse xml configuration
// 解析方法, 解析配置 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 the root) { // parse the configuration process, a method to write code in a bit like a spring, // separated analytical method for the configuration element
// methods described herein correspond to the respective mybatis-config.xml child element
// mybatis-config.xml the individual elements in the Configuration parsed into
the try { // Issue # 117 Read Properties First propertiesElement (root.evalNode ( "Properties" )); // Settings the Properties Settings settingsAsProperties = ( root.evalNode ( "Settings" )); // load the virtual file system loadCustomVfs (Settings); // load the log to achieve loadCustomLogImpl (Settings); // type alias typeAliasesElement (root.evalNode ( "typeAliases" )); // plugins pluginElement (root.evalNode ( "plugins" )); // object factory objectFactoryElement (root.evalNode ( "objectFactory" )); // objects packaging plants objectWrapperFactoryElement ( root.evalNode ( "objectWrapperFactory" )); // reflector factory reflectorFactoryElement (root.evalNode ( "reflectorFactory" )); // --------------------- --- // set Configuration attributes, // settings, typeAliases, plugins, the objectFactory, objectWrapperFactory, Configuration attributes corresponding to reflectorFactory settingsElement (Settings); // the Read the After objectFactory IT and objectWrapperFactory Issue # 631 // environment configuration environmentsElement (root.evalNode ( "Environments" )); // database the above mentioned id databaseIdProviderElement (root.evalNode ( "databaseIdProvider" )); // type of processor typeHandlerElement (root.evalNode ( "typeHandlers" )); // key // by mappers configuration path mapperElement (root.evalNode ( "by mappers" )); } the catch (Exception E) { the throw new new BuilderException ( "Error parsing the SQL Mapper Configuration Cause:. "+ And e);+ } }
Four Dian focuses on XMLConfigBuilder #mapperElement () parse mapper.xml, packed into mappedStatement
// Load Mapper // ============================================ = // total inlet mybatis Mapper is loaded // 1. Find the corresponding interface to add a configuration package Mapper, mapper.xml profile should in the same package // 2. by configuring resource = "mapper.xml" load path // ============================================= Private void mapperElement (XNode parent) throws Exception { // by mappers IF (parent =! null ) { for (XNode Child: parent.getChildren ()) { IF ( "Package" .equals (child.getName ())) { // . 1. You can specify a "package" level Child.getStringAttribute mapperPackage = String ( "name" ); // will load all classes package name, and use the annotation builder resolved to parse mapper.xml configuration file, and then parse mapper.class // only through specified xml configuration file to find, if the specified class, the corresponding profile and needs to be placed under a similar package, or the classpath path configuration.addMappers (mapperPackage); } the else { // single designated // Resource, specify the corresponding URL xml file, the class corresponding to the class specified 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."); } } } } }
Five Dian XMLMapperBuilder # parse ()
/ * * Main method, the parse * / public void the parse () { IF (! Configuration.isResourceLoaded (Resource)) { // load mapper.xml respective analytical elements, including parsing Statement, The resultMap // warning in the MapperProxy MapperMethod package calls, call parameters will be transferred to the appropriate type, the corresponding data bindings ConfigurationElement (parser.evalNode ( "/ Mapper" )); // set the parsed resource loaded configuration.addLoadedResource (resource); // loading parsing corresponding mapper interface mapper interface into registry bindMapperForNamespace (); } // load the analytical data have not yet been loaded 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)) { //May not know at The Real the Spring Resource name SO WE the SET A Flag // to Prevent loading Again from the this Resource at The Mapper interface // look AT MapperAnnotationBuilder # loadXmlResource configuration.addLoadedResource ( "namespace:" + namespace); // add is already in configuration resource loading configuration.addMapper (boundType); // add mapper interface configuration, then parse the added mapperRegistry } } } }
Six Dian will be added to the configuration of the mapper mapperRegistry, the period is resolved by 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 //Automatically BE the Binding May at The otherwise by at The attempted Displays // Mapper Parser. the If at The of the type already Known IS, IT by Will not the try. // use annotation builder to build, will first resolve mapper.xml configuration file, and then parse mapper.class annotation file MapperAnnotationBuilder Parser = new new MapperAnnotationBuilder (config, type); parser.parse (); LoadCompleted = to true ; } the finally { IF (! LoadCompleted) { knownMappers.remove (type); } } } }