暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

mybatis框架分析之实现自己的framework(中)

Alleria Windrunner 2020-01-10
185
在上篇中我们的mybatis框架撸完了解析全局配置文件中的数据源配置部分,本篇我们继续。
首先回顾一下我们定义的配置文件格式。mybatis的全局配置文件SqlMapConfig.xml格式如下:
    <configuration>
    <!-- mybatis 数据源环境配置 -->
    <environments default="dev">
    <environment id="dev">
    <!-- 配置数据源信息 -->
    <dataSource type="DBCP">
    <property name="driver" value="com.mysql.jdbc.Driver"></property>
    <property name="url"
    value="jdbc:mysql://47.106.242.17:3306/erp?allowMultiQueries=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
    </dataSource>
    </environment>
    </environments>


    <!-- 映射文件加载 -->
    <mappers>
    <!-- resource指定映射文件的类路径 -->
    <mapper resource="mapper/UserMapper.xml"></mapper>
    </mappers>
    </configuration>
    然后映射文件*Mapper.xml格式如下:
      <mapper namespace="test">
      <!-- select标签,封装了SQL语句信息、入参类型、结果映射类型 -->
      <select id="queryUserById" parameterType="java.lang.Integer"
      resultType="com.jth.mybatis.entity.UserDo" statementType="prepared">
      SELECT * FROM user WHERE id = #{id}
      </select>
      </mapper>
      接下来我们把XmlConfigParser.java中上篇未完成的parseMappers方法补充全,代码如下:
        /**
        * 解析映射文件标签
        * @param mappersElement
        */
        private void parseMappers(Element mappersElement) {
        List<Element> elements = mappersElement.elements(ConfigLabelType.MAPPER.getValue());
        for (Element envElement : elements) {
        String mapperPath = envElement.attributeValue("resource");
        parseMapper(mapperPath);
        }
        }


        /**
        * 解析映射文件标签
        * @param mapperPath
        */
        private void parseMapper(String mapperPath) {
        XmlMapperParser xmlMapperParser = new XmlMapperParser(configuration);
        InputStream mapperInputStream = ResourceUtil.getResourceAsStream(mapperPath);
        Document mapperDocument = DocumentUtil.createDocument(mapperInputStream);
        xmlMapperParser.parse(mapperDocument.getRootElement());
        }
        遍历Mappers中所有的mapper获取每一个mapper的Document文本对象,然后将解析mapper文本对象的工作交给XmlMapperParser.java类,实现如下:
          package com.eleven.config;


          import org.dom4j.Element;


          import java.util.List;


          /**
          * 映射配置文件解析类
          * @author eleven
          * @see
          * @since
          */


          public class XmlMapperParser<T> {


          private String namespace;


          private Configuration configuration;


          public XmlMapperParser(Configuration configuration) {
          this.configuration = configuration;
          }


          /**
          * 解析映射文件
          * @param mapperElement
          */
          public void parse(Element mapperElement) {
          namespace = mapperElement.attributeValue("namespace");
          List<Element> elementList = mapperElement.elements("select");
          for (Element element : elementList) {
          parseSelect(element, namespace);
          }
          }




          /**
          * 解析select标签
          * @param selectElement
          */
          private void parseSelect(Element selectElement, String namespace) {
          String id = selectElement.attributeValue("id");
          StringBuilder statementIdBuilder = new StringBuilder().append(namespace).append(".").append(id);
          String parameterTypeStr = selectElement.attributeValue("parameterType");
          Class<?> parameterType = resolveClazz(parameterTypeStr);
          String resultTypeStr = selectElement.attributeValue("resultType");
          Class<?> resultType = resolveClazz(resultTypeStr);
          String statementType = selectElement.attributeValue("statementType");
          String sqlText = selectElement.getText();
          MappedStatement mappedStatement = new MappedStatement(id, parameterType, resultType, statementType, new BoundSql(sqlText));
          configuration.setMappedStatements(statementIdBuilder.toString(), mappedStatement);
          }


          /**
          * 解析Class
          * @param clazzTypeStr
          * @return
          */
          private Class<?> resolveClazz(String clazzTypeStr) {
          try {
          return Class.forName(clazzTypeStr);
          } catch (ClassNotFoundException e) {
          e.printStackTrace();
          }
          return null;
          }
          }
          我们只对select标签的解析进行实现,其它类型的sql语句update|delete|insert可以对照着来。之前我们分析过sql语句相关的信息我们存储在MappedStatement对象中,所以我们还需要把MappedStatement类进行实现:
            package com.eleven.config;


            /**
            * 映射文件配置类
            * @author eleven
            * @see
            * @since
            */


            public class MappedStatement<T> {


            private String id;


            private Class<T> parameterType;


            private Class<T> resultType;


            private String statementType;


            private BoundSql boundSql;


            public MappedStatement(String id, Class<T> parameterType, Class<T> resultType, String statementType, BoundSql boundSql) {
            this.id = id;
            this.parameterType = parameterType;
            this.resultType = resultType;
            this.statementType = statementType;
            this.boundSql = boundSql;
            }


            public String getId() {
            return id;
            }


            public Class<T> getParameterType() {
            return parameterType;
            }


            public Class<T> getResultType() {
            return resultType;
            }


            public String getStatementType() {
            return statementType;
            }


            public BoundSql getBoundSql() {
            return boundSql;
            }


            public void setId(String id) {
            this.id = id;
            }


            public void setParameterType(Class<T> parameterType) {
            this.parameterType = parameterType;
            }


            public void setResultType(Class<T> resultType) {
            this.resultType = resultType;
            }


            public void setStatementType(String statementType) {
            this.statementType = statementType;
            }


            public void setBoundSql(BoundSql boundSql) {
            this.boundSql = boundSql;
            }
            }
            这里BoundSql代表真正可执行的sql语句,因为mapper中的sqlText中还包含了大量的标签之类的,所以这里需要抽象出一个单独的类,实现如下:
              package com.eleven.config;


              /**
              * BoundSql对象
              * @author eleven
              * @see
              * @since
              */


              public class BoundSql {


              /**
              * sql文本
              */
              private String sqlText;


              /**
              * 可执行sql
              */
              private String sql;


              public BoundSql(String sqlText) {
              this.sqlText = sqlText;
              }


              public String getSqlText() {
              return sqlText;
              }


              public String getSql() {
              return sql;
              }


              public void setSqlText(String sqlText) {
              this.sqlText = sqlText;
              }


              public void setSql(String sql) {
              this.sql = sql;
              }
              }
              好了,本篇就到此了,具体的可执行sql语句解析我们留到下篇去实现,剩下我们测试一下效果:

              另外我们可以看看官方的mybatis框架解析配置文件这部分的实现。要看框架源码,有两点诀窍需要注意:
              1. 找到入口

              2. 找到主线

              入口怎么找?很简单,就是到mybatis官方文档里面找quikstart的demo,入口就找到了,那我们开始吧:
                @Before
                public void init() throws Exception{
                // 加载全局配置文件(同时把映射文件也加载了)
                String resource = "phase01/SqlMapConfig.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                // sqlsessionFactory需要通过sqlsessionFactoryBuilder读取全局配置文件信息之后
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                }
                当你单独使用mybatis的时候是这样开始的?还记得么?所以入口就在SqlSessionFactoryBuilder().build(inputStream)这里。下图是mybatis框架的源码:

                接下来我们就沿着入口阅读mybatis解析配置文件的源码。首先是SqlSessionFactoryBuilder这个类:
                  /**
                  * Copyright ${license.git.copyrightYears} the original author or authors.
                  *
                  * Licensed under the Apache License, Version 2.0 (the "License");
                  * you may not use this file except in compliance with the License.
                  * You may obtain a copy of the License at
                  *
                  * http://www.apache.org/licenses/LICENSE-2.0
                  *
                  * Unless required by applicable law or agreed to in writing, software
                  * distributed under the License is distributed on an "AS IS" BASIS,
                  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                  * See the License for the specific language governing permissions and
                  * limitations under the License.
                  */
                  package org.apache.ibatis.session;


                  import java.io.IOException;
                  import java.io.InputStream;
                  import java.io.Reader;
                  import java.util.Properties;


                  import org.apache.ibatis.builder.xml.XMLConfigBuilder;
                  import org.apache.ibatis.exceptions.ExceptionFactory;
                  import org.apache.ibatis.executor.ErrorContext;
                  import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;


                  /**
                  * Builds {@link SqlSession} instances.
                  *
                  * @author Clinton Begin
                  */
                  public class SqlSessionFactoryBuilder {


                  public SqlSessionFactory build(Reader reader) {
                  return build(reader, null, null);
                  }


                  public SqlSessionFactory build(Reader reader, String environment) {
                  return build(reader, environment, null);
                  }


                  public SqlSessionFactory build(Reader reader, Properties properties) {
                  return build(reader, null, properties);
                  }


                  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
                  try {
                  XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
                  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.
                  }
                  }
                  }


                  public SqlSessionFactory build(InputStream inputStream) {
                  return build(inputStream, null, null);
                  }


                  public SqlSessionFactory build(InputStream inputStream, String environment) {
                  return build(inputStream, environment, null);
                  }


                  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
                  return build(inputStream, null, properties);
                  }


                  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
                  try {
                  // XMLConfigBuilder:用来解析XML配置文件
                  // 使用构建者模式
                  XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
                  // parser.parse():使用XPATH解析XML配置文件,将配置文件封装为Configuration对象
                  // 返回DefaultSqlSessionFactory对象,该对象拥有Configuration对象(封装配置文件信息)
                  return build(parser.parse());
                  } catch (Exception e) {
                  throw ExceptionFactory.wrapException("Error building SqlSession.", e);
                  } finally {
                  ErrorContext.instance().reset();
                  try {
                  inputStream.close();
                  } catch (IOException e) {
                  // Intentionally ignore. Prefer previous error.
                  }
                  }
                  }

                  public SqlSessionFactory build(Configuration config) {
                  // 创建SqlSessionFactory接口的默认实现类
                  return new DefaultSqlSessionFactory(config);
                  }


                  }
                  mybatis使用的是XPATH解析xml文件,它有一个XMLConfigBuilder的解析类,我们自己撸的框架有一个XmlConfigParser,差不多嘛。继续看看XMLConfigBuilder这个类:
                    /**
                    * Copyright ${license.git.copyrightYears} the original author or authors.
                    *
                    * Licensed under the Apache License, Version 2.0 (the "License");
                    * you may not use this file except in compliance with the License.
                    * You may obtain a copy of the License at
                    *
                    * http://www.apache.org/licenses/LICENSE-2.0
                    *
                    * Unless required by applicable law or agreed to in writing, software
                    * distributed under the License is distributed on an "AS IS" BASIS,
                    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                    * See the License for the specific language governing permissions and
                    * limitations under the License.
                    */
                    package org.apache.ibatis.builder.xml;


                    import java.io.InputStream;
                    import java.io.Reader;
                    import java.util.Properties;
                    import javax.sql.DataSource;


                    import org.apache.ibatis.builder.BaseBuilder;
                    import org.apache.ibatis.builder.BuilderException;
                    import org.apache.ibatis.datasource.DataSourceFactory;
                    import org.apache.ibatis.executor.ErrorContext;
                    import org.apache.ibatis.executor.loader.ProxyFactory;
                    import org.apache.ibatis.io.Resources;
                    import org.apache.ibatis.io.VFS;
                    import org.apache.ibatis.logging.Log;
                    import org.apache.ibatis.mapping.DatabaseIdProvider;
                    import org.apache.ibatis.mapping.Environment;
                    import org.apache.ibatis.parsing.XNode;
                    import org.apache.ibatis.parsing.XPathParser;
                    import org.apache.ibatis.plugin.Interceptor;
                    import org.apache.ibatis.reflection.DefaultReflectorFactory;
                    import org.apache.ibatis.reflection.MetaClass;
                    import org.apache.ibatis.reflection.ReflectorFactory;
                    import org.apache.ibatis.reflection.factory.ObjectFactory;
                    import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
                    import org.apache.ibatis.session.AutoMappingBehavior;
                    import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;
                    import org.apache.ibatis.session.Configuration;
                    import org.apache.ibatis.session.ExecutorType;
                    import org.apache.ibatis.session.LocalCacheScope;
                    import org.apache.ibatis.transaction.TransactionFactory;
                    import org.apache.ibatis.type.JdbcType;


                    /**
                    * @author Clinton Begin
                    * @author Kazuki Shimizu
                    */
                    public class XMLConfigBuilder extends BaseBuilder {


                    private boolean parsed;
                    private final XPathParser parser;
                    private String environment;
                    private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();


                    public XMLConfigBuilder(Reader reader) {
                    this(reader, null, null);
                    }


                    public XMLConfigBuilder(Reader reader, String environment) {
                    this(reader, environment, null);
                    }


                    public XMLConfigBuilder(Reader reader, String environment, Properties props) {
                    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
                    }


                    public XMLConfigBuilder(InputStream inputStream) {
                    this(inputStream, null, null);
                    }


                    public XMLConfigBuilder(InputStream inputStream, String environment) {
                    this(inputStream, environment, null);
                    }


                    public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
                    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
                    }


                    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
                    // 创建Configuration对象,并通过TypeAliasRegistry注册一些Mybatis内部相关类的别名
                    super(new Configuration());
                    ErrorContext.instance().resource("SQL Mapper Configuration");
                    this.configuration.setVariables(props);
                    this.parsed = false;
                    this.environment = environment;
                    this.parser = parser;
                    }


                    /**
                    * 解析XML配置文件
                    * @return
                    */
                    public Configuration parse() {
                    if (parsed) {
                    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
                    }
                    parsed = true;
                    // parser.evalNode("/configuration"):通过XPATH解析器,解析configuration根节点
                    // 从configuration根节点开始解析,最终将解析出的内容封装到Configuration对象中
                    parseConfiguration(parser.evalNode("/configuration"));
                    return configuration;
                    }


                    private void parseConfiguration(XNode root) {
                    try {
                    //issue #117 read properties first
                    // 解析</properties>标签
                    propertiesElement(root.evalNode("properties"));
                    // 解析</settings>标签
                    Properties settings = settingsAsProperties(root.evalNode("settings"));
                    loadCustomVfs(settings);
                    loadCustomLogImpl(settings);
                    // 解析</typeAliases>标签
                    typeAliasesElement(root.evalNode("typeAliases"));
                    // 解析</plugins>标签
                    pluginElement(root.evalNode("plugins"));
                    // 解析</objectFactory>标签
                    objectFactoryElement(root.evalNode("objectFactory"));
                    // 解析</objectWrapperFactory>标签
                    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
                    // 解析</reflectorFactory>标签
                    reflectorFactoryElement(root.evalNode("reflectorFactory"));
                    settingsElement(settings);

                    // read it after objectFactory and objectWrapperFactory issue #631
                    // 解析</environments>标签
                    environmentsElement(root.evalNode("environments"));
                    // 解析</databaseIdProvider>标签
                    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
                    // 解析</typeHandlers>标签
                    typeHandlerElement(root.evalNode("typeHandlers"));
                    // 解析</mappers>标签
                    mapperElement(root.evalNode("mappers"));
                    } catch (Exception e) {
                    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
                    }
                    }


                    private Properties settingsAsProperties(XNode context) {
                    if (context == null) {
                    return new Properties();
                    }
                    Properties props = context.getChildrenAsProperties();
                    // Check that all settings are known to the configuration class
                    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
                    for (Object key : props.keySet()) {
                    if (!metaConfig.hasSetter(String.valueOf(key))) {
                    throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
                    }
                    }
                    return props;
                    }


                    private void loadCustomVfs(Properties props) throws ClassNotFoundException {
                    String value = props.getProperty("vfsImpl");
                    if (value != null) {
                    String[] clazzes = value.split(",");
                    for (String clazz : clazzes) {
                    if (!clazz.isEmpty()) {
                    @SuppressWarnings("unchecked")
                    Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
                    configuration.setVfsImpl(vfsImpl);
                    }
                    }
                    }
                    }


                    private void loadCustomLogImpl(Properties props) {
                    Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
                    configuration.setLogImpl(logImpl);
                    }


                    private void typeAliasesElement(XNode parent) {
                    if (parent != null) {
                    for (XNode child : parent.getChildren()) {
                    if ("package".equals(child.getName())) {
                    String typeAliasPackage = child.getStringAttribute("name");
                    configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
                    } else {
                    String alias = child.getStringAttribute("alias");
                    String type = child.getStringAttribute("type");
                    try {
                    Class<?> clazz = Resources.classForName(type);
                    if (alias == null) {
                    typeAliasRegistry.registerAlias(clazz);
                    } else {
                    typeAliasRegistry.registerAlias(alias, clazz);
                    }
                    } catch (ClassNotFoundException e) {
                    throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
                    }
                    }
                    }
                    }
                    }


                    private void pluginElement(XNode parent) throws Exception {
                    if (parent != null) {
                    for (XNode child : parent.getChildren()) {
                    String interceptor = child.getStringAttribute("interceptor");
                    Properties properties = child.getChildrenAsProperties();
                    Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
                    interceptorInstance.setProperties(properties);
                    configuration.addInterceptor(interceptorInstance);
                    }
                    }
                    }


                    private void objectFactoryElement(XNode context) throws Exception {
                    if (context != null) {
                    String type = context.getStringAttribute("type");
                    Properties properties = context.getChildrenAsProperties();
                    ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
                    factory.setProperties(properties);
                    configuration.setObjectFactory(factory);
                    }
                    }


                    private void objectWrapperFactoryElement(XNode context) throws Exception {
                    if (context != null) {
                    String type = context.getStringAttribute("type");
                    ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
                    configuration.setObjectWrapperFactory(factory);
                    }
                    }


                    private void reflectorFactoryElement(XNode context) throws Exception {
                    if (context != null) {
                    String type = context.getStringAttribute("type");
                    ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();
                    configuration.setReflectorFactory(factory);
                    }
                    }


                    private void propertiesElement(XNode context) throws Exception {
                    if (context != null) {
                    // 获取子标签<property>标签的内容
                    Properties defaults = context.getChildrenAsProperties();
                    // 获取标签<properties>标签resource属性的值
                    String resource = context.getStringAttribute("resource");
                    // 获取标签<properties>标签url属性的值
                    String url = context.getStringAttribute("url");
                    if (resource != null && url != null) {
                    throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
                    }
                    // 如果resource或者url的值不为空,则加载对应的资源信息,然后合并到defaults中
                    if (resource != null) {
                    defaults.putAll(Resources.getResourceAsProperties(resource));
                    } else if (url != null) {
                    defaults.putAll(Resources.getUrlAsProperties(url));
                    }
                    Properties vars = configuration.getVariables();
                    if (vars != null) {
                    defaults.putAll(vars);
                    }
                    parser.setVariables(defaults);
                    configuration.setVariables(defaults);
                    }
                    }


                    private void settingsElement(Properties props) {
                    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
                    configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
                    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
                    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
                    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
                    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
                    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
                    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
                    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
                    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
                    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
                    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
                    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
                    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
                    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
                    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
                    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
                    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
                    configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
                    configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
                    configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
                    configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
                    configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
                    configuration.setLogPrefix(props.getProperty("logPrefix"));
                    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
                    }


                    private void environmentsElement(XNode context) throws Exception {
                    if (context != null) {
                    if (environment == null) {
                    environment = context.getStringAttribute("default");
                    }
                    for (XNode child : context.getChildren()) {
                    String id = child.getStringAttribute("id");
                    if (isSpecifiedEnvironment(id)) {
                    TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                    DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                    DataSource dataSource = dsFactory.getDataSource();
                    Environment.Builder environmentBuilder = new Environment.Builder(id)
                    .transactionFactory(txFactory)
                    .dataSource(dataSource);
                    configuration.setEnvironment(environmentBuilder.build());
                    }
                    }
                    }
                    }


                    private void databaseIdProviderElement(XNode context) throws Exception {
                    DatabaseIdProvider databaseIdProvider = null;
                    if (context != null) {
                    String type = context.getStringAttribute("type");
                    // awful patch to keep backward compatibility
                    if ("VENDOR".equals(type)) {
                    type = "DB_VENDOR";
                    }
                    Properties properties = context.getChildrenAsProperties();
                    databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
                    databaseIdProvider.setProperties(properties);
                    }
                    Environment environment = configuration.getEnvironment();
                    if (environment != null && databaseIdProvider != null) {
                    String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
                    configuration.setDatabaseId(databaseId);
                    }
                    }


                    private TransactionFactory transactionManagerElement(XNode context) throws Exception {
                    if (context != null) {
                    String type = context.getStringAttribute("type");
                    Properties props = context.getChildrenAsProperties();
                    TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
                    factory.setProperties(props);
                    return factory;
                    }
                    throw new BuilderException("Environment declaration requires a TransactionFactory.");
                    }


                    private DataSourceFactory dataSourceElement(XNode context) throws Exception {
                    if (context != null) {
                    String type = context.getStringAttribute("type");
                    Properties props = context.getChildrenAsProperties();
                    DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
                    factory.setProperties(props);
                    return factory;
                    }
                    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
                    }


                    private void typeHandlerElement(XNode parent) {
                    if (parent != null) {
                    for (XNode child : parent.getChildren()) {
                    if ("package".equals(child.getName())) {
                    String typeHandlerPackage = child.getStringAttribute("name");
                    typeHandlerRegistry.register(typeHandlerPackage);
                    } else {
                    String javaTypeName = child.getStringAttribute("javaType");
                    String jdbcTypeName = child.getStringAttribute("jdbcType");
                    String handlerTypeName = child.getStringAttribute("handler");
                    Class<?> javaTypeClass = resolveClass(javaTypeName);
                    JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
                    Class<?> typeHandlerClass = resolveClass(handlerTypeName);
                    if (javaTypeClass != null) {
                    if (jdbcType == null) {
                    typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
                    } else {
                    typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
                    }
                    } else {
                    typeHandlerRegistry.register(typeHandlerClass);
                    }
                    }
                    }
                    }
                    }


                    /**
                    * 解析<mappers>标签
                    * @param parent mappers标签对应的XNode对象
                    * @throws Exception
                    */
                    private void mapperElement(XNode parent) throws Exception {
                    if (parent != null) {
                    // 获取<mappers>标签的子标签
                    for (XNode child : parent.getChildren()) {
                    // <package>子标签
                    if ("package".equals(child.getName())) {
                    // 获取mapper接口和mapper映射文件对应的package包名
                    String mapperPackage = child.getStringAttribute("name");
                    // 将包下所有的mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
                    configuration.addMappers(mapperPackage);
                    } else {// <mapper>子标签
                    // 获取<mapper>子标签的resource属性
                    String resource = child.getStringAttribute("resource");
                    // 获取<mapper>子标签的url属性
                    String url = child.getStringAttribute("url");
                    // 获取<mapper>子标签的class属性
                    String mapperClass = child.getStringAttribute("class");
                    // 它们是互斥的
                    if (resource != null && url == null && mapperClass == null) {
                    ErrorContext.instance().resource(resource);
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    // 专门用来解析mapper映射文件
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                    // 通过XMLMapperBuilder解析mapper映射文件
                    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());
                    // 通过XMLMapperBuilder解析mapper映射文件
                    mapperParser.parse();
                    } else if (resource == null && url == null && mapperClass != null) {
                    Class<?> mapperInterface = Resources.classForName(mapperClass);
                    // 将指定mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
                    configuration.addMapper(mapperInterface);
                    } else {
                    throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                    }
                    }
                    }
                    }
                    }


                    private boolean isSpecifiedEnvironment(String id) {
                    if (environment == null) {
                    throw new BuilderException("No environment specified.");
                    } else if (id == null) {
                    throw new BuilderException("Environment requires an id attribute.");
                    } else if (environment.equals(id)) {
                    return true;
                    }
                    return false;
                    }


                    }
                    我们可以重点看看parseConfiguration()方法中的environmentsElement(root.evalNode("environments"))以及mapperElement(root.evalNode("mappers"));方法,因为我们自己撸的框架中也实现了这些部分。
                    文章转载自Alleria Windrunner,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                    评论