MyBatis源码解析

来源:互联网 发布:期货信息软件 编辑:程序博客网 时间:2024/06/03 16:37

MyBatis源码解析

1. MyBatis主要核心部件:

  • Configuration 包含MyBatis所有的配置信息,除此之外还负责穿件一些MyBatis内部使用对象;如:Executor等;
    • SqlSession 与数据库交互的会话,MyBatis顶层API,完成增删改查功能;如:selectOne、selectList、update、delete等方法;
  • StatementHandler 封装了JDBC Statement操作,负责对JDBC Statement操作;负责设置参数,将JDBC Statement结果集转换成指定对象;
  • ParameterHandler 负责将用户传递的参数转换成JDBC Statement所需要的参数;
  • ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型集合;
    • TypeHandler 负责JAVA数据类型与JDBC数据类型只拿的映射与转换;
  • MappedStatement MappedStatement维护了一条select|update|delete|insert节点的封装【简单点讲就是一条sql的配置信息】;
  • SqlSource负责根据用户传递的阐述信息,动态生成sql语句,封装到BoundSql对象中;
  • BoundSql 表示动态生成的sql语句,以及相应的参数信息;

下图为MyBatis类层次结构,摘自网上

MyBatis层次结构

2. MyBatis初始化过程–Configuration

2.1. 初始化方式:

​ a. xml:将MyBatis的所有配置信息放在xml中,MyBatis通过加载xml配置文件,将配置文件组装成Configuration对象;如:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>    <settings>        <setting name="cacheEnabled" value="true"/>        <setting name="lazyLoadingEnabled" value="false"/>    </settings>    <typeAliases>        <typeAlias type="cn.test.mybatis.App" alias="App"/>    </typeAliases>    <environments default="development">        <environment id="development">            <transactionManager type="JDBC" />             <!-- 配置数据库连接信息 -->            <dataSource type="POOLED">                <property name="driver" value="com.mysql.jdbc.Driver" />                <property name="url" value="jdbc:mysql://localhost:3306/auth" />                <property name="username" value="root" />                <property name="password" value="root" />            </dataSource>        </environment>    </environments>    <mappers>        <mapper resource="cn/AppMapper.xml"/>    </mappers></configuration>

​ b.java api: 跳开xml配置,通过手动创建Configuration对象,针对配置项set进入Configuration对象;【不推荐】

2.2. 基于XML创建Configuration对象过程

简单运行demo, conf.xml见上节所示:

package cn.test.mybatis;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.InputStream;public class TestMain {    public static void main(String[] args) {        InputStream is = TestMain.class.getResourceAsStream("/conf.xml");        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);        SqlSession sqlSession = sqlSessionFactory.openSession();        App app = null;        try {//            AppDao appDao = sqlSession.getMapper(AppDao.class);//            app = appDao.getById(1l);            String statement = "cn.test.mybatis.dao.AppDao.getById";            app = sqlSession.selectOne(statement, 1l);            System.out.println(app);        } catch (Exception e) {            e.printStackTrace();        } finally {            sqlSession.close();        }    }}

Configuration创建时序图,来源网上:

XML配置创建Configuration时序图

整个Configuration初始化从SqlSessionFactory的build方法开始,调用XMLConfigBuilder的parse方法,构建Configuration对象,也就是MyBatis的配置信息:

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.parse解析xml文件的configuration节点下settingspropertiestypeAliasesplutinsenviromentstypeHandlersmappers等子节点;PS:用XMLMapperBuilder来解析mappers节点,其中包含MappedStatement信息;
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 {      Properties settings = settingsAsPropertiess(root.evalNode("settings"));      //issue #117 read properties first      propertiesElement(root.evalNode("properties"));      loadCustomVfs(settings);      typeAliasesElement(root.evalNode("typeAliases"));      pluginElement(root.evalNode("plugins"));      objectFactoryElement(root.evalNode("objectFactory"));      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));      reflectorFactoryElement(root.evalNode("reflectorFactory"));      settingsElement(settings);      // read it after objectFactory and objectWrapperFactory issue #631      environmentsElement(root.evalNode("environments"));      databaseIdProviderElement(root.evalNode("databaseIdProvider"));      typeHandlerElement(root.evalNode("typeHandlers"));      mapperElement(root.evalNode("mappers"));    } catch (Exception e) {      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);    }  }
  1. 将xml解析出的配置设置到Configuration对象中,以typeAliases为例:
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);          }        }      }    }  }

