Mybatis 源码分析(一)

前言

        接下来我们就开始Mybatis的源码之旅,首先从宏观上了解Mybatis的整体框架分为三层,分别是基础支持层、核心处理层、接口层,详情看图片

 接口层

        接口层是开发者使用频率最多的一层,核心对象SqlSession,它是上层应用和Mybatis打交道的桥梁,SqlSession上定义了非常多的对数据库的操作方法。接口层在接收到调用请求的时候,会调用核心处理层的相应模块来完成具体的操作。

核心处理层

        跟数据库操作相关的动作都是在这一层完成的,核心处理层主要做了这几件事:

  1. 把接口中传入的参数解析并且映射成JDBC类型
  2. 解析xml文件中的SQL语句,包括插入参数和动态SQL。
  3. 执行SQL语句。
  4. 处理结果集、并映射成JAVA对象

        插件也属于核心层,这是由它工作方式和拦截的对象决定的。

基础支持层

        基础支持层主要是一些抽取出来的通用的功能,实现复用,用来支持核心处理层的功能,比如数据源、缓存、日志、xml解析、反射、IO、事务等等。

核心流程

系统启动至最终SQL的执行过程 

        Mybatis的主要工作流程其实就是系统启动至最终SQL的执行过程,从构建SqlSessionFactory开始,调用Configuration用来解析全局配置和映射文件,最终生成一个新的DefaultSqlSessionFactory类。当完成SqlSessionFactory构建后,就会继续构建SqlSession,SqlSession是面向应用开发的接口,SqlSession会调用执行器Executor,Executor再调用处理器StatementHandler,StatementHandler又包括输入参数处理器ParameterHanlder以及输出参数ResultSetHandler,最终执行sql语句。

核心对象的生命周期

SqlSessionFactoryBuiler

        首先是SqlSessionFactoryBuiler,它是用来构建SqlSessionFactory的,而SqlSessionFactory只需要一个,所以只要构建了这一个SqlSessionFactory,SqlSessionFactoryBuiler的流程就结束了,也没有存在的意义了,所以它的生命周期只存在于方法的局部。

SqlSessionFactory

        SqlSessionFactory是用来创建SqlSession的,每次应用程序访问数据库,都需要创建一个会话。因为我们一直有创建会话的需要,所以SqlSessionFactory应该存在于应用的整个生命周期中,作用域是应用作用域。创建SqlSession只需要一个实例来做就行了,否则会产生很多的混乱和浪费资源。所以采用单例模式

SqlSession

        SqlSession是一个会话,因为它是线程不安全的,不能在线程间共享。所以在请求开始的时候创建一个SqlSession对象,在请求结束或者说方法执行完毕的时候要及时关闭它。

Mapper

        Mapper实际上是一个代理对象,是从SqlSession中获取的。作用是发送SQL来操作数据库的数据。它应该在一个SqlSession事务方法之内。

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

 SqlSessionFactory

        首先我们来看下SqlSessionFactory对象的获取

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

SqlSessionFactoryBuilder

        首先我们new一个SqlSessionFactoryBuilder对象,这是建造者模式的运用,建造者模式用来创建复杂对象,而不需要关注内部细节,是一种封装的提现。Mybatis中很多地方用到了建造者模式。

        SqlSessionFactoryBuilder用来创建SqlSessionFactory对象的方法是build(),build()方法有9个重载,可以用不同的方式来创建SqlSessionFactory对象。SqlSessionFactory对象默认是单例的。

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
          // 用于解析 mybatis-config.xml,同时创建了 Configuration 对象 >>
          XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // 解析XML,最终返回一个 DefaultSqlSessionFactory >>
        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.
      }
    }
  }

// 构建的代码,parser.parse()方法返回的是一个Configuration对象,build方法的如下,在这儿我们可以看到SessionFactory最终实现是DefaultSqlSessionFactory对象。
public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

        在build方法中首先是创建了一个XMLConfigBuilder对象,XMLConfigBuilder是抽象类BaseBuilder的一个子类,专门用来解析全局配置文件,针对不同的构建目标还有其他的一些子类(关联到源码路径),比如:

  • XMLMapperBuilder:解析Mapper映射器
  • XMLStatementBuilder:解析增删改查标签
  • XMLScriptBuilder:解析动态SQL

XMLConfigBuilder

        然后我们再来看看XMLConfigBuilder初始化的时候做了哪些操作

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    // // EntityResolver的实现类是XMLMapperEntityResolver 来完成配置文件的校验,根据对应的DTD文件来实现

    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

        再去进入重载的构造方法中

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    // 完成了Configuration的初始化
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    // 设置对应的Properties属性
    this.configuration.setVariables(props);
    // 设置 是否解析的标志为 false
    this.parsed = false;
    // 初始化environment
    this.environment = environment;
    // 初始化 解析器
    this.parser = parser;
  }

