MyBatis Plus提供了分页插件PaginationInterceptor、执行分析插件SqlExplainInterceptor、性能分析插件PerformanceInterceptor以及乐观锁插件OptimisticLockerInterceptor。
Mybatis 通过插件 (Interceptor) 可以做到拦截四大对象相关方法的执行 ,根据需求完成相关数据的动态改变。
四大对象是:
四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll()
,会经过每个插件对象的 plugin()
方法,目的是为当前的四大对象创建代理
。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理 。
如下所示:
这里以分页插件为例:
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
SQL执行分析拦截器,全类名是com.baomidou.mybatisplus.plugins.SqlExplainInterceptor
,只支持 MySQL5.6.3以上版本。
该插件的作用是分析 DELETE UPDATE
语句 ,防止小白或者恶意进行DELETE UPDATE
全表操作,不建议在生产环境中使用会造成性能下降,
在插件的底层通过SQL语句分析命令 Explain
分析当前的 SQL语句,根据结果集中的 Extra
列来断定当前是否全表操作。
性能分析拦截器,全类名是com.baomidou.mybatisplus.plugins.PerformanceInterceptor
,用于输出每条 SQL 语句及其执行时间。SQL性能执行分析 ,开发环境使用 超过指定时间,停止运行。
全类名是com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor
。如果想实现如下需求 : 当要更新一条记录的时候,希望这条记录没有被别人更新,就可以使用该插件进行判断。
乐观锁的实现原理(@Version 用于注解实体字段,必须要有) :
如下图所示,在系统启动时会初始化定义的bean。DefaultListableBeanFactory.preInstantiateSingletons
方法中会从beanDefinitionNames中获取bean name然后依次创建。
这里可以看到RootBeanDefinition是com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean
。
第一次获取bean的时候会走到AbstractAutowireCapableBeanFactory.createBean
进行bean的创建。
/**
创建一个bean实例,为bean实例设置属性值,调用post-processors-bean后置处理器
*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
//...暂时忽略其他代码
try {
// 这里会首先触发BeanPostProcessors ,如果这里能获取到bean则直接返回,不再走doCreateBean
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
//...暂时忽略其他代码
//如果上面没有获取到bean,则会走doCreateBean--这也是创建bean的核心过程
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//...暂时忽略其他代码
return beanInstance;
}
在AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation中就会分别执行bean后置处理器的前置和后置方法。
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
AbstractAutowireCapableBeanFactory.doCreateBean是创建bean的核心方法,这会为bean属性赋值并会触发bean后置处理器、InitializingBean以及自定init方法等。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.--实例化bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//获取bean的包装对象-这里很重要
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
//调用MergedBeanDefinitionPostProcessors的postProcessMergedBeanDefinition方法
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
//...
//这里暂时忽略其他代码
// Initialize the bean instance.--初始化bean实例
Object exposedObject = bean;
try {
//如下方法很重要
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
//...
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
//...
return exposedObject;
}
顾名思义,为bean实例属性赋值。
AbstractAutowireCapableBeanFactory.populateBean
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
//获取属性值列表
PropertyValues pvs = mbd.getPropertyValues();
//...该种符号表示暂时忽略其他代码
//在为bean属性赋值前,给InstantiationAwareBeanPostProcessors 机会修改bean的状态
//应用场景如支持字段注入
boolean continueWithPropertyPopulation = true;
//循环调用InstantiationAwareBeanPostProcessors 的postProcessAfterInstantiation方法
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
//解析autowire注解字段,进行主动注入
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
//循环调用InstantiationAwareBeanPostProcessors 的postProcessPropertyValues方法
if (hasInstAwareBpps || needsDepCheck) {
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
//在这里为属性赋值,会进行类型转换,这里注意关键词deep copy
//如果是引用类型且bean没有存在,则会进行bean的创建过程
applyPropertyValues(beanName, mbd, bw, pvs);
}
如下图所示在创建sqlSessionFactoryBean过程中会创建其属性globalConfiguration对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性PaginationInterceptor
对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性SqlExplainInterceptor
对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性PerformanceInterceptor
对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性OptimisticLockerInterceptor
对象:
AbstractAutowireCapableBeanFactory.initializeBean源码如下:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction
AbstractAutowireCapableBeanFactory.invokeInitMethods方法源码如下:
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
//调用InitializingBean.afterPropertiesSet
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction
如下图所示,MybatisSqlSessionFactoryBean同样实现了InitializingBean接口。那么我们就需要注意其afterPropertiesSet方法了。
MybatisSqlSessionFactoryBean.afterPropertiesSet
如下所示,代码很简短只是创建了sqlSessionFactory。
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
//sqlSessionFactoryBuilder在populateBean的applyPropertyValues过程中已经存在!
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
进入afterPropertiesSet()
方法前MybatisSqlSessionFactoryBean
如下所示:
我们看一下sqlSessionFactory,这是一段很长的过程:
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
Configuration configuration;
// TODO 加载自定义 MybatisXmlConfigBuilder
MybatisXMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
//通常如果配置了configLocation会从这里创建MybatisXMLConfigBuilder,
//其构造方法又创建了MybatisConfiguration
xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
}
// TODO 使用自定义配置
configuration = new MybatisConfiguration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}
if (hasLength(this.typeAliasesPackage)) {
// TODO 支持自定义通配符
String[] typeAliasPackageArray;
if (typeAliasesPackage.contains("*") && !typeAliasesPackage.contains(",")
&& !typeAliasesPackage.contains(";")) {
typeAliasPackageArray = PackageHelper.convertTypeAliasesPackage(typeAliasesPackage);
} else {
typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
}
if (typeAliasPackageArray == null) {
throw new MybatisPlusException("not find typeAliasesPackage:" + typeAliasesPackage);
}
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
// TODO 自定义枚举类扫描处理
if (hasLength(this.typeEnumsPackage)) {
Set classes = null;
if (typeEnumsPackage.contains("*") && !typeEnumsPackage.contains(",")
&& !typeEnumsPackage.contains(";")) {
classes = PackageHelper.scanTypePackage(typeEnumsPackage);
} else {
String[] typeEnumsPackageArray = tokenizeToStringArray(this.typeEnumsPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
if (typeEnumsPackageArray == null) {
throw new MybatisPlusException("not find typeEnumsPackage:" + typeEnumsPackage);
}
classes = new HashSet();
for (String typePackage : typeEnumsPackageArray) {
classes.addAll(PackageHelper.scanTypePackage(typePackage));
}
}
// 取得类型转换注册器
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
for (Class cls : classes) {
if (cls.isEnum()) {
if (IEnum.class.isAssignableFrom(cls)) {
typeHandlerRegistry.register(cls.getName(), com.baomidou.mybatisplus.handlers.EnumTypeHandler.class.getCanonicalName());
} else {
// 使用原生 EnumOrdinalTypeHandler
typeHandlerRegistry.register(cls.getName(), org.apache.ibatis.type.EnumOrdinalTypeHandler.class.getCanonicalName());
}
}
}
}
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 (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
if (this.cache != null) {
configuration.addCache(this.cache);
}
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();
}
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
// 设置元数据相关
GlobalConfigUtils.setMetaData(dataSource, globalConfig);
SqlSessionFactory sqlSessionFactory = this.sqlSessionFactoryBuilder.build(configuration);
// TODO SqlRunner
SqlRunner.FACTORY = sqlSessionFactory;
// TODO 缓存 sqlSessionFactory
globalConfig.setSqlSessionFactory(sqlSessionFactory);
// TODO 设置全局参数属性
globalConfig.signGlobalConfig(sqlSessionFactory);
if (!isEmpty(this.mapperLocations)) {
if (globalConfig.isRefresh()) {
//TODO 设置自动刷新配置 减少配置
new MybatisMapperRefresh(this.mapperLocations, sqlSessionFactory, 2,
2, true);
}
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
// TODO 这里也换了噢噢噢噢
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");
}
}
return sqlSessionFactory;
}
上面代码主要做了如下事情:
configuration.addInterceptor(plugin)
;MybatisSqlSessionTemplate--SqlSession
的实现类也就是说,在MybatisSqlSessionFactoryBean.afterPropertiesSet
方法执行结束后,SqlSessionFactory、SqlSessionTemplate、Configuration等都已存在!
示例代码如下:
List emps = employeeMapper.selectPage(page, null);
如下图所示,此时我们获取到的employeeMapper其实是个代理对象:
1.1如下所示,会执行MapperProxy.invoke
方法
MapperMethod.execute中会根据SqlCommand.type进行类型判断:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
//这里根据method的返回类型执行不同方法
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
1.3 MapperMethod.executeForMany
解析参数然后调用sqlSession获取结果。
private Object executeForMany(SqlSession sqlSession, Object[] args) {
List result;
//解析参数为map结构
Object param = method.convertArgsToSqlCommandParam(args);
//判断是否拥有RowBounds参数---也就是是否需要分页
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
//如果不需要分页,则会查询所有符合条件的结果
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
//判断返回数组还是集合
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
sqlSession.selectList(command.getName(), param, rowBounds)
2.1 SqlSessionTemplate.selectList
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy. selectList(statement, parameter, rowBounds);
}
这里调用其代理对象
来执行方法,sqlSessionProxy如下所示:
如下图所示,其来到了SqlSessionTemplate$SqlSessionInterceptor.invoke
方法中(SqlSessionInterceptor是SqlSessionTemplate的内部类
):
2.2 SqlSessionInterceptor
这里会获取SqlSession 实例对象,根据SqlSession 和参数反射调用Method 获取结果。
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里获取SqlSession 实例对象
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
//反射调用方法获取结果
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
这里是真实获取一个sqlsession实例对象。SqlSessionUtils.getSqlSession如下所示:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
//断言sessionFactory不为null
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
//断言executorType不为null
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
//获取SqlSessionHolder 先尝试从holder中获取SqlSession
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
//如果获取的session不为null,则直接返回
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
//如果上面没有获取到sqlsession,则使用sessionFactory创建一个session
session = sessionFactory.openSession(executorType);
//获取一个SessionHolder,并把括号内参数赋予给SessionHolder
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
//返回获取的sqlsession
return session;
}
3.1 sessionFactory.openSession
DefaultSqlSessionFactory.openSession如下所示:
public SqlSession openSession(ExecutorType execType) {
return openSessionFromDataSource(execType, null, false);
}
继续往下跟踪openSessionFromDataSource:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//根据MybatisConfiguration获取environment :其有数据源、transactionFactory等
final Environment environment = configuration.getEnvironment();
//获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//获取事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//获取执行器,这里很重要哦会调用interceptorChain.pluginAll
final Executor executor = configuration.newExecutor(tx, execType);
//返回一个DefaultSqlSession实例
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();
}
}
获取执行器对象并第一次调用interceptorChain.pluginAll。这里来到了Configuration.newExecutor方法处:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//根据不同ExecutorType获取不同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;
}
首先会根据ExecutorType创建不同的Executor,如批处理BatchExecutor、ReuseExecutor、SimpleExecutor以及缓存处理CachingExecutor。Executor是Mybatis的核心负责SQL动态语句的生成以及查询缓存的维护。
而BatchExecutor、ReuseExecutor、SimpleExecutor实例化的构造方法都会走到BaseExecutor构造方法,可以看到执行器Executor持有了事务对象transaction 、缓存对象localCache 、出参缓存对象localOutputParameterCache 、配置对象configuration 以及包装执行器对象wrapper -其也是Executor类型。
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
此时包括的插件如下图所示:
如下代码所示,会遍历拦截器然后调用每个拦截器的plugin方法,这里target为CachingExecutor。
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
5.1PaginationInterceptor.plugin
如下,其只会对StatementHandler起作用。
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PaginationInterceptor extends SqlParserHandler implements Interceptor {
//...
}
plugin方法如下:
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}
return target;
}
5.2SqlExplainInterceptor.plugin
(目前只支持 MYSQL-5.6.3 以上版本)如下所示,其对Executor起作用。
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class SqlExplainInterceptor implements Interceptor {
//...
}
plugin方法如下:
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}
Plugin.wrap方法,会生成一个target代理对象
public static Object wrap(Object target, Interceptor interceptor) {
//获取感兴趣的类与类的方法
Map, Set> signatureMap = getSignatureMap(interceptor);
//获取目标类型,这里为class org.apache.ibatis.executor.CachingExecutor
Class> type = target.getClass();
//获取所有type.getInterfaces()且在signatureMap 中的有
Class>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
5.3PerformanceInterceptor.plugin,其对StatementHandler起作用
@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})})
public class PerformanceInterceptor implements Interceptor {
//...
}
plugin方法如下所示:
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}
return target;
}
5.4 OptimisticLockerInterceptor.plugin,其对Executor起作用
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class OptimisticLockerInterceptor implements Interceptor {
//...
}
同样调用Plugin.wrap返回一个动态代理对象。
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}
这时target如下所示:
这里会根据session, executorType, exceptionTranslator实例化一个SqlSessionHolder。
然后会依次返回,返回到③获取sqlsession处:
那么接下来就该Object result = method.invoke(sqlSession, args)
使用刚获取到的sqlsession以及参数反射调用具体方法了。这里method为public abstract java.util.List org.apache.ibatis.session.SqlSession.selectList(java.lang.String,java.lang.Object,org.apache.ibatis.session.RowBounds)
这里的statement其实就是方法的完整描述,如com.jane.mp.mapper.EmployeeMapper.selectPage
,RowBounds 是用来进行分页的。这里的executor是org.apache.ibatis.executor.CachingExecutor@433ffad1
public List 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();
}
}
代码如下所示:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
首先从signatureMap中获取method.getDeclaringClass()
对应的方法,然后判断methods 是否包含method。如果包含,则调用interceptor.intercept
方法(可以理解为插件开始起作用了);如果不包含,则直接执行method.invoke(target, args)
。
如下图所示,这里target为代理对象,interceptor为OptimisticLockerInterceptor
。
Executor会被OptimisticLockerInterceptor、SqlExplainInterceptor处理(二者都处理update方法),所以会进行两次代理。那么在这里也同样会进行两次解析。
代码如下所示:
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
首先使用ms.getBoundSql获取了boundSql对象如下:
createCacheKey(ms, parameterObject, rowBounds, boundSql)创建缓存key。这个方法很有意思,它自己并不处理而是使用了其委派对象delegate去处理(这里可以联想一下设计模式的委派模式
),如下所示:
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
}
我们再回顾下CachingExecutor的构造方法:
public class CachingExecutor implements Executor {
//委派实例对象
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
//...
}
然后我们依次返回查看获取到的CacheKey-1113517040:-1225312459:com.jane.mp.mapper.EmployeeMapper.selectPage:0:1:SELECT id AS id,last_name AS lastName,email,gender,age,version FROM tbl_employee:MybatisSqlSessionFactoryBean
这里会首先判断缓存是否存在,如果缓存存在则尝试从缓存里面根据CacheKey 获取结果,如果结果获取不到则执行查询并再次放到缓存里面;如果缓存不存在则直接直接查询。
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//获取缓存,首先从缓存里面获取,如果缓存获取不到则执行查询并放到缓存里面
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List list = (List) tcm.getObject(cache, key);
if (list == null) {
list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
这里delegate为SimpleExecutor实例对象。
SimpleExecutor会调用父类BaseExecutor的query方法:
public List 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 list;
try {
queryStack++;
list = resultHandler == null ? (List) 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;
}