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

共 20122字,需浏览 41分钟

 ·

2020-09-10 02:03

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

66套java从入门到精通实战课程分享 

【1】MyBatis Plus插件

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

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

四大对象是:

  • Executor

  • StatementHandler

  • ParameterHandler

  • ResultSetHandler

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

① xml下插件的配置

如下所示:

"sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean"><property name="dataSource" ref="dataSource">property><property name="configLocation" value="classpath:mybatis-config.xml">property><property name="typeAliasesPackage" value="com.jane.mp.beans">property><property name="globalConfig" ref="globalConfiguration">property><property name="plugins"><list><bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor">bean><bean class="com.baomidou.mybatisplus.plugins.SqlExplainInterceptor"><property name="stopProceed" value="true">property>bean><bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor"><property name="format" value="true">property>bean><bean class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor">bean>list>property>bean>

② 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;    }  }//...暂时忽略其他代码
//如果上面没有获取到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;}

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

③ 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); }}//...
// Register bean as disposable.try { registerDisposableBeanIfNecessary(beanName, bean, mbd);}//...return exposedObject;}

④ 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<Object>() {      @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); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); }
if (mbd == null || !mbd.isSynthetic()) { // //调用bean后置处理器的后置方法 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean;}

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<Object>() { @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_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;}

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

  • 获取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其实是个代理对象:


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:

https://janus.blog.csdn.net/article/details/108441600





粉丝福利:108本java从入门到大神精选电子书领取

???

?长按上方锋哥微信二维码 2 秒
备注「1234」即可获取资料以及
可以进入java1234官方微信群



感谢点赞支持下哈 

浏览 40
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报