1. MyBatis 配置文件解析過程

配置文件解析過程

    @Test
    public void test0() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory =
                new SqlSessionFactoryBuilder().build(inputStream);
    }

獲取資源文件

在這裏插入圖片描述

其中getClassLoaders 獲取對應的類加載器

ClassLoader[] getClassLoaders(ClassLoader classLoader) {
    return new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        systemClassLoader};
  }

SqlSessionFactoryBuilder#build

  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
 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) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

以上主要分爲:

  1. 構造XMLConfigBuilder
  2. 調用 parse 方法解析配置文件,生成 Configuration 對象

XMLConfigBuilder

  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 {
      //issue #117 read properties first
      //1. 解析properties
      propertiesElement(root.evalNode("properties"));
      //2. 解析settings
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      //3. 加載 vfs
      loadCustomVfs(settings);
      //4. 解析 typeAliases 配置
      typeAliasesElement(root.evalNode("typeAliases"));
      //5. 解析 plugins 配置
      pluginElement(root.evalNode("plugins"));
      //6. 解析 objectFactory 配置
      objectFactoryElement(root.evalNode("objectFactory"));
      //7. 解析 objectWrapperFactory 配置
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //8. 解析 reflectorFactory 配置
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      //9. settings 中的信息設置到 Configuration 對象中
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      //10. 解析 environments 配置
      environmentsElement(root.evalNode("environments"));
      //11. 解析 databaseIdProvider,獲取並設置 databaseId 到 Configuration 對象
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //12. 解析 typeHandlers 配置
      typeHandlerElement(root.evalNode("typeHandlers"));
      //13. 解析 mappers 配置
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

propertiesElement(root.evalNode(“properties”))

properties的配置DTD

<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>
<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>

根據DTD我們可以看出解析properties的方法也比較簡單

  private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      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.");
      }
      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);
    }
  }

settingsAsProperties(XNode context)

setting的配置文件格式如下:

<settings>
        <setting name="defaultStatementTimeout" value="10"/>
    </settings>

DTD文件如下:

<!ELEMENT settings (setting+)>

<!ELEMENT setting EMPTY>
<!ATTLIST setting
name CDATA #REQUIRED
value CDATA #REQUIRED
>

具體的解析代碼:

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;
  }

如上最核心的方法爲:

MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
  1. MetaClass對象的構建
public class MetaClass {

  private final ReflectorFactory reflectorFactory;
  private final Reflector reflector;

  private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
    this.reflectorFactory = reflectorFactory;
    this.reflector = reflectorFactory.findForClass(type);
  }

  public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
    return new MetaClass(type, reflectorFactory);
  }

  public MetaClass metaClassForProperty(String name) {
    Class<?> propType = reflector.getGetterType(name);
    return MetaClass.forClass(propType, reflectorFactory);
  }

如下圖爲MetaClass的核心方法
在這裏插入圖片描述

我們通過方法可以看到,MetaClass主要輔助於反射,判斷當前類的各種反射屬性。

MetaClass主要引入以下類:

  1. ReflectorFactory:顧名思義,Reflector的工廠類,兼有緩存Reflector對象的功能
  2. Reflector:反射器,用於解析和存儲目標類中的元信息
  3. PropertyTokenizer:屬性名分詞器,用於處理較爲複雜的屬性名

1.1 DefaultReflectorFactory


public class DefaultReflectorFactory implements ReflectorFactory {
  private boolean classCacheEnabled = true;
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();

  public DefaultReflectorFactory() {
  }

  @Override
  public boolean isClassCacheEnabled() {
    return classCacheEnabled;
  }

  @Override
  public void setClassCacheEnabled(boolean classCacheEnabled) {
    this.classCacheEnabled = classCacheEnabled;
  }

  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
            // synchronized (type) removed see issue #461
      Reflector cached = reflectorMap.get(type);
      if (cached == null) {
        cached = new Reflector(type);
        reflectorMap.put(type, cached);
      }
      return cached;
    } else {
      return new Reflector(type);
    }
  }

}

其中最核心的方法爲 DefaultReflectorFactory#findForClass,主要可以看出來該類會緩存對應的Reflector對象

1.2 Reflector

public class Reflector {

  private final Class<?> type;
  private final String[] readablePropertyNames;
  private final String[] writeablePropertyNames;
  private final Map<String, Invoker> setMethods = new HashMap<String, Invoker>();
  private final Map<String, Invoker> getMethods = new HashMap<String, Invoker>();
  private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
  private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
  private Constructor<?> defaultConstructor;

  1. PropertyTokenizer
    PropertyTokenizer 主要是輔助對各種Property的方法解析

解析typeAliasesElement

 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);
          }
        }
      }
    }
  }

registerAlias方法只是將我們解析到的type alias做下JVM緩存

  public void registerAlias(Class<?> type) {
    String alias = type.getSimpleName();
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    } 
    registerAlias(alias, type);
  }

  public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }

environmentsElement(root.evalNode(“environments”));


  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());
        }
      }
    }
  }

主要初始化TransactionFactory和DataSourceFactory

解析節點

數據庫字段類型和 Java 類型進行一個轉換,主要使用typeHandlers來完成

private void typeHandlerElement(XNode parent) throws Exception {
    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);
          }
        }
      }
    }
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章