本文是结合spring-mybatis整合进行的分析

1、先看看依赖的jar包:

        <dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>

2、mybatis主要两个关键对象时SqlSessionFactory和SqlSession,接下来主要结合源码对这两个对象流程进行分析:

在分析这两个对象之前先来看看XML配置情况:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"
default-autowire="byName" default-lazy-init="true"> <!-- DataSource数据 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="name" value="souchecar"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="20"/>
<property name="minIdle" value="2"/>
<property name="initialSize" value="2"/>
<property name="validationQuery" value="SELECT 1"/>
<property name="testOnBorrow" value="false"/>
<property name="testOnReturn" value="false"/>
<property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="defaultAutoCommit" value="true"/>
<property name="removeAbandoned" value="true"/>
<property name="removeAbandonedTimeout" value="60"/>
<property name="logAbandoned" value="true"/>
<property name="filters" value="stat"/>
</bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- <property name="configLocation" value="classpath:Configuration.xml" /> -->
<property name="mapperLocations">
<list>
<value>classpath*:sqlmap/**/*.xml</value>
</list>
</property>
<property name="dataSource" ref="dataSource"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/> </beans>

在配置中主要包含了数据源DruidDataSource,SqlSessionFactoryBean(包含了mybatis的映射文件),事物DataSourceTransactionManager,以及SqlSessionTemplate信息的配置,这里就不对具体的配置作用做过多的介绍

3、SqlSessionFactory

  1)SqlSessionFactory对象是mybatis中的核心对象之一,主要是通过SqlSessionFactory来创建SqlSession对象,一般在一个数据库中,最好采用单例的模式,将SqlSessionFactory创建成一个单例的对象;

  2)结合以上的配置和源码来分析SqlSessionFactory创建的一个过程:在配置文件中,主要是通过SqlSessionFactoryBean来管理SqlSessionFactory创建过程,因为该类实现了InitializingBean接口,所以在spring初始化改bean的时候,会先执行InitializingBean接口中的afterPropertiesSet()方法,在该方法中会去调用buildSqlSessionFactory()方法,该方法是用来创建Configuration对象,将配置文件中配置项信息加载到该对象中,然后在根据Configuration创建SqlSessionFactory,如下:

  public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); this.sqlSessionFactory = buildSqlSessionFactory();
}
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;

    XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (logger.isDebugEnabled()) {
logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
} if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
} if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
} if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (logger.isDebugEnabled()) {
logger.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
} if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (logger.isDebugEnabled()) {
logger.debug("Registered type alias: '" + typeAlias + "'");
}
}
} if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (logger.isDebugEnabled()) {
logger.debug("Registered plugin: '" + plugin + "'");
}
}
} if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (logger.isDebugEnabled()) {
logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
} if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (logger.isDebugEnabled()) {
logger.debug("Registered type handler: '" + typeHandler + "'");
}
}
} if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse(); if (logger.isDebugEnabled()) {
logger.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
} if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
} Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
configuration.setEnvironment(environment); if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
} if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
     // 1.获取配置中映射mapper标签中的信息,存储到MappedStatement对象中,并sql存储到SqlSource对象中 
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
} if (logger.isDebugEnabled()) {
logger.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
  // 2.创建SqlSessionFactory对象
return this.sqlSessionFactoryBuilder.build(configuration); }

  在该方法中先来分析一下xmlMapperBuilder.parse()方法,这个方法主要是将映射文件中的信息存储到相应的对象中:

  public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper")); //我们关注的是这个方法
configuration.addLoadedResource(resource);
bindMapperForNamespace();
} parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}

  在方法中configurationElement()主要是用来解析mapper标签中的信息,我们主要关注的是这个方法,下来的方法是用来解析映射文件中其他属性信息的

   private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete")); //我们关注的是这个方法
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}

  在configurationElement()方法中用来解析命名空间,参数集,结果集,select,insert,update,delete,标签等等,我们关注的是buildStatementFromContext(context.evalNodes("select|insert|update|delete"))方法

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

  在parseStatementNode()方法中,主要是用来将映射文件中的配置信息存储到MappedStatement对象中去,并将sql语句存储到了SqlSource对象中,到时候执行sql的时候,直接从该对象中获取;

4、在配置文件中sqlSession使用的是SqlSessionTemplate,实现了SqlSession接口,就是一个SqlSession模板类,在加载<bean id = sqlSession>标签时,会去调用SqlSessionTemplate构造方法,SqlSessionTemplate类有三个构造方法,最终会调用

