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类层次结构,摘自网上
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创建时序图,来源网上:
整个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. } } }
- XMLConfigBuilder.parse解析xml文件的
configuration
节点下settings
、properties
、typeAliases
、plutins
、enviroments
、typeHandlers
、mappers
等子节点;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); } }
- 将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对象也会被销毁。
- SqlSession.close;
- SqlSession.clearCache;
- SqlSession中执行任何一个update操作(update, delete,insert);
注意
根据一级缓存的特性,应该一下几点:
- 对于数据变化频率很大,并且需要高时效准确性的数据要求,使用SqlSession查询的时候,要控制好SqlSession的生存时间,SqlSession的生存时间越长,其中缓存的数据有可能就越久,从而造成和真实数据库的误差;
- 对于只执行、并且频繁执行大范围的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;}
二级缓存使用前提:
- MyBatis支持二级缓存的总开关:cacheEnabled=true
- 该select语句所在Mapper,配置或节点
- 该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);}
- Mybatis SqlSessionTemplate 源码解析
- mybatis源码初步解析
- MyBatis源码解析一
- Mybatis SqlSessionTemplate 源码解析
- Mybatis SqlSessionTemplate 源码解析
- mybatis源码解析
- Mybatis源码解析 KeyGenerator
- mybatis源码解析
- Mybatis SqlSessionTemplate 源码解析
- Mybatis SqlSessionTemplate 源码解析
- mybatis源码解析
- Mybatis源码解析
- MyBatis源码解析
- mybatis源码解析(4)
- Mybatis-spring整合源码解析
- MyBatis Spring 集成源码解析
- mybatis源码解析------Configuration类
- Mybatis 源码解析 -事务模块
- KMP算法的概述
- Android studio打包错误com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536
- 原win7系统迁移到SSD固态硬盘
- JavaEE——servlet详解
- HDFS高级操作命令和工具
- MyBatis源码解析
- LDR指令和LDR伪指令区别
- PHP jpgraph库的配置及生成统计图表:折线图、柱状图、饼状图等
- EventBus 源码分析
- LockSupport
- Verilog学习笔记--时延
- HTML5权威指南笔记:35-使用canvas元素(1)
- 记录学习过程中碰到的json对象数组字符串转成list方法
- 无人驾驶汽车系统入门(四)——反馈控制入门,PID控制