其中typeAliasRegistry即Configuration中的属性;

public BaseBuilder(Configuration configuration) {    this.configuration = configuration;    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();  }

3. MyBatis执行过程

此处以select查询为例,观察MyBatis运行过程

3.1. 准备

sql 准备

CREATE TABLE `app` (  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Id',  `app_id` varchar(150) NOT NULL COMMENT 'appId',  `app_name` varchar(150) DEFAULT NULL COMMENT '名字',  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  `modify_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',  PRIMARY KEY (`id`),  KEY `ix_create_time` (`create_time`),  KEY `ix_modify_time` (`modify_time`),  KEY `ix_app_id` (`app_id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='app';-- 添加数据insert into app(id, app_id, app_name) values (1, 'delimont.sm', '系统管理');

MyBatis配置文件

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>    <settings>        <setting name="cacheEnabled" value="true"/>        <setting name="lazyLoadingEnabled" value="false"/>    </settings>    <typeAliases>        <typeAlias type="cn.test.mybatis.App" alias="App"/>    </typeAliases>    <environments default="development">        <environment id="development">            <transactionManager type="JDBC" /> <!-- 声明使用那种事务管理机制 JDBC/MANAGED -->            <!-- 配置数据库连接信息 -->            <dataSource type="POOLED">                <property name="driver" value="com.mysql.jdbc.Driver" />                <property name="url" value="jdbc:mysql://localhost:3306/auth" />                <property name="username" value="root" />                <property name="password" value="root" />            </dataSource>        </environment>    </environments>    <mappers>        <mapper resource="cn/AppMapper.xml"/>    </mappers></configuration>

App实体Bean以及相关Mapper配置文件

package cn.test.mybatis;import java.io.Serializable;import java.util.Date;import lombok.Data;@Datapublic class App implements Serializable {    private Long id;    private String appId;    private String appName;    private Date createTime;    private Date modifyTime;}
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="cn.test.mybatis.dao.AppDao">    <resultMap id="BaseMap" type="App">        <id column="id" jdbcType="BIGINT" property="id" />        <result column="app_id" jdbcType="VARCHAR" property="appId"/>        <result column="app_name" jdbcType="TIMESTAMP" property="appName"/>        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>        <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>    </resultMap>    <select id="getById" parameterType="java.lang.Long" resultMap="BaseMap">        SELECT * FROM app WHERE id=#{id};    </select>    <select id="getAll" resultMap="BaseMap">        SELECT * FROM app;    </select></mapper>
package cn.test.mybatis.dao;import cn.test.mybatis.App;import java.util.List;public interface AppDao {    App getById(Long id);    List<App> getAll();}

客户端执行代码

package cn.test.mybatis;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.InputStream;public class TestMain {    public static void main(String[] args) {        InputStream is = TestMain.class.getResourceAsStream("/conf.xml");        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);        SqlSession sqlSession = sqlSessionFactory.openSession();        App app = null;        try {//            AppDao appDao = sqlSession.getMapper(AppDao.class);//            app = appDao.getById(1l);            String statement = "cn.test.mybatis.dao.AppDao.getById";            app = sqlSession.selectOne(statement, 1l);            System.out.println(app);        } catch (Exception e) {            e.printStackTrace();        } finally {            sqlSession.close();        }    }}

3.2. 代码解析

3.2.1. SqlSession— MyBatis顶级API
SqlSession sqlSession = sqlSessionFactory.openSession();
@Overridepublic SqlSession openSession() {  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {    Transaction tx = null;    try {      final Environment environment = configuration.getEnvironment();      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);      final Executor executor = configuration.newExecutor(tx, execType);      return new DefaultSqlSession(configuration, executor, autoCommit);    } catch (Exception e) {      closeTransaction(tx); // may have fetched a connection so lets call close()      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }

创建Executor,创建时使用了简单工厂模式,通过executorType创建对应的Executor;

如果开启了MyBatis的二级缓存,Executor会是CacheEnabled;

那么问题来了,这种模式是代理模式还是装饰者模式?为什么?代理模式与装饰者模式的区别?

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {    ....      final Executor executor = configuration.newExecutor(tx, execType);    ...  }public Executor newExecutor(Transaction transaction, ExecutorType executorType) {    executorType = executorType == null ? defaultExecutorType : executorType;    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;    Executor executor;    if (ExecutorType.BATCH == executorType) {      executor = new BatchExecutor(this, transaction);    } else if (ExecutorType.REUSE == executorType) {      executor = new ReuseExecutor(this, transaction);    } else {      executor = new SimpleExecutor(this, transaction);    }    // cacheEnabled配置项,决定是否开启二级缓存;有机会见下文介绍MyBatis的二级缓存    if (cacheEnabled) {      executor = new CachingExecutor(executor);    }    executor = (Executor) interceptorChain.pluginAll(executor);    return executor;  }

注:构建SqlSession的时候,还会创建一个Executor对象,所有sql执行会委托给Executor,见下文;

3.2.2. sql执行过程

app = sqlSession.selectOne("cn.test.mybatis.dao.AppDao.getById", 1l);
@Overridepublic <T> T selectOne(String statement, Object parameter) {  // Popular vote was to return null on 0 results and throw exception on too many.  List<T> list = this.<T>selectList(statement, parameter);  if (list.size() == 1) {    return list.get(0);  } else if (list.size() > 1) {    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());  } else {    return null;  }}@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {  try {    MappedStatement ms = configuration.getMappedStatement(statement);    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);  } catch (Exception e) {    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);  } finally {    ErrorContext.instance().reset();  }}

通过cn.test.mybatis.dao.AppDao.getById找到对应的sql的配置信息MappedStatement;

通过Executor执行query方法;

//BaseExecutor@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {  // 将传入的参数,以及SqlSource创建BoundSql对象;动态生成需要执行的sql语句  BoundSql boundSql = ms.getBoundSql(parameter);  // CacheKey 缓存键值;MyBatis一级缓存使用  // 具体生成规则详见createCacheKey  CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);  return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}@SuppressWarnings("unchecked")@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());  if (closed) {    throw new ExecutorException("Executor was closed.");  }  if (queryStack == 0 && ms.isFlushCacheRequired()) {    clearLocalCache();  }  List<E> list;  try {    queryStack++;    // 通过本地缓存[一级缓存]查找结果    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;    if (list != null) {      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);    } else {      // 如果缓存中并有找到对应的查询结果,直接从数据库中读取数据      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);    }  } finally {    queryStack--;  }  if (queryStack == 0) {    for (DeferredLoad deferredLoad : deferredLoads) {      deferredLoad.load();    }    deferredLoads.clear();    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {      clearLocalCache();    }  }  return list;}

创建java.Sql.Statement对象,对创建的Statement对象设置参数,即设置SQL 语句中 ? 设置为指定的参数

// SimpleExecutor@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {  Statement stmt = null;  try {    Configuration configuration = ms.getConfiguration();    // 根据既有的参数,创建StatementHandler对象来执行查询操作    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);    // 创建java.Sql.Statement对象,传递给StatementHandler对象      stmt = prepareStatement(handler, ms.getStatementLog());    // 调用StatementHandler.query()方法,返回List结果集    return handler.<E>query(stmt, resultHandler);  } finally {    closeStatement(stmt);  }}private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {  Statement stmt;  Connection connection = getConnection(statementLog);  stmt = handler.prepare(connection, transaction.getTimeout());  // 对创建的Statement对象设置参数,即设置SQL 语句中 ? 设置为指定的参数   // 有兴趣可看具体时间  handler.parameterize(stmt);  return stmt;}

通过Statement连接数据库执行sql,将返回结构结构化

// SimpleExecutor@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {  ...    // 调用StatementHandler.query()方法,返回List结果集    return handler.<E>query(stmt, resultHandler);  ...}// PreparedStatementHandler@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {  PreparedStatement ps = (PreparedStatement) statement;  // 执行sql  ps.execute();  // 将返回结果结构化  // 具体实现见源代码  return resultSetHandler.<E> handleResultSets(ps);}

4. MyBatis缓存机制

MyBatis将数据缓存分为两部分:一级缓存与二级缓存:

​ 一级缓存:是Session会话级别的缓存,位于表示一次数据库会话的SqlSession对象之中,又被称之为本地缓存;

​ 二级缓存:是Application应用级别的缓存,作用范围是整个Applicaiton应用;

MyBatis查询的顺序是:

​ 二级缓存 —> 一级缓存 —> 数据库

MyBatis中一级缓存和二级缓存结构如下,摘自网络:

4.1. 一级缓存

代码位置

// BaseExecutor@SuppressWarnings("unchecked")@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());  if (closed) {    throw new ExecutorException("Executor was closed.");  }  if (queryStack == 0 && ms.isFlushCacheRequired()) {    clearLocalCache();  }  List<E> list;  try {    queryStack++;    // localCache 缓存中获取查询结果    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;    if (list != null) {      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);    } else {      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);    }  } finally {    queryStack--;  }  if (queryStack == 0) {    for (DeferredLoad deferredLoad : deferredLoads) {      deferredLoad.load();    }    // issue #601    deferredLoads.clear();    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {      // issue #482      clearLocalCache();    }  }  return list;}

SqlSession查询时序图,摘自网络:

生命周期

SqlSession对象持有Executor对象,Executor持有PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象中的PerpetualCache对象也会被销毁。

  1. SqlSession.close;
  2. SqlSession.clearCache;
  3. SqlSession中执行任何一个update操作(update, delete,insert);

注意

根据一级缓存的特性,应该一下几点:

  1. 对于数据变化频率很大,并且需要高时效准确性的数据要求,使用SqlSession查询的时候,要控制好SqlSession的生存时间,SqlSession的生存时间越长,其中缓存的数据有可能就越久,从而造成和真实数据库的误差;
  2. 对于只执行、并且频繁执行大范围的select操作的SqlSession对象;

4.2. 二级缓存

代码位置

// DefaultSqlSessionFactory@Overridepublic SqlSession openSession() {  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {  ...    final Executor executor = configuration.newExecutor(tx, execType);    return new DefaultSqlSession(configuration, executor, autoCommit);  ...}// Configurationpublic Executor newExecutor(Transaction transaction, ExecutorType executorType) {  executorType = executorType == null ? defaultExecutorType : executorType;  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;  Executor executor;  if (ExecutorType.BATCH == executorType) {    executor = new BatchExecutor(this, transaction);  } else if (ExecutorType.REUSE == executorType) {    executor = new ReuseExecutor(this, transaction);  } else {    executor = new SimpleExecutor(this, transaction);  }  // 二级缓存  if (cacheEnabled) {    executor = new CachingExecutor(executor);  }  executor = (Executor) interceptorChain.pluginAll(executor);  return executor;}

二级缓存使用前提:

  1. MyBatis支持二级缓存的总开关:cacheEnabled=true
  2. 该select语句所在Mapper,配置或节点
  3. 该select语句参数useCache=true

5. 与Spring集成

配置

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">    <!-- 数据源 -->    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/auth"/>        <property name="username" value="root"/>        <property name="password" value="root"/>    </bean>    <!-- 通过SqlSessionFactoryBean构建sqlSessionFactory -->    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">        <property name="mapperLocations" value="classpath*:cn/**/*.xml" />        <!-- MyBatis配置 -->        <property name="configLocation" value="classpath:conf-spring.xml"/>        <property name="dataSource" ref="dataSource"/>    </bean>    <!-- 将制定查询类路径下的接口类,自动将他们创建成MapperFactoryBean -->    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">        <property name="basePackage" value="cn.test.mybatis.dao"/>        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />    </bean></beans>

执行

package cn.test.mybatis;import cn.test.mybatis.dao.AppDao;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class SpringMain {    public static void main(String[] args) {        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");        AppDao appDao = applicationContext.getBean(AppDao.class);        App app = appDao.getById(1l);        System.out.println(app);    }}

通过代码看本质

// MapperScannerConfigurer@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {  if (this.processPropertyPlaceHolders) {    processPropertyPlaceHolders();  }  ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);  scanner.setAddToConfig(this.addToConfig);  scanner.setAnnotationClass(this.annotationClass);  scanner.setMarkerInterface(this.markerInterface);  scanner.setSqlSessionFactory(this.sqlSessionFactory);  scanner.setSqlSessionTemplate(this.sqlSessionTemplate);  scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);  scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);  scanner.setResourceLoader(this.applicationContext);  scanner.setBeanNameGenerator(this.nameGenerator);  scanner.registerFilters();  scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));}// ClassPathMapperScanner@Overridepublic Set<BeanDefinitionHolder> doScan(String... basePackages) {  Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);  if (beanDefinitions.isEmpty()) {    logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");  } else {    processBeanDefinitions(beanDefinitions);  }  return beanDefinitions;}// 修改BeanDefinition信息private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {    GenericBeanDefinition definition;    for (BeanDefinitionHolder holder : beanDefinitions) {      definition = (GenericBeanDefinition) holder.getBeanDefinition();      if (logger.isDebugEnabled()) {        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()           + "' and '" + definition.getBeanClassName() + "' mapperInterface");      }      // 将目标类型传入MapperFactoryBan      // 本例中为:cn.test.mybatis.dao.AppDao definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());       // 设置Bean的真实类型MapperFactoryBean      definition.setBeanClass(this.mapperFactoryBean.getClass());      definition.getPropertyValues().add("addToConfig", this.addToConfig);      boolean explicitFactoryUsed = false;      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));        explicitFactoryUsed = true;      } else if (this.sqlSessionFactory != null) {        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);        explicitFactoryUsed = true;      }      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {        if (explicitFactoryUsed) {          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");        }        definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));        explicitFactoryUsed = true;      } else if (this.sqlSessionTemplate != null) {        if (explicitFactoryUsed) {          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");        }        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);        explicitFactoryUsed = true;      }      if (!explicitFactoryUsed) {        if (logger.isDebugEnabled()) {          logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");        }        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);      }    }  }
// MapperFactoryBean@Overridepublic T getObject() throws Exception {  return getSqlSession().getMapper(this.mapperInterface);}// DefaultSqlSession@Overridepublic <T> T getMapper(Class<T> type) {  return configuration.<T>getMapper(type, this);}// Configurationpublic <T> T getMapper(Class<T> type, SqlSession sqlSession) {  return mapperRegistry.getMapper(type, sqlSession);}// MapperRegistrypublic <T> T getMapper(Class<T> type, SqlSession sqlSession) {  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);  if (mapperProxyFactory == null) {    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");  }  try {    return mapperProxyFactory.newInstance(sqlSession);  } catch (Exception e) {    throw new BindingException("Error getting mapper instance. Cause: " + e, e);  }}// MapperProxyFactory// 使用动态代理public T newInstance(SqlSession sqlSession) {  final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);  return newInstance(mapperProxy);}protected T newInstance(MapperProxy<T> mapperProxy) {  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}
原创粉丝点击