Configuration

        Configuration完成了类型别名的注册工作,通过上面的分析我们可以看到XMLConfigBuilder完成了XML文件的解析,对应XPathParse和Configuration对象的初始化操作,然后我们再来看看parse方法到底是如何解析配置文件的。

public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

parse解析

        进入具体的解析方法

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

parseConfiguration方法

private void parseConfiguration(XNode root) {
        try {
            // issue #117 read properties first
            // 对于全局配置文件各种标签的解析
            propertiesElement(root.evalNode("properties"));
            // 解析 settings 标签
            Properties settings = settingsAsProperties(root.evalNode("settings"));
			// 读取文件
            loadCustomVfs(settings);
			// 日志设置
            loadCustomLogImpl(settings);
			// 类型别名
            typeAliasesElement(root.evalNode("typeAliases"));
			// 插件
            pluginElement(root.evalNode("plugins"));
			// 用于创建对象
            objectFactoryElement(root.evalNode("objectFactory"));
			// 用于对对象进行加工
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
			// 反射工具箱
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
			// settings 子标签赋值,默认值就是在这里提供的 >>
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
			// 创建了数据源 >>
            environmentsElement(root.evalNode("environments"));
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            typeHandlerElement(root.evalNode("typeHandlers"));
			// 解析引用的Mapper映射器
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }

全局配置文件解析

properties解析

private void propertiesElement(XNode context) throws Exception {
        if (context != null) {
			// 创建了一个 Properties 对象,后面可以用到
            Properties defaults = context.getChildrenAsProperties();
            String resource = context.getStringAttribute("resource");
            String url = context.getStringAttribute("url");
            if (resource != null && url != null) {
				// url 和 resource 不能同时存在
                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属性中指定的 properties 文件
            if (resource != null) {
                defaults.putAll(Resources.getResourceAsProperties(resource));
            } else if (url != null) {
                defaults.putAll(Resources.getUrlAsProperties(url));
            }
            Properties vars = configuration.getVariables();
            if (vars != null) {
				// 和 Configuration中的 variables 属性合并
                defaults.putAll(vars);
            }
			// 更新对应的属性信息
            parser.setVariables(defaults);
            configuration.setVariables(defaults);
        }
    }

        第一个是解析properties标签,读取引入的外部配置文件。这里面又有两种类型,一种是放在reource目录下的,是相对路径,一种是写的绝对路径(url)。解析的最终结果就是我们会把所有的配置信息放到名为defaults的properties对象里面,HashTable存储的。最后把XPathParser和Configuration的Properties属性都设置成我们填充后的properties对象。

settings解析

private Properties settingsAsProperties(XNode context) {
        if (context == null) {
            return new Properties();
        }
		// 获取settings节点下的所有的子节点
        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;
    }

getChildrenAsProperties方法就是具体的解析了

public Properties getChildrenAsProperties() {
        Properties properties = new Properties();
        Iterator var2 = this.getChildren().iterator();

        while(var2.hasNext()) {
            XNode child = (XNode)var2.next();
            // 获取对应的name和value属性
            String name = child.getStringAttribute("name");
            String value = child.getStringAttribute("value");
            if (name != null && value != null) {
                properties.setProperty(name, value);
            }
        }

        return properties;
    }

loadCustomVfs(settings)方法

        loadCustomVfs是获取Vitual File System的自定义实现类,比如要读取本地文件,或者FTP远程文件的时候,就可以用到自定义的VFS类。

        根据settings标签里面的vfsImpl标签生成了一个抽象类VFS的子类,在Mybatis中有JBoss6VFS和DefaultVFS两个实现,在IO包中,最后赋值到Configuration中。

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

loadCustomLogImpl(settings)方法

        loadCustomLogImpl是根据标签获取日志的实现类,我们可以用到很多的日志的方案,包 括LOG4J,LOG4J2,SLF4J等等,在logging包中。

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

typeAliases解析

        这一步是类型别名的解析

private void typeAliasesElement(XNode parent) {
        // 放入 TypeAliasRegistry
        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) {
                            // 扫描 @Alias 注解使用
                            typeAliasRegistry.registerAlias(clazz);
                        } else {
							// 直接注册
                            typeAliasRegistry.registerAlias(alias, clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
                    }
                }
            }
        }
    }

plugins解析        

        插件标签的解析

private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
            for (XNode child : parent.getChildren()) {
				// 获取<plugin> 节点的 interceptor 属性的值
                String interceptor = child.getStringAttribute("interceptor");
				// 获取<plugin> 下的所有的properties子节点
                Properties properties = child.getChildrenAsProperties();
				// 获取 Interceptor 对象
                Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
                // 设置 interceptor的 属性
				interceptorInstance.setProperties(properties);
				// Configuration中记录 Interceptor
                configuration.addInterceptor(interceptorInstance);
            }
        }
    }
