MyBatis Plus插件机制与执行流程原理分析详解
MyBatisPlus插件
MyBatisPlus提供了分页插件PaginationInterceptor、执行分析插件SqlExplainInterceptor、性能分析插件PerformanceInterceptor以及乐观锁插件OptimisticLockerInterceptor。
Mybatis通过插件(Interceptor)可以做到拦截四大对象相关方法的执行,根据需求完成相关数据的动态改变。
四大对象是:
- Executor
- StatementHandler
- ParameterHandler
- ResultSetHandler
四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll(),会经过每个插件对象的plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理。
①xml下插件的配置
如下所示:
②springboot下注册插件
这里以分页插件为例:
@BeanpublicPaginationInterceptorpaginationInterceptor() { PaginationInterceptorpaginationInterceptor=newPaginationInterceptor(); //设置请求的页面大于最大页后操作,true调回到首页,false继续请求默认false //paginationInterceptor.setOverflow(false); //设置最大单页限制数量,默认500条,-1不受限制 //paginationInterceptor.setLimit(500); //开启count的join优化,只针对部分leftjoin paginationInterceptor.setCountSqlParser(newJsqlParserCountOptimize(true)); returnpaginationInterceptor; }
③SqlExplainInterceptor
SQL执行分析拦截器,全类名是com.baomidou.mybatisplus.plugins.SqlExplainInterceptor,只支持MySQL5.6.3以上版本。
该插件的作用是分析DELETEUPDATE语句,防止小白或者恶意进行DELETEUPDATE全表操作,不建议在生产环境中使用会造成性能下降,
在插件的底层通过SQL语句分析命令Explain分析当前的SQL语句,根据结果集中的Extra列来断定当前是否全表操作。
④性能分析插件
性能分析拦截器,全类名是com.baomidou.mybatisplus.plugins.PerformanceInterceptor,用于输出每条SQL语句及其执行时间。SQL性能执行分析,开发环境使用超过指定时间,停止运行。
⑤乐观锁插件
全类名是com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor。如果想实现如下需求:当要更新一条记录的时候,希望这条记录没有被别人更新,就可以使用该插件进行判断。
乐观锁的实现原理(@Version用于注解实体字段,必须要有):
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时,setversion=yourVersion+1whereversion=yourVersion
- 如果version不对,就更新失败
【2】获取sqlSessionFactoryBean
如下图所示,在系统启动时会初始化定义的bean。DefaultListableBeanFactory.preInstantiateSingletons方法中会从beanDefinitionNames中获取beanname然后依次创建。
这里可以看到RootBeanDefinition是com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean。
①获取bean的过程中bean属性
如下所示,在getBean过程中可以看到bean的属性:
②createBean
第一次获取bean的时候会走到AbstractAutowireCapableBeanFactory.createBean进行bean的创建。
/**创建一个bean实例,为bean实例设置属性值,调用post-processors-bean后置处理器*/@OverrideprotectedObjectcreateBean(StringbeanName,RootBeanDefinitionmbd,Object[]args) throwsBeanCreationException {//...暂时忽略其他代码 try{//这里会首先触发BeanPostProcessors,如果这里能获取到bean则直接返回,不再走doCreateBean Objectbean=resolveBeforeInstantiation(beanName,mbdToUse); if(bean!=null) { returnbean; }}//...暂时忽略其他代码
在AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation中就会分别执行bean后置处理器的前置和后置方法。
protectedObjectresolveBeforeInstantiation(StringbeanName,RootBeanDefinitionmbd) {Objectbean=null; if(!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { //Makesurebeanclassisactuallyresolvedatthispoint. 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); } returnbean; }
执行后置处理器的前置方法如下所示:
③doCreateBean
AbstractAutowireCapableBeanFactory.doCreateBean是创建bean的核心方法,这会为bean属性赋值并会触发bean后置处理器、InitializingBean以及自定init方法等。
protectedObjectdoCreateBean(finalStringbeanName,finalRootBeanDefinitionmbd,finalObject[]args)throwsBeanCreationException { //Instantiatethebean.--实例化beanBeanWrapperinstanceWrapper=null;if(mbd.isSingleton()){instanceWrapper=this.factoryBeanInstanceCache.remove(beanName);}if(instanceWrapper==null) {//获取bean的包装对象-这里很重要instanceWrapper=createBeanInstance(beanName,mbd,args);}finalObjectbean=(instanceWrapper!=null?instanceWrapper.getWrappedInstance():null);Class>beanType=(instanceWrapper!=null?instanceWrapper.getWrappedClass():null);mbd.resolvedTargetType=beanType; //Allowpost-processorstomodifythemergedbeandefinition.synchronized(mbd.postProcessingLock) {if(!mbd.postProcessed) {try{//调用MergedBeanDefinitionPostProcessors的postProcessMergedBeanDefinition方法applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName); }catch(Throwableex) { thrownewBeanCreationException(mbd.getResourceDescription(),beanName,"Post-processingofmergedbeandefinitionfailed",ex); }mbd.postProcessed=true;}}//...//这里暂时忽略其他代码 //Initializethebeaninstance.--初始化bean实例ObjectexposedObject=bean;try{//如下方法很重要populateBean(beanName,mbd,instanceWrapper);if(exposedObject!=null) { exposedObject=initializeBean(beanName,exposedObject,mbd); }}//...
④populateBean
顾名思义,为bean实例属性赋值。
AbstractAutowireCapableBeanFactory.populateBean
protectedvoidpopulateBean(StringbeanName,RootBeanDefinitionmbd,BeanWrapperbw){//获取属性值列表PropertyValuespvs=mbd.getPropertyValues(); //...该种符号表示暂时忽略其他代码 //在为bean属性赋值前,给InstantiationAwareBeanPostProcessors机会修改bean的状态//应用场景如支持字段注入booleancontinueWithPropertyPopulation=true; //循环调用InstantiationAwareBeanPostProcessors的postProcessAfterInstantiation方法if(!mbd.isSynthetic()&&hasInstantiationAwareBeanPostProcessors()){for(BeanPostProcessorbp:getBeanPostProcessors()){if(bpinstanceofInstantiationAwareBeanPostProcessor){InstantiationAwareBeanPostProcessoribp=(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){MutablePropertyValuesnewPvs=newMutablePropertyValues(pvs); //Addpropertyvaluesbasedonautowirebynameifapplicable.if(mbd.getResolvedAutowireMode()==RootBeanDefinition.AUTOWIRE_BY_NAME){autowireByName(beanName,mbd,bw,newPvs);} //Addpropertyvaluesbasedonautowirebytypeifapplicable.if(mbd.getResolvedAutowireMode()==RootBeanDefinition.AUTOWIRE_BY_TYPE){autowireByType(beanName,mbd,bw,newPvs);} pvs=newPvs;}booleanhasInstAwareBpps=hasInstantiationAwareBeanPostProcessors();booleanneedsDepCheck=(mbd.getDependencyCheck()!=RootBeanDefinition.DEPENDENCY_CHECK_NONE);//循环调用InstantiationAwareBeanPostProcessors的postProcessPropertyValues方法if(hasInstAwareBpps||needsDepCheck){PropertyDescriptor[]filteredPds=filterPropertyDescriptorsForDependencyCheck(bw,mbd.allowCaching);if(hasInstAwareBpps){for(BeanPostProcessorbp:getBeanPostProcessors()){if(bpinstanceofInstantiationAwareBeanPostProcessor){InstantiationAwareBeanPostProcessoribp=(InstantiationAwareBeanPostProcessor)bp;pvs=ibp.postProcessPropertyValues(pvs,filteredPds,bw.getWrappedInstance(),beanName);if(pvs==null){return;}}}}if(needsDepCheck){checkDependencies(beanName,mbd,filteredPds,pvs);}}//在这里为属性赋值,会进行类型转换,这里注意关键词deepcopy//如果是引用类型且bean没有存在,则会进行bean的创建过程applyPropertyValues(beanName,mbd,bw,pvs);}
如下图所示在创建sqlSessionFactoryBean过程中会创建其属性globalConfiguration对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性PaginationInterceptor对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性SqlExplainInterceptor对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性PerformanceInterceptor对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性OptimisticLockerInterceptor对象:
⑤initializeBean
AbstractAutowireCapableBeanFactory.initializeBean源码如下:
protectedObjectinitializeBean(finalStringbeanName,finalObjectbean,RootBeanDefinitionmbd) { if(System.getSecurityManager()!=null) { AccessController.doPrivileged(newPrivilegedAction
AbstractAutowireCapableBeanFactory.invokeInitMethods方法源码如下:
protectedvoidinvokeInitMethods(StringbeanName,finalObjectbean,RootBeanDefinitionmbd) throwsThrowable{ booleanisInitializingBean=(beaninstanceofInitializingBean); if(isInitializingBean&&(mbd==null||!mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if(logger.isDebugEnabled()) { logger.debug("InvokingafterPropertiesSet()onbeanwithname'"+beanName+"'"); } //调用InitializingBean.afterPropertiesSet if(System.getSecurityManager()!=null) { try{ AccessController.doPrivileged(newPrivilegedExceptionAction
如下图所示,MybatisSqlSessionFactoryBean同样实现了InitializingBean接口。那么我们就需要注意其afterPropertiesSet方法了。
⑥MybatisSqlSessionFactoryBean.afterPropertiesSet
如下所示,代码很简短只是创建了sqlSessionFactory。
@OverridepublicvoidafterPropertiesSet()throwsException { notNull(dataSource,"Property'dataSource'isrequired");//sqlSessionFactoryBuilder在populateBean的applyPropertyValues过程中已经存在! notNull(sqlSessionFactoryBuilder,"Property'sqlSessionFactoryBuilder'isrequired"); state((configuration==null&&configLocation==null)||!(configuration!=null&&configLocation!=null), "Property'configuration'and'configLocation'cannotspecifiedwithtogether"); this.sqlSessionFactory=buildSqlSessionFactory(); }
进入afterPropertiesSet()方法前MybatisSqlSessionFactoryBean如下所示:
我们看一下sqlSessionFactory,这是一段很长的过程:
protectedSqlSessionFactorybuildSqlSessionFactory()throwsException { Configurationconfiguration;//TODO加载自定义MybatisXmlConfigBuilder MybatisXMLConfigBuilderxmlConfigBuilder=null; if(this.configuration!=null) { configuration=this.configuration; if(configuration.getVariables()==null) { configuration.setVariables(this.configurationProperties); }elseif(this.configurationProperties!=null) { configuration.getVariables().putAll(this.configurationProperties); } }elseif(this.configLocation!=null){//通常如果配置了configLocation会从这里创建MybatisXMLConfigBuilder,//其构造方法又创建了MybatisConfiguration xmlConfigBuilder=newMybatisXMLConfigBuilder(this.configLocation.getInputStream(),null,this.configurationProperties); configuration=xmlConfigBuilder.getConfiguration();}else{ if(LOGGER.isDebugEnabled()){ LOGGER.debug("Property'configuration'or'configLocation'notspecified,usingdefaultMyBatisConfiguration"); } //TODO使用自定义配置 configuration=newMybatisConfiguration(); 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){ thrownewMybatisPlusException("notfindtypeAliasesPackage:"+typeAliasesPackage); }for(StringpackageToScan:typeAliasPackageArray) { configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType==null?Object.class:typeAliasesSuperType); if(LOGGER.isDebugEnabled()) { LOGGER.debug("Scannedpackage:'"+packageToScan+"'foraliases"); } } } //TODO自定义枚举类扫描处理 if(hasLength(this.typeEnumsPackage)) { Setclasses=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) { thrownewMybatisPlusException("notfindtypeEnumsPackage:"+typeEnumsPackage); } classes=newHashSet (); for(StringtypePackage:typeEnumsPackageArray) { classes.addAll(PackageHelper.scanTypePackage(typePackage)); } }//取得类型转换注册器 TypeHandlerRegistrytypeHandlerRegistry=configuration.getTypeHandlerRegistry(); for(Classcls:classes){if(cls.isEnum()){if(IEnum.class.isAssignableFrom(cls)){ typeHandlerRegistry.register(cls.getName(),com.baomidou.mybatisplus.handlers.EnumTypeHandler.class.getCanonicalName());}else{ //使用原生EnumOrdinalTypeHandlertypeHandlerRegistry.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("Registeredtypealias:'"+typeAlias+"'"); } }} if(!isEmpty(this.plugins)) { for(Interceptorplugin:this.plugins) { configuration.addInterceptor(plugin); if(LOGGER.isDebugEnabled()) { LOGGER.debug("Registeredplugin:'"+plugin+"'"); } } } if(hasLength(this.typeHandlersPackage)) { String[]typeHandlersPackageArray=tokenizeToStringArray(this.typeHandlersPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for(StringpackageToScan:typeHandlersPackageArray) { configuration.getTypeHandlerRegistry().register(packageToScan); if(LOGGER.isDebugEnabled()){ LOGGER.debug("Scannedpackage:'"+packageToScan+"'fortypehandlers"); } } } if(!isEmpty(this.typeHandlers)) { for(TypeHandler>typeHandler:this.typeHandlers) { configuration.getTypeHandlerRegistry().register(typeHandler); if(LOGGER.isDebugEnabled()) { LOGGER.debug("Registeredtypehandler:'"+typeHandler+"'"); }}} if(this.databaseIdProvider!=null){//fix#64setdatabaseIdbeforeparsemapperxmls try{configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); }catch(SQLExceptione){ thrownewNestedIOException("FailedgettingadatabaseId",e); } } if(this.cache!=null) { configuration.addCache(this.cache); } if(xmlConfigBuilder!=null) { try { xmlConfigBuilder.parse(); if(LOGGER.isDebugEnabled()) { LOGGER.debug("Parsedconfigurationfile:'"+this.configLocation+"'"); }}catch(Exceptionex) { thrownewNestedIOException("Failedtoparseconfigresource:"+this.configLocation,ex); }finally{ ErrorContext.instance().reset(); } } if(this.transactionFactory==null) {this.transactionFactory=newSpringManagedTransactionFactory(); } configuration.setEnvironment(newEnvironment(this.environment,this.transactionFactory,this.dataSource)); //设置元数据相关 GlobalConfigUtils.setMetaData(dataSource,globalConfig);SqlSessionFactorysqlSessionFactory=this.sqlSessionFactoryBuilder.build(configuration); //TODOSqlRunner SqlRunner.FACTORY=sqlSessionFactory;//TODO缓存sqlSessionFactoryglobalConfig.setSqlSessionFactory(sqlSessionFactory);//TODO设置全局参数属性 globalConfig.signGlobalConfig(sqlSessionFactory); if(!isEmpty(this.mapperLocations)){ if(globalConfig.isRefresh()){ //TODO设置自动刷新配置减少配置 newMybatisMapperRefresh(this.mapperLocations,sqlSessionFactory,2, 2,true);} for(ResourcemapperLocation:this.mapperLocations) { if(mapperLocation==null){ continue; } try{ //TODO这里也换了噢噢噢噢 XMLMapperBuilderxmlMapperBuilder=newXMLMapperBuilder(mapperLocation.getInputStream(), configuration,mapperLocation.toString(),configuration.getSqlFragments()); xmlMapperBuilder.parse(); }catch(Exceptione){ thrownewNestedIOException("Failedtoparsemappingresource:'"+mapperLocation+"'",e); }finally{ ErrorContext.instance().reset(); } if(LOGGER.isDebugEnabled()) { LOGGER.debug("Parsedmapperfile:'"+mapperLocation+"'"); } } } else{ if(LOGGER.isDebugEnabled()) { LOGGER.debug("Property'mapperLocations'wasnotspecifiedornomatchingresourcesfound"); } } returnsqlSessionFactory; }
上面代码主要做了如下事情:
- 获取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】查询执行流程分析
示例代码如下:
Listemps=employeeMapper.selectPage(page,null);
如下图所示,此时我们获取到的employeeMapper其实是个代理对象:
总结
到此这篇关于MyBatisPlus插件机制与执行流程原理分析的文章就介绍到这了,更多相关MyBatisPlus插件机制内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。