博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis技术内幕:初始化之加载 mybatis-config
阅读量:6277 次
发布时间:2019-06-22

本文共 37766 字,大约阅读时间需要 125 分钟。

概述

在 MyBatis 初始化过程中,会加载mybatis-config.xml配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到Configuration 对象中。例如:

  • resultMap节点(即 ResultSet 的映射规则) 会被解析成 ResultMap 对象
  • result 节点(即属性映射)会被解析成 ResultMapping 对象。之后,利用该 Configuration 对象创建SqlSessionFactory对象。待MyBatis初始化之后,开发人员可以通过初始化得到 SqlSessionFactory 创建 SqlSession对象并完成数据库操作。
  • 对应 builder 模块,为配置解析过程
  • 对应 mapping 模块,主要为 SQL 操作解析后的映射。

因为整个 MyBatis 的初始化流程涉及代码颇多,所以拆分成三篇文章:

  • 加载 mybatis-config.xml 配置文件。
  • 加载 Mapper 映射配置文件。
  • 加载 Mapper 接口中的注解信息。

本文就主要分享第一部分「加载 mybatis-config.xml 配置文件」。

1.入口

MyBatis 的初始化流程的入口是 SqlSessionFactoryBuilder#build(Reader reader, String environment, Properties properties) 方法,代码如下:

// SqlSessionFactoryBuilder 中,build 方法有多种重载方式。这里就选取一个。// SqlSessionFactoryBuilder.java/** * 构造 SqlSessionFactory 对象 * * @param reader Reader 对象 * @param environment 环境 * @param properties Properties 变量 * @return SqlSessionFactory 对象 */public SqlSessionFactory build(Reader reader, String environment, Properties properties) {    try {        // <1> 创建 XMLConfigBuilder 对象        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);        // <2> 执行 XML 解析        // <3> 创建 DefaultSqlSessionFactory 对象        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.        }    }}复制代码
  • <1> 处,创建 XMLConfigBuilder 对象。
  • <2> 处,调用 XMLConfigBuilder#parse()方法,执行XML解析,返回Configuration 对象。
  • <3> 处,创建 DefaultSqlSessionFactory 对象。

本文的重点是 <1><2> 处,即 XMLConfigBuilder 类。

2.BaseBuilder

org.apache.ibatis.builder.BaseBuilder基础构造器抽象类,为子类提供通用的工具类。

为什么不直接讲 XMLConfigBuilder ,而是先讲BaseBuilder呢?因为,BaseBuilder 是 XMLConfigBuilder 的父类,并且它还有其他的子类。如下图所示:

2.1 构造方法

// BaseBuilder.java/** * MyBatis Configuration 对象 */protected final Configuration configuration;protected final TypeAliasRegistry typeAliasRegistry;protected final TypeHandlerRegistry typeHandlerRegistry;public BaseBuilder(Configuration configuration) {    this.configuration = configuration;    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();}复制代码

configuration 属性,MyBatis-Configuration对象。XML和注解中解析到的配置,最终都会设置到 org.apache.ibatis.session.Configuration

2.2 parseExpression

#parseExpression(String regex,String defaultValue)方法,创建正则表达式。代码如下:

// BaseBuilder.java/** * 创建正则表达式 * * @param regex 指定表达式 * @param defaultValue 默认表达式 * @return 正则表达式 */@SuppressWarnings("SameParameterValue")protected Pattern parseExpression(String regex, String defaultValue) {    return Pattern.compile(regex == null ? defaultValue : regex);}复制代码

2.3 xxxValueOf

#xxxValueOf(...) 方法,将字符串转换成对应的数据类型的值。代码如下:

// BaseBuilder.javaprotected Boolean booleanValueOf(String value, Boolean defaultValue) {    return value == null ? defaultValue : Boolean.valueOf(value);}protected Integer integerValueOf(String value, Integer defaultValue) {    return value == null ? defaultValue : Integer.valueOf(value);}protected Set
stringSetValueOf(String value, String defaultValue) { value = (value == null ? defaultValue : value); return new HashSet<>(Arrays.asList(value.split(",")));}复制代码

2.4 resolveJdbcType

#resolveJdbcType(String alias) 方法,解析对应的 JdbcType类型。代码如下:

// BaseBuilder.javaprotected JdbcType resolveJdbcType(String alias) {    if (alias == null) {        return null;    }    try {        return JdbcType.valueOf(alias);    } catch (IllegalArgumentException e) {        throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);    }}复制代码