参数sqlSessionFactory是SqlSessionFactoryBean初始化时生成的DefaultSqlSessionFactory,参数executorType是自己传入的,默认是simple,在这个构造方法中会初始换sqlSession(标记部分),通过动态代理生成SqlSession代理类,这里重点介绍

SqlSessionInterceptor,这个是SqlSessionTemplate类中的内部类,实现了InvokerHandler,是一个代理类,所以生成的SqlSession代理类,每个方法都会去执行SqlSessionInterceptor中的invoke()方法,如下所示:

首先会去初始化sqlSession对象,在getSqlSession(sessionFactory, executorType, exceptionTranslator)方法中会去调用sessionFactory中的openSession(executorType)方法,又通过调用openSessionFromDataSource(execType, level, autoCommit)方法,在该方法中进行初始化,对事物对象,executor进行初始化(Configuration.newExecutor(tx, execType)根据传入的类型创建对应的Executor对象),并创建且返回defaultSqlSession对象;executor对象对接下来sql的执行很重要,会在下一节进行介绍;

5、SqlSession

  1)SqlSession主要有四大核心组件对象:Executor,StatementHandler,ParameterHandler,ResultSetHandler

  2)在分析这四个组件对象之前,先介绍一下,为什么在使用mybatis,只需要写映射文件对应的接口类,不需要写接口的实现类,这是因为mybatis使用Java中的动态代理,在SqlSession.getMapper(Class<T> type)方法时,本质是调用Configuration类下getMapper(Class<T> type, SqlSession sqlSession),调用关系如下:

SqlSession.getMapper——>Configuration.getMapper——>MapperRegistry.getMapper——>MapperProxyFactory.newInstance

在MapperProxyFactory.newInstance方法中,会使用代理类MapperProxy为该接口生成一个代理对象,在代理类中的每个方法中都会调用MapperProxy类中的invoke()方法,在invoke()方法中会为每个方法生成一个MapperMethod对象,在去调用MapperMethod类中execute()方法,这个其实就是SqlSession的入口;

 public class MapperProxyFactory<T> {

   private final Class<T> mapperInterface;
private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
} public Class<T> getMapperInterface() {
return mapperInterface;
} public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
} @SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
} public T newInstance(SqlSession sqlSession) {
24 final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
25 return newInstance(mapperProxy);
} }
 public class MapperProxy<T> implements InvocationHandler, Serializable {

   private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
22 final MapperMethod mapperMethod = cachedMapperMethod(method);
23 return mapperMethod.execute(sqlSession, args);
} private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
} }

3)在execute方法中会根据sql类型,选择相应的方法进行执行,其实本质是调用SqlSession接口中相应的方法,SqlSession有三个实现类,其实最后本质还是调用的是DefaultSqlSession中的实现方法(在上节中介绍会初始化DefaultSqlSession对象),底层的调用是Executor的query()方法,调用的是实现类BaseExecutor的query()方法,在该方法中调用的是doQuery()方法;Executor有三个实现类,SimpleExecutor(默认)、ReuseExecutor、BatchExecutor;具体使用哪个在初始化SqlSession时确定,参考上节;在接下来的例子主要是以SimpleExecutor类举例:

   public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
5 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
6 stmt = prepareStatement(handler, ms.getStatementLog());
7 return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}

4)StatementHandler

  在doquery()方法中会创建StatementHandler对象,并调用prepareStatement对sql进行预编译和参数初始化,在prepareStatement方法中又通过调用StatementHandler接口中的prepare()创建Statement对象和调用parameterize()方法进行参数初始化和sql预编译;

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}

5)ParameterHandler

  在调用parameterize()方法,底层是通过ParameterHandler.setParameters(statement)方法进行参数的初始化过程;具体的实现过程可以参考DefaultParameterHandler.setParameters(statement)方法;

6)ResultSetHandler

  通过StatementHandler.query()方法会将sql执行返回的结果进行封装,根据配置的resultSetType类型进行转换,具体实现可以参考DefaultResultSetHandler类中的handleResultSets方法;