objectFactoryElement,objectWrapperFactoryElement及reflectorFactoryElement解析
private void objectFactoryElement(XNode context) throws Exception {
        if (context != null) {
			// 获取<objectFactory> 节点的 type 属性
            String type = context.getStringAttribute("type");
			// 获取 <objectFactory> 节点下的配置信息
            Properties properties = context.getChildrenAsProperties();
			// 获取ObjectFactory 对象的对象 通过反射方式
            ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
			// ObjectFactory 和 对应的属性信息关联
            factory.setProperties(properties);
			// 将创建的ObjectFactory对象绑定到Configuration中
            configuration.setObjectFactory(factory);
        }
    }

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

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

        ObjectFactory用来创建返回的对象,ObjectWrapperFactory用来对对象做特殊的处理。比如;select 没有写别名,查询返回的是一个Map,可以在自定义的objectWrapperFactory中把下划线命名变成驼峰名。

        ReflectorFactory是反射的工具箱,对反射的操作进行了封装,以上对象都是用resolveClass创建的。

settingsElement(settings)方法

        这里是对settings标签里面所以子标签的处理,前面我们已经把子标签全部转换成了Properties对象,所以在这里处理Properties对象就可以了。

        settings二级标签中一共26个配置,比如二级缓存、延迟加载、默认执行器类型等等。

需要注意的是。settings里面的默认值都是在这里赋值的

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.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
        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")));
        configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
        configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
    }
environmentsElement解析

        这一步是解析environments标签,一个environment就是对应一个数据源,所以在这里我们会根据配置的 创建一个事务工厂,根据标签创建一个数据源,最后把这两个对 象设置成Environment对象的属性,放到Configuration里面。

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"));
                    // 数据源工厂(例如 DruidDataSourceFactory )
					DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                    // 数据源
					DataSource dataSource = dsFactory.getDataSource();
                    // 包含了 事务工厂和数据源的 Environment
					Environment.Builder environmentBuilder = new Environment.Builder(id)
                            .transactionFactory(txFactory)
                            .dataSource(dataSource);
                    // 放入 Configuration
					configuration.setEnvironment(environmentBuilder.build());
                    break;
                }
            }
        }
    }
databaseIdProviderElement方法

        解析databaseIdProvider标签,生成DatabaseIdProvider对象(用来支持不同厂商的数据库)。

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).getDeclaredConstructor().newInstance();
            databaseIdProvider.setProperties(properties);
        }
        Environment environment = configuration.getEnvironment();
        if (environment != null && databaseIdProvider != null) {
            String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
            configuration.setDatabaseId(databaseId);
        }
    }
typeHandlerElement 方法

        跟TypeAlias一样,TypeHandler有两种配置方式,一种是单独配置一个类,一种是指定一个package。 最后我们得到的是JavaType和JdbcType,以及用来做相互映射的TypeHandler之间的映射关系,存放在 TypeHandlerRegistry对象里面。

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);
                    }
                }
            }
        }
    }
mapperElement方法,mapper解析

        mapper解析,根据全局配置文件中不同的注册方式,用不同的方法扫描,但最终都是做了两件事情,对于语句的注册和接口的注册

扫描类型含义
resource相对路径
url绝对路径
package
class单个接口
private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            for (XNode child : parent.getChildren()) {
				// 不同的定义方式的扫描,最终都是调用 addMapper()方法(添加到 MapperRegistry)。这个方法和 getMapper() 对应
				// package 包
                if ("package".equals(child.getName())) {
                    String mapperPackage = child.getStringAttribute("name");
                    configuration.addMappers(mapperPackage);
                } else {
                    String resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    String mapperClass = child.getStringAttribute("class");
                    if (resource != null && url == null && mapperClass == null) {
						// resource 相对路径
                        ErrorContext.instance().resource(resource);
                        try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
                            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                            // 解析 Mapper.xml,总体上做了两件事情 
							mapperParser.parse();
                        }
                    } else if (resource == null && url != null && mapperClass == null) {
                        // url 绝对路径
						ErrorContext.instance().resource(url);
                        try(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 单个接口
						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.");
                    }
                }
            }
        }
    }

映射文件的解析

        首先进入parse方法

public void parse() {
        // 总体上做了两件事情,对于语句的注册和接口的注册
        if (!configuration.isResourceLoaded(resource)) {
            // 1、具体增删改查标签的解析。
            // 一个标签一个MappedStatement。 >
            configurationElement(parser.evalNode("/mapper"));
            configuration.addLoadedResource(resource);
            // 2、把namespace(接口类型)和工厂类绑定起来,放到一个map。
            // 一个namespace 一个 MapperProxyFactory >>
            bindMapperForNamespace();
        }

        parsePendingResultMaps();
        parsePendingCacheRefs();
        parsePendingStatements();
    }

        configurationElement()——解析所有的子标签,最终获得MappedStatement对象。bindMapperForNamespace()——把namespace(接口类型)和工厂类MapperProxyFactory绑定起来。