2.5 resolveResultSetType

#resolveResultSetType(String alias) 方法,解析对应的 ResultSetType 类型。代码如下:

// BaseBuilder.javaprotected ResultSetType resolveResultSetType(String alias) {    if (alias == null) {        return null;    }    try {        return ResultSetType.valueOf(alias);    } catch (IllegalArgumentException e) {        throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e);    }}复制代码

2.6 resolveParameterMode

#resolveParameterMode(String alias) 方法,解析对应的 ParameterMode 类型。代码如下:

// BaseBuilder.javaprotected ParameterMode resolveParameterMode(String alias) {    if (alias == null) {        return null;    }    try {        return ParameterMode.valueOf(alias);    } catch (IllegalArgumentException e) {        throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e);    }}复制代码

2.7 createInstance

#createInstance(String alias) 方法,创建指定对象。代码如下:

// BaseBuilder.javaprotected Object createInstance(String alias) {    // <1> 获得对应的类型    Class
clazz = resolveClass(alias); if (clazz == null) { return null; } try { // <2> 创建对象 return resolveClass(alias).newInstance(); // 这里重复获得了一次 } catch (Exception e) { throw new BuilderException("Error creating instance. Cause: " + e, e); }}复制代码

<1> 处,调用#resolveClass(String alias)方法,获得对应的类型。代码如下:

// BaseBuilder.javaprotected 
Class
resolveClass(String alias) { if (alias == null) { return null; } try { return resolveAlias(alias); } catch (Exception e) { throw new BuilderException("Error resolving class. Cause: " + e, e); }}protected
Class
resolveAlias(String alias) { return typeAliasRegistry.resolveAlias(alias);}复制代码

从 typeAliasRegistry 中,通过别名或类全名,获得对应的类

<2> 处,创建对象。

2.8 resolveTypeHandler

#resolveTypeHandler(Class<?> javaType, String typeHandlerAlias)方法,从 typeHandlerRegistry 中获得或创建对应的 TypeHandler 对象。代码如下:

// BaseBuilder.javaprotected TypeHandler
resolveTypeHandler(Class
javaType, Class
> typeHandlerType) { if (typeHandlerType == null) { return null; } // javaType ignored for injected handlers see issue #746 for full detail // 先获得 TypeHandler 对象 TypeHandler
handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType); if (handler == null) { // 如果不存在,进行创建 TypeHandler 对象 // not in registry, create a new one handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType); } return handler;}复制代码

3. XMLConfigBuilder

org.apache.ibatis.builder.xml.XMLConfigBuilder继承BaseBuilder抽象类,XML 配置构建器,主要负责解析mybatis-config.xml配置文件。即对应

3.1 构造方法

// XMLConfigBuilder.java/** * 是否已解析 */private boolean parsed;/** * 基于 Java XPath 解析器 */private final XPathParser parser;/** * 环境 */private String environment;/** * ReflectorFactory 对象 */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) {    // <1> 创建 Configuration 对象    super(new Configuration());    ErrorContext.instance().resource("SQL Mapper Configuration");    // <2> 设置 Configuration 的 variables 属性    this.configuration.setVariables(props);    this.parsed = false;    this.environment = environment;    this.parser = parser;}复制代码
  • parser 属性,XPathParser 对象。在 《精尽 MyBatis 源码分析 —— 解析器模块》 中,已经详细解析。
  • localReflectorFactory 属性,DefaultReflectorFactory 对象。在 《精尽 MyBatis 源码分析 —— 反射模块》 中,已经详细解析。
    构造方法重载了比较多,只需要看最后一个。

<1> 处,创建 Configuration 对象

<2> 处,设置 Configuration 对象的 variables 属性。代码如下:

// Configuration.java/** * 变量 Properties 对象。 * * 参见 {
@link org.apache.ibatis.builder.xml.XMLConfigBuilder#propertiesElement(XNode context)} 方法 */protected Properties variables = new Properties();public void setVariables(Properties variables) { this.variables = variables;}复制代码

3.2 parse

#parse() 方法,解析 XML 成 Configuration 对象。代码如下:

// XMLConfigBuilder.javapublic Configuration parse() {    // <1.1> 若已解析,抛出 BuilderException 异常    if (parsed) {        throw new BuilderException("Each XMLConfigBuilder can only be used once.");    }    // <1.2> 标记已解析    parsed = true;    // <2> 解析 XML configuration 节点    parseConfiguration(parser.evalNode("/configuration"));    return configuration;}复制代码

<1.1> 处,若已解析,抛出 BuilderException 异常

<1.2> 处,标记已解析
<2> 处,调用 XPathParser#evalNode(String expression)方法,获得XML<configuration /> 节点,后调用 #parseConfiguration(XNode root) 方法,解析该节点。详细解析,见 「3.3 parseConfiguration」

3.3 parseConfiguration

#parseConfiguration(XNode root) 方法,解析 <configuration /> 节点。代码如下:

// XMLConfigBuilder.javaprivate void parseConfiguration(XNode root) {    try {        //issue #117 read properties first        // <1> 解析 
标签 propertiesElement(root.evalNode("properties")); // <2> 解析
标签 Properties settings = settingsAsProperties(root.evalNode("settings")); // <3> 加载自定义 VFS 实现类 loadCustomVfs(settings); // <4> 解析
标签 typeAliasesElement(root.evalNode("typeAliases")); // <5> 解析
标签 pluginElement(root.evalNode("plugins")); // <6> 解析
标签 objectFactoryElement(root.evalNode("objectFactory")); // <7> 解析
标签 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // <8> 解析
标签 reflectorFactoryElement(root.evalNode("reflectorFactory")); // <9> 赋值
到 Configuration 属性 settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // <10> 解析
标签 environmentsElement(root.evalNode("environments")); // <11> 解析
标签 databaseIdProviderElement(root.evalNode("databaseIdProvider")); // <12> 解析
标签 typeHandlerElement(root.evalNode("typeHandlers")); // <13> 解析
标签 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); }}复制代码