mybatis原理解析的更多相关文章

  1. 互联网轻量级框架SSM-查缺补漏第七天(MyBatis的解析和运行原理)

    第七章MyBatis的解析和运行原理 SqlSessionFactory是MyBatis的核心类之一,其最重要的功能就是提供创建MyBatis的核心借口SqlSession,所以要先创建SqlSess ...

  2. Mybatis的解析和运行原理

    Mybatis的解析和运行原理 Mybatis的运行过程大致分为两大步:第一步,读取配置文件缓存到Configuration对象,用以创建 SqlSessionFactory:第二步,SqlSessi ...

  3. 《深入理解mybatis原理》 MyBatis事务管理机制

    MyBatis作为Java语言的数据库框架,对数据库的事务管理是其很重要的一个方面.本文将讲述MyBatis的事务管理的实现机制. 首先介绍MyBatis的事务Transaction的接口设计以及其不 ...

  4. 《深入理解mybatis原理》 Mybatis初始化机制具体解释

    对于不论什么框架而言.在使用前都要进行一系列的初始化,MyBatis也不例外. 本章将通过下面几点具体介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XM ...

  5. 《深入理解mybatis原理》 MyBatis的架构设计以及实例分析

    作者博客:http://blog.csdn.net/u010349169/article/category/2309433 MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简 ...

  6. 深入理解mybatis原理, Mybatis初始化SqlSessionFactory机制详解(转)

    文章转自http://blog.csdn.net/l454822901/article/details/51829785 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章 ...

  7. 《深入理解mybatis原理6》 MyBatis的一级缓存实现详解 及使用注意事项

    <深入理解mybatis原理> MyBatis的一级缓存实现详解 及使用注意事项 0.写在前面   MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓 ...

  8. 《深入理解mybatis原理2》 Mybatis初始化机制详解

    <深入理解mybatis原理> Mybatis初始化机制详解 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章将通过以下几点详细介绍MyBatis的初始化过程 ...

  9. 《深入理解mybatis原理3》 Mybatis数据源与连接池

    <深入理解mybatis原理> Mybatis数据源与连接池 对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题.本文将通过对MyBatis框架的数据源结构 ...

随机推荐

  1. 手机APP缓存的获取和清理功能的实现

    package com.loaderman.appcachedemo; import android.content.pm.IPackageDataObserver; import android.c ...

  2. js解析后台传过来的json

    java ,action public void print(String rs){ PrintWriter out; try { HttpServletResponse response = Ser ...

  3. oracle 导出关键字说明

    关键字       说明(默认) ----------------------------------------- UserId    用户名/口令 Full      导出整个文件(N) Buff ...

  4. CentOS7性能监控系统安装

    想打造 New Relic 那样漂亮的实时监控系统我们只需要 InfluxDB/collectd/Grafana 这三个工具,这三个工具的关系是这样的: 采集数据(collectd)-> 存储数 ...

  5. Vue组件中的data属性

    Vue中的data属性专门用来以对象方式存放数据,它有两种用法. var vm=new Vue({ data:{a:1,b:2,}, }) var vm=new Vue({ data(){return ...

  6. Nginx代理与反向代理、负载均衡实

    通过 Nginx 提供的反向代理和负载均衡功能,可以合理的完成业务的分配,提高网站的处理能力:同时利用缓存功能,还可以将不需要实时更新的动态页面输出结果,转化为静态网页形成缓存,从而提高网站的响应速度 ...

  7. 在依赖的框架中已经有统一异常处理的情况下,如何定制自己的统一异常处理spring boot版本

    spring boot 环境下的统一异常处理大家已经非常熟悉了,不熟悉的化可以参考 <<Spring Boot中Web应用的统一异常处理>>.公司内部的统一异常处理如下: @E ...

  8. 【DSP开发】【计算机视觉】TI 视觉软件开发套件ADAS

    关键字:TI  视觉软件开发套件  ADAS 日前,德州仪器 (TI) 宣布推出其视觉软件开发套件(SDK),从而为开发人员提供了一款灵活的框架.一组丰富齐全的硬件设备驱动程序和一套适用的开发工具,可 ...

  9. 【Python开发】Url中文字符时记得转码edcode("utf-8")

    在url中使用中文其实是一个坏习惯,会带来一系列的转码问题, 我更喜欢英文译名或者id来标识某个uri.但是现实往往是残酷的, 特别是在我们调用别人服务时候,有时候被逼无奈使用中文URL. Pytho ...

  10. C#实现多线程的方式:使用Parallel类

    简介 在C#中实现多线程的另一个方式是使用Parallel类.  在.NET4中 ,另一个新增的抽象线程是Parallel类 .这个类定义了并行的for和foreach的 静态方法.在为 for和 f ...