configurationElement方法

        configurationElement是对Mapper.xml中所有具体的标签的解析,包括namespace、cache、 parameterMap、resultMap、sql和select|insert|update|delete。

private void configurationElement(XNode context) {
        try {
            String namespace = context.getStringAttribute("namespace");
            if (namespace == null || namespace.isEmpty()) {
                throw new BuilderException("Mapper's namespace cannot be empty");
            }
            builderAssistant.setCurrentNamespace(namespace);
            // 添加缓存对象
            cacheRefElement(context.evalNode("cache-ref"));
            // 解析 cache 属性,添加缓存对象
            cacheElement(context.evalNode("cache"));
            // 创建 ParameterMapping 对象
            parameterMapElement(context.evalNodes("/mapper/parameterMap"));
            // 创建 List<ResultMapping>
            resultMapElements(context.evalNodes("/mapper/resultMap"));
            // 解析可以复用的SQL
            sqlElement(context.evalNodes("/mapper/sql"));
            // 解析增删改查标签,得到 MappedStatement >>
            buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
        }
    }

        在buildStatementFromContext()方法中,创建了用来解析增删改查标签的XMLStatementBuilder,并 且把创建的MappedStatement添加到mappedStatements中。

private void buildStatementFromContext(List<XNode> list) {
        if (configuration.getDatabaseId() != null) {
            buildStatementFromContext(list, configuration.getDatabaseId());
        }
        buildStatementFromContext(list, null);
    }

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        for (XNode context : list) {
            final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
            try {
                statementParser.parseStatementNode();
            } catch (IncompleteElementException e) {
                configuration.addIncompleteStatement(statementParser);
            }
        }
    }

bindMapperForNamespace方法

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 && !configuration.hasMapper(boundType)) {
                // Spring may not know the real resource name so we set a flag
                // to prevent loading again this resource from the mapper interface
                // look at MapperAnnotationBuilder#loadXmlResource
                configuration.addLoadedResource("namespace:" + namespace);
                // 添加到 MapperRegistry,本质是一个 map,里面也有 Configuration >>
                configuration.addMapper(boundType);
            }
        }
    }

        通过源码分析发现主要是是调用了addMapper()。addMapper()方法中,把接口类型注册到 MapperRegistry中:实际上是为接口创建一个对应的MapperProxyFactory(用于为这个type提供工厂 类,创建MapperProxy)。

public <T> void addMapper(Class<T> type) {
        // 检测 type 是否为接口
        if (type.isInterface()) {
            // 检测是否已经加装过该接口
            if (hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
                // !Map<Class<?>, MapperProxyFactory<?>> 存放的是接口类型,和对应的工厂类的关系
                knownMappers.put(type, new MapperProxyFactory<>(type));
                // It's important that the type is added before the parser is run
                // otherwise the binding may automatically be attempted by the
                // mapper parser. If the type is already known, it won't try.
                // 注册了接口之后,根据接口,开始解析所有方法上的注解,例如 @Select >>
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    knownMappers.remove(type);
                }
            }
        }
    }

同样的再进入parse方法查看

public void parse() {
        String resource = type.toString();
        if (!configuration.isResourceLoaded(resource)) {
            // 先判断 Mapper.xml 有没有解析,没有的话先解析 Mapper.xml(例如定义 package 方式)
            loadXmlResource();
            configuration.addLoadedResource(resource);
            assistant.setCurrentNamespace(type.getName());
            // 处理 @CacheNamespace
            parseCache();
            // 处理 @CacheNamespaceRef
            parseCacheRef();
            // 获取所有方法
            for (Method method : type.getMethods()) {
                if (!canHaveStatement(method)) {
                    continue;
                }
                if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
                        && method.getAnnotation(ResultMap.class) == null) {
                    // 解析方法上的注解,添加到 MappedStatement 集合中 >>
                    parseResultMap(method);
                }
                try {
                    parseStatement(method);
                } catch (IncompleteElementException e) {
                    configuration.addIncompleteMethod(new MethodResolver(this, method));
                }
            }
        }
        parsePendingMethods();
    }

大家可以继续进入到parseStatement方法中查看。

总结:

  1. 我们主要完成了config配置文件、Mapper文件、Mapper接口中注解的解析
  2. 我们得到了一个最重要的对象Configuration,这里面存放了全部的配置信息,在它的属性里还有各种各样的容器
  3. 最后返回了一个DefaultSqlSessionFactory,里面持有了Configuration的实例

 最后时序图

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值