<1> 处,调用 #propertiesElement(XNode context) 方法,解析 <properties /> 节点。详细解析,见 「3.3.1 propertiesElement」.

<2> 处,调用 #settingsAsProperties(XNode context) 方法,解析 <settings /> 节点。详细解析,见 「3.3.2 settingsAsProperties」.
<3> 处,调用 #loadCustomVfs(Properties settings) 方法,加载自定义 VFS 实现类。详细解析,见 「3.3.3 loadCustomVfs」.
<4> 处,调用 #typeAliasesElement(XNode parent) 方法,解析 <typeAliases /> 节点。详细解析,见 「3.3.4 typeAliasesElement」.
<5> 处,调用 #typeAliasesElement(XNode parent)方法,解析 <plugins /> 节点。详细解析,见 「3.3.5 pluginElement」.
<6> 处,调用 #objectFactoryElement(XNode parent) 方法,解析 <objectFactory /> 节点。详细解析,见 「3.3.6 pluginElement」.
<7> 处,调用 #objectWrapperFactoryElement(XNode parent) 方法,解析 <objectWrapperFactory /> 节点。详细解析,见 「3.3.7 objectWrapperFactoryElement」.
<8> 处,调用 #reflectorFactoryElement(XNode parent) 方法,解析 <reflectorFactory /> 节点。详细解析,见 「3.3.8 reflectorFactoryElement」
<9> 处,调用 #settingsElement(Properties props) 方法,赋值 <settings /> 到 Configuration 属性。详细解析,见 「3.3.9 settingsElement」
<10> 处,调用 #environmentsElement(XNode context) 方法,解析 <environments /> 标签。详细解析,见 「3.3.10 environmentsElement」
<11> 处,调用 #databaseIdProviderElement(XNode context) 方法,解析 <databaseIdProvider /> 标签。详细解析,见 「3.3.11 databaseIdProviderElement」
<12> 处,调用 #typeHandlerElement(XNode context) 方法,解析 <typeHandlers /> 标签。详细解析,见 「3.3.12 typeHandlerElement」
<13> 处,调用 #mapperElement(XNode context) 方法,解析 <mappers /> 标签。详细解析,见 「3.3.13 mapperElement」

3.3.1 propertiesElement

#propertiesElement(XNode context) 方法,解析 <properties /> 节点。大体逻辑如下:

解析 <properties />标签,成Properties对象。覆盖configuration中的 Properties 对象到上面的结果。设置结果到 parserconfiguration 中。代码如下:

// XMLConfigBuilder.javaprivate void propertiesElement(XNode context) throws Exception {    if (context != null) {        // 读取子标签们,为 Properties 对象        Properties defaults = context.getChildrenAsProperties();        // 读取 resource 和 url 属性        String resource = context.getStringAttribute("resource");        String url = context.getStringAttribute("url");        if (resource != null && url != null) { // resource 和 url 都存在的情况下,抛出 BuilderException 异常            throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");        }        // 读取本地 Properties 配置文件到 defaults 中。        if (resource != null) {            defaults.putAll(Resources.getResourceAsProperties(resource));            // 读取远程 Properties 配置文件到 defaults 中。        } else if (url != null) {            defaults.putAll(Resources.getUrlAsProperties(url));        }        // 覆盖 configuration 中的 Properties 对象到 defaults 中。        Properties vars = configuration.getVariables();        if (vars != null) {            defaults.putAll(vars);        }        // 设置 defaults 到 parser 和 configuration 中。        parser.setVariables(defaults);        configuration.setVariables(defaults);    }}复制代码

3.3.2 settingsAsProperties

#settingsElement(Properties props) 方法,将 <setting />标签解析为 Properties 对象。代码如下:

// XMLConfigBuilder.javaprivate Properties settingsAsProperties(XNode context) {    // 将子标签,解析成 Properties 对象    if (context == null) {        return new Properties();    }    Properties props = context.getChildrenAsProperties();    // Check that all settings are known to the configuration class    // 校验每个属性,在 Configuration 中,有相应的 setting 方法,否则抛出 BuilderException 异常    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;}复制代码

3.3.3 loadCustomVfs

#loadCustomVfs(Properties settings)方法,加载自定义VFS实现类。代码如下:

// XMLConfigBuilder.javaprivate void loadCustomVfs(Properties props) throws ClassNotFoundException {    // 获得 vfsImpl 属性    String value = props.getProperty("vfsImpl");    if (value != null) {        // 使用 , 作为分隔符,拆成 VFS 类名的数组        String[] clazzes = value.split(",");        // 遍历 VFS 类名的数组        for (String clazz : clazzes) {            if (!clazz.isEmpty()) {                // 获得 VFS 类                @SuppressWarnings("unchecked")                Class
vfsImpl = (Class
) Resources.classForName(clazz); // 设置到 Configuration 中 configuration.setVfsImpl(vfsImpl); } } }}// Configuration.java/** * VFS 实现类 */protected Class
vfsImpl;public void setVfsImpl(Class
vfsImpl) { if (vfsImpl != null) { // 设置 vfsImpl 属性 this.vfsImpl = vfsImpl; // 添加到 VFS 中的自定义 VFS 类的集合 VFS.addImplClass(this.vfsImpl); }}复制代码

3.3.4 typeAliasesElement

#typeAliasesElement(XNode parent) 方法,解析 <typeAliases /> 标签,将配置类注册到 typeAliasRegistry 中。代码如下:

// XMLConfigBuilder.javaprivate 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); // 获得类是否存在 // 注册到 typeAliasRegistry 中 if (alias == null) { typeAliasRegistry.registerAlias(clazz); } else { typeAliasRegistry.registerAlias(alias, clazz); } } catch (ClassNotFoundException e) { // 若类不存在,则抛出 BuilderException 异常 throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); } } } }}复制代码

3.3.5 pluginElement

#pluginElement(XNode parent) 方法,解析 <plugins /> 标签,添加到 Configuration#interceptorChain 中。代码如下:

// XMLConfigBuilder.javaprivate void pluginElement(XNode parent) throws Exception {    if (parent != null) {        // 遍历 
标签 for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); Properties properties = child.getChildrenAsProperties(); // <1> 创建 Interceptor 对象,并设置属性 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); interceptorInstance.setProperties(properties); // <2> 添加到 configuration 中 configuration.addInterceptor(interceptorInstance); } }}复制代码

<1> 处,创建 Interceptor 对象,并设置属性。关于 Interceptor 类,后续文章,详细解析。

<2> 处,调用 Configuration#addInterceptor(Interceptor interceptor) 方法,添加到 configuration 中。代码如下:

// Configuration.java/** * 拦截器链 */protected final InterceptorChain interceptorChain = new InterceptorChain();public void addInterceptor(Interceptor interceptor) {    interceptorChain.addInterceptor(interceptor);}复制代码
  • 关于 InterceptorChain 类,后续文章,详细解析。

3.3.6 objectFactoryElement

#objectFactoryElement(XNode parent) 方法,解析 <objectFactory /> 节点。代码如下:

// XMLConfigBuilder.javaprivate void objectFactoryElement(XNode context) throws Exception {    if (context != null) {        // 获得 ObjectFactory 的实现类        String type = context.getStringAttribute("type");        // 获得 Properties 属性        Properties properties = context.getChildrenAsProperties();        // <1> 创建 ObjectFactory 对象,并设置 Properties 属性        ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();        factory.setProperties(properties);        // <2> 设置 Configuration 的 objectFactory 属性        configuration.setObjectFactory(factory);    }}复制代码

<1> 处,创建 ObjectFactory 对象,并设置 Properties 属性。

<2> 处,调用 Configuration#setObjectFactory(ObjectFactory objectFactory) 方法,设置 ConfigurationobjectFactory 属性。代码如下:

// Configuration.java/** * ObjectFactory 对象 */protected ObjectFactory objectFactory = new DefaultObjectFactory();public void setObjectFactory(ObjectFactory objectFactory) {    this.objectFactory = objectFactory;}复制代码

3.3.7 objectWrapperFactoryElement

#objectWrapperFactoryElement(XNode context) 方法,解析 <objectWrapperFactory /> 节点。代码如下:

// XMLConfigBuilder.javaprivate void objectWrapperFactoryElement(XNode context) throws Exception {    if (context != null) {        // 获得 ObjectFactory 的实现类        String type = context.getStringAttribute("type");        // <1> 创建 ObjectWrapperFactory 对象        ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();        // 设置 Configuration 的 objectWrapperFactory 属性        configuration.setObjectWrapperFactory(factory);    }}复制代码

<1> 处,创建 ObjectWrapperFactory 对象。

<2> 处,调用 Configuration#setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) 方法,设置 Configuration 的 objectWrapperFactory 属性。代码如下:

// Configuration.java/** * ObjectWrapperFactory 对象 */protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {    this.objectWrapperFactory = objectWrapperFactory;}复制代码

3.3.8 reflectorFactoryElement

#reflectorFactoryElement(XNode parent) 方法,解析 <reflectorFactory /> 节点。代码如下:

// XMLConfigBuilder.javaprivate void reflectorFactoryElement(XNode context) throws Exception {    if (context != null) {        // 获得 ReflectorFactory 的实现类        String type = context.getStringAttribute("type");        // 创建 ReflectorFactory 对象        ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();        // 设置 Configuration 的 reflectorFactory 属性        configuration.setReflectorFactory(factory);    }}复制代码

<1> 处,创建 ReflectorFactory 对象。 <2> 处,调用 Configuration#setReflectorFactory(ReflectorFactory reflectorFactory) 方法,设置 Configuration 的 reflectorFactory 属性。代码如下:

// Configuration.java/** * ReflectorFactory 对象 */protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();public void setReflectorFactory(ReflectorFactory reflectorFactory) {    this.reflectorFactory = reflectorFactory;}复制代码

3.3.9 settingsElement

#settingsElement(Properties props) 方法,赋值 <settings /> 到 Configuration 属性。代码如下:

// XMLConfigBuilder.javaprivate void settingsElement(Properties props) throws Exception {    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")));    @SuppressWarnings("unchecked")    Class
typeHandler = resolveClass(props.getProperty("defaultEnumTypeHandler")); configuration.setDefaultEnumTypeHandler(typeHandler); 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")); @SuppressWarnings("unchecked") Class
logImpl = resolveClass(props.getProperty("logImpl")); configuration.setLogImpl(logImpl); configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));}复制代码

属性比较多,瞟一眼就行。

3.3.10 environmentsElement

#environmentsElement(XNode context) 方法,解析 <environments /> 标签。代码如下:

// XMLConfigBuilder.javaprivate void environmentsElement(XNode context) throws Exception {    if (context != null) {        // <1> environment 属性非空,从 default 属性获得        if (environment == null) {            environment = context.getStringAttribute("default");        }        // 遍历 XNode 节点        for (XNode child : context.getChildren()) {            // <2> 判断 environment 是否匹配            String id = child.getStringAttribute("id");            if (isSpecifiedEnvironment(id)) {                // <3> 解析 `
` 标签,返回 TransactionFactory 对象 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); // <4> 解析 `
` 标签,返回 DataSourceFactory 对象 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); // <5> 创建 Environment.Builder 对象 Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); // <6> 构造 Environment 对象,并设置到 configuration 中 configuration.setEnvironment(environmentBuilder.build()); } } }}复制代码

<1> 处,若 environment 属性非空,从 default 属性种获得 environment 属性。

<2> 处,遍历 XNode 节点,获得其 id 属性,后调用 #isSpecifiedEnvironment(String id) 方法,判断 environment 和 id 是否匹配。代码如下:

// XMLConfigBuilder.javaprivate 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;}复制代码

<3> 处,调用 #transactionManagerElement(XNode context) 方法,解析 <transactionManager /> 标签,返回 TransactionFactory 对象。代码如下:

// XMLConfigBuilder.javaprivate TransactionFactory transactionManagerElement(XNode context) throws Exception {    if (context != null) {        // 获得 TransactionFactory 的类        String type = context.getStringAttribute("type");        // 获得 Properties 属性        Properties props = context.getChildrenAsProperties();        // 创建 TransactionFactory 对象,并设置属性        TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();        factory.setProperties(props);        return factory;    }    throw new BuilderException("Environment declaration requires a TransactionFactory.");}复制代码

<4> 处,调用 #dataSourceElement(XNode context) 方法,解析 <dataSource /> 标签,返回 DataSourceFactory 对象,而后返回 DataSource 对象。代码如下:

// XMLConfigBuilder.javaprivate DataSourceFactory dataSourceElement(XNode context) throws Exception {    if (context != null) {        // 获得 DataSourceFactory 的类        String type = context.getStringAttribute("type");        // 获得 Properties 属性        Properties props = context.getChildrenAsProperties();        // 创建 DataSourceFactory 对象,并设置属性        DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();        factory.setProperties(props);        return factory;    }    throw new BuilderException("Environment declaration requires a DataSourceFactory.");}复制代码

<5> 处,创建 Environment.Builder 对象。

<6> 处,构造 Environment 对象,并设置到 configuration 中。代码如下:

// Configuration.java/** * DB Environment 对象 */protected Environment environment;public void setEnvironment(Environment environment) {    this.environment = environment;}复制代码

3.3.10.1 Environment

org.apache.ibatis.mapping.EnvironmentDB 环境。代码如下:

// Environment.javapublic final class Environment {    /**     * 环境变好     */    private final String id;    /**     * TransactionFactory 对象     */    private final TransactionFactory transactionFactory;    /**     * DataSource 对象     */    private final DataSource dataSource;    public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {        if (id == null) {            throw new IllegalArgumentException("Parameter 'id' must not be null");        }        if (transactionFactory == null) {            throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");        }        this.id = id;        if (dataSource == null) {            throw new IllegalArgumentException("Parameter 'dataSource' must not be null");        }        this.transactionFactory = transactionFactory;        this.dataSource = dataSource;    }    /**     * 构造器     */    public static class Builder {        /**         * 环境变好         */        private String id;        /**         * TransactionFactory 对象         */        private TransactionFactory transactionFactory;        /**         * DataSource 对象         */        private DataSource dataSource;        public Builder(String id) {            this.id = id;        }        public Builder transactionFactory(TransactionFactory transactionFactory) {            this.transactionFactory = transactionFactory;            return this;        }        public Builder dataSource(DataSource dataSource) {            this.dataSource = dataSource;            return this;        }        public String id() {            return this.id;        }        public Environment build() {            return new Environment(this.id, this.transactionFactory, this.dataSource);        }    }    public String getId() {        return this.id;    }    public TransactionFactory getTransactionFactory() {        return this.transactionFactory;    }    public DataSource getDataSource() {        return this.dataSource;    }}复制代码

3.3.11 databaseIdProviderElement

#databaseIdProviderElement(XNode context) 方法,解析 <databaseIdProvider /> 标签。代码如下:

// XMLConfigBuilder.javaprivate void databaseIdProviderElement(XNode context) throws Exception {    DatabaseIdProvider databaseIdProvider = null;    if (context != null) {        // <1> 获得 DatabaseIdProvider 的类        String type = context.getStringAttribute("type");        // awful patch to keep backward compatibility 保持兼容        if ("VENDOR".equals(type)) {            type = "DB_VENDOR";        }        // <2> 获得 Properties 对象        Properties properties = context.getChildrenAsProperties();        // <3> 创建 DatabaseIdProvider 对象,并设置对应的属性        databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();        databaseIdProvider.setProperties(properties);    }    Environment environment = configuration.getEnvironment();    if (environment != null && databaseIdProvider != null) {        // <4> 获得对应的 databaseId 编号        String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());        // <5> 设置到 configuration 中        configuration.setDatabaseId(databaseId);    }}复制代码

不了解的胖友,可以先看看 《MyBatis 文档 —— XML 映射配置文件 —— databaseIdProvider 数据库厂商标识》 。 <1> 处,获得 DatabaseIdProvider 的类。 <2> 处,获得 Properties 对象。 <3> 处,创建 DatabaseIdProvider 对象,并设置对应的属性。 <4> 处,调用 DatabaseIdProvider#getDatabaseId(DataSource dataSource) 方法,获得对应的 databaseId 标识。 <5> 处,设置到 configuration 中。代码如下:

// Configuration.java/** * 数据库标识 */protected String databaseId;public void setDatabaseId(String databaseId) {    this.databaseId = databaseId;}复制代码

3.3.11.1 DatabaseIdProvider

org.apache.ibatis.mapping.DatabaseIdProvider数据库标识提供器接口。代码如下:

public interface DatabaseIdProvider {    /**     * 设置属性     *     * @param p Properties 对象     */    void setProperties(Properties p);    /**     * 获得数据库标识     *     * @param dataSource 数据源     * @return 数据库标识     * @throws SQLException 当 DB 发生异常时     */    String getDatabaseId(DataSource dataSource) throws SQLException;}复制代码

3.3.11.2 VendorDatabaseIdProvider

org.apache.ibatis.mapping.VendorDatabaseIdProvider实现 DatabaseIdProvider 接口,供应商数据库标识提供器实现类。

① 构造方法// VendorDatabaseIdProvider.java/** * Properties 对象 */private Properties properties;@Overridepublic String getDatabaseId(DataSource dataSource) {    if (dataSource == null) {        throw new NullPointerException("dataSource cannot be null");    }    try {        return getDatabaseName(dataSource);    } catch (Exception e) {        log.error("Could not get a databaseId from dataSource", e);    }    return null;}@Overridepublic void setProperties(Properties p) {    this.properties = p;}复制代码

② 获得数据库标识

#getDatabaseId(DataSource dataSource) 方法,代码如下:

// VendorDatabaseIdProvider.java

@Override public String getDatabaseId(DataSource dataSource) { if (dataSource == null) { throw new NullPointerException("dataSource cannot be null"); } try { // 获得数据库标识 return getDatabaseName(dataSource); } catch (Exception e) { log.error("Could not get a databaseId from dataSource", e); } return null; }

private String getDatabaseName(DataSource dataSource) throws SQLException { // <1> 获得数据库产品名 String productName = getDatabaseProductName(dataSource); if (this.properties != null) { for (Map.Entry<Object, Object> property : properties.entrySet()) { // 如果产品名包含 KEY ,则返回对应的 VALUE if (productName.contains((String) property.getKey())) { return (String) property.getValue(); } } // no match, return null return null; } // <3> 不存在 properties ,则直接返回 productName return productName; } <1> 处,调用 #getDatabaseProductName(DataSource dataSource) 方法,获得数据库产品名。代码如下:

// VendorDatabaseIdProvider.java

private String getDatabaseProductName(DataSource dataSource) throws SQLException { try (Connection con = dataSource.getConnection()) { // 获得数据库连接 DatabaseMetaData metaData = con.getMetaData(); // 获得数据库产品名 return metaData.getDatabaseProductName(); } } 通过从 Connection 获得数据库产品名。 <2> 处,如果 properties 非空,则从 properties 中匹配 KEY ?若成功,则返回 VALUE ,否则,返回 null 。 <3> 处,如果 properties 为空,则直接返回 productName 。

3.3.12 typeHandlerElement

#typeHandlerElement(XNode parent) 方法,解析 <typeHandlers /> 标签。代码如下:

// XMLConfigBuilder.javaprivate void typeHandlerElement(XNode parent) throws Exception {    if (parent != null) {        // 遍历子节点        for (XNode child : parent.getChildren()) {            // <1> 如果是 package 标签,则扫描该包            if ("package".equals(child.getName())) {                String typeHandlerPackage = child.getStringAttribute("name");                typeHandlerRegistry.register(typeHandlerPackage);            // <2> 如果是 typeHandler 标签,则注册该 typeHandler 信息            } else {                // 获得 javaType、jdbcType、handler                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); // 非空 // 注册 typeHandler if (javaTypeClass != null) { if (jdbcType == null) { typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); } else { typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); } } else { typeHandlerRegistry.register(typeHandlerClass); } } } }}复制代码

遍历子节点,分别处理 <1> 是 和 <2> 是 两种标签的情况。逻辑比较简单,最终都是注册到 typeHandlerRegistry 中。

3.3.13 mapperElement

#mapperElement(XNode context) 方法,解析 标签。代码如下:

// XMLConfigBuilder.java

private void mapperElement(XNode parent) throws Exception { if (parent != null) { // <0> 遍历子节点 for (XNode child : parent.getChildren()) { // <1> 如果是 package 标签,则扫描该包 if ("package".equals(child.getName())) { // 获得包名 String mapperPackage = child.getStringAttribute("name"); // 添加到 configuration 中 configuration.addMappers(mapperPackage); // 如果是 mapper 标签, } else { // 获得 resource、url、class 属性 String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); // <2> 使用相对于类路径的资源引用 if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); // 获得 resource 的 InputStream 对象 InputStream inputStream = Resources.getResourceAsStream(resource); // 创建 XMLMapperBuilder 对象 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); // 执行解析 mapperParser.parse(); // <3> 使用完全限定资源定位符(URL) } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); // 获得 url 的 InputStream 对象 InputStream inputStream = Resources.getUrlAsStream(url); // 创建 XMLMapperBuilder 对象 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); // 执行解析 mapperParser.parse(); // <4> 使用映射器接口实现类的完全限定类名 } else if (resource == null && url == null && mapperClass != null) { // 获得 Mapper 接口 Class<?> mapperInterface = Resources.classForName(mapperClass); // 添加到 configuration 中 configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } } <0> 处,遍历子节点,处理每一个节点。根据节点情况,会分成 <1>、<2>、<3>、<4> 种情况,并且第一个是处理 标签,后三个是处理 标签。 <1> 处,如果是 标签,则获得 name 报名,并调用 Configuration#addMappers(String packageName) 方法,扫描该包下的所有 Mapper 接口。代码如下:

// Configuration.java

/**

  • MapperRegistry 对象 */ protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

public void addMappers(String packageName) { // 扫描该包下所有的 Mapper 接口,并添加到 mapperRegistry 中 mapperRegistry.addMappers(packageName); } <4> 处,如果是 mapperClass 非空,则是使用映射器接口实现类的完全限定类名,则获得 Mapper 接口,并调用 Configuration#addMapper(Class type) 方法,直接添加到 configuration 中。代码如下:

// Configuration.java

public void addMapper(Class type) { mapperRegistry.addMapper(type); } 实际上,<1> 和 <4> 是相似情况,差别在于前者需要扫描,才能获取到所有的 Mapper 接口,而后者明确知道是哪个 Mapper 接口。 <2> 处,如果是 resource 非空,则是使用相对于类路径的资源引用,则需要创建 XMLMapperBuilder 对象,并调用 XMLMapperBuilder#parse() 方法,执行解析 Mapper XML 配置。执行之后,我们就能知道这个 Mapper XML 配置对应的 Mapper 接口。关于 XMLMapperBuilder 类,我们放在下一篇博客中,详细解析。 <3> 处,如果是 url 非空,则是使用完全限定资源定位符(URL),情况和 <2> 是类似的。

转载地址:http://ctgpa.baihongyu.com/

你可能感兴趣的文章
使用深度学习检测DGA(域名生成算法)——LSTM的输入数据本质上还是词袋模型...
查看>>
【转】利用mybatis-generator自动生成代码
查看>>
架构师应该了解的知识1
查看>>
在Flex (Flash)中嵌入HTML 代码或页面—Flex IFrame
查看>>
防止Direct Input获取多次输入
查看>>
Interspeech 2017 | Self-adaptive Speech Recognition Technology
查看>>
Linux中MySQL数据库max_allowed_packet的调整
查看>>
MySQL 学习笔记 二
查看>>
Host prepare for your automation work
查看>>
Thinkphp中field和getField
查看>>
AngularJS之初级Route【一】(六)
查看>>
QTP的那些事--采用DOM,描述性编程获取指定的对象
查看>>
linux异步通信之epoll【转】
查看>>
前端自学路线之js篇
查看>>
C++:运算符重载函数之友元运算符重载
查看>>
ANT task之Junit、JunitReport
查看>>
selenium的那些事--运行报错
查看>>
谋求职业发展,是“走”还是“留”
查看>>
SpreadJS 在 Angular2 中支持绑定哪些属性?
查看>>
Lucene 定义
查看>>