MyBatis Plus插件机制与执行流程原理分析详解

网友投稿 454 2022-11-23


MyBatis Plus插件机制与执行流程原理分析详解

MyBatis Plus插件

MyBatis Plus提供了分页插件PaginationInterceptor、执行分析插件SqlExplainInterceptor、性能分析插件PerformanceInterceptor以及乐观锁插件OptimisticLockerInterceptor。

Mybatis 通过插件 (Interceptor) 可以做到拦截四大对象相关方法的执行 ,根据需求完成相关数据的动态改变。

四大对象是:

Executor

StatementHandler

ParameterHandler

ResultSetHandler

四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll(),bNjOlzfVOU会经过每个插件对象的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理 。

① xml下插件的配置

如下所示:

② springboot下注册插件

这里以分页插件为例:

@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;

}

③ SqlExplainInterceptor

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 用于注解实体字段,必须要有) :

取出记录时,获取当前 version

更新时,带上这个version

执行更新时,set version = yourVersion+1 where version = yourVersion

如果 version不对,就更新失败

【2】获取sqlSessionFactoryBean

如下图所示,在系统启动时会初始化定义的bean。DefaultListableBeanFactory.preInstantiateSingletons方法中会从beanDefinitionNames中获取bean name然后依次创建。

这里可以看到RootBeanDefinition是com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean。

① 获取bean的过程中bean属性

如下所示,在getBean过程中可以看到bean的属性:

② createBean

第一次获取bean的时候会走到AbstractAutowireCapableBeanFactory.createBean进行bean的创建。

/** 创建一个bean实例,为bean实例设置属性值,调用post-processors-bean后置处理器 */@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args)

throws BeanCreationException

{//...暂时忽略其他代码

try {// 这里会首先触发BeanPostProcessors ,如果这里能获取到bean则直接返回,不再走doCreateBean

Object bean = resolveBeforeInstantiation(beanName, mbdToUse);

if (bean != null)

{

return bean;

} }//...暂时忽略其他代码

在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)

{ http://

bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

}

}

}

mbd.beforeInstantiationResolved = (bean != null);

}

return bean;

}

执行后置处理器的前置方法如下所示:

③ doCreateBean

AbstractAutowireCapableBeanFactory.doCreateBean是创建bean的核心方法,这会为bean属性赋值并会触发bean后置处理器、InitializingBean以及自定init方法等。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException

{

// Instantiate the bean.--实例化beanBeanWrapper 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);

}}//...

④ populateBean

顾名思义,为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对象:

⑤ initializeBean

AbstractAutowireCapableBeanFactory.initializeBean源码如下:

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd)

{

if (System.getSecurityManager() != null)

{

AccessController.doPrivileged(new PrivilegedAction()

{

@Override public Object run()

{

invokeAwareMethods(beanName, bean);

return null;

}

}, getAccessControlContext()); } else { //调用意识/通知方法 invokeAwareMethods(beanName, bean);

}

Object wrappedBean = bean;

if (mbd == null || !mbd.isSynthetic())

{ //调用bean后置处理器的前置方法

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

} //调用初始化方法

try {

invokeInitMethods(beanName, wrappedBean, mbd);

} cabNjOlzfVOUtch (Throwable ex)

{

throw new BeanCreationException(

(mbd != null ? mbd.getResourceDescription() : null),

beanName, "Invocation of init method failed", ex);

}

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()

{

@Override public Object run() throws Exception

{

((InitializingBean) bean).afterPropertiesSet();

return null;

} }, getAccessControlContext());

}

catch (PrivilegedActionException pae)

{

throw pae.getException();

} } else { ((InitializingBean) bean).afterPropertiesSet();

} }//调用自定义初始化方法 if (mbd != null)

{

String initMethodName = mbd.getInitMethodName();

if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&

!mbd.isExternallyManagedInitMethod(initMethodName))

{

invokeCustomInitMethod(beanName, bean, mbd);

}

}}

如下图所示,MybatisSqlSessionFactoryBean同样实现了InitializingBean接口。那么我们就需要注意其afterPropertiesSet方法了。

⑥ MybatisSqlSessionFactoryBean.afterPropertiesSet

如下所示,代码很简短只是创建了sqlSessionFactory。

@Overridepublic 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_Dhttp://ELIMITERS);

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;

}

上面代码主要做了如下事情:

获取MybatisXMLConfigBuilder对象

获取Configuration对象-MybatisConfiguration

配置对象Configuration添加插件configuration.addInterceptor(plugin);

xmlConfigBuilder.parse()对configuration做进一步处理

获取SpringManagedTransactionFactory用来创建SpringManagedTransaction

获取一个DefaultSqlSessionFactory实例对象

globalConfig.setSqlSessionFactory(sqlSessionFactory)中会创建MybatisSqlSessionTemplate

解析mapperLocation对应的一个个mapper配置文件,使用助手builderAssistant的addMappedStatement方法将一个个结点添加配置对象中

其他属性设置等等

也就是说,在MybatisSqlSessionFactoryBean.afterPropertiesSet方法执行结束后,SqlSessionFactory、SqlSessionTemplate、Configuration等都已存在!

【3】查询执行流程分析

示例代码如下:

List emps = employeeMapper.selectPage(page, null);

如下图所示,此时我们获取到的employeeMapper其实是个代理对象:

总结


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:JAVA异常处理捕获与抛出原理解析
下一篇:java 使用idea将工程打成jar并创建成exe文件类型执行的方法详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~