您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關MyBatis Plus執行流程與插件機制原理的深入淺析,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
MyBatis Plus插件
MyBatis Plus提供了分頁插件PaginationInterceptor、執行分析插件SqlExplainInterceptor、性能分析插件PerformanceInterceptor以及樂觀鎖插件OptimisticLockerInterceptor。
Mybatis 通過插件 (Interceptor) 可以做到攔截四大對象相關方法的執行 ,根據需求完成相關數據的動態改變。
四大對象是:
四大對象的每個對象在創建時,都會執行interceptorChain.pluginAll(),會經過每個插件對象的 plugin()方法,目的是為當前的四大對象創建代理。代理對象就可以攔截到四大對象相關方法的執行,因為要執行四大對象的方法需要經過代理 。
① xml下插件的配置
如下所示:
<bean id="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> <!-- 注入全局MP策略配置 --><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> <!-- <property name="maxTime" value="5"></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 用于注解實體字段,必須要有) :
【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) { 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<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); }
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<Class> 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<Class>(); 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; }
上面代碼主要做了如下事情:
也就是說,在MybatisSqlSessionFactoryBean.afterPropertiesSet方法執行結束后,SqlSessionFactory、SqlSessionTemplate、Configuration等都已存在!
【3】查詢執行流程分析
示例代碼如下:
List<Employee > emps = employeeMapper.selectPage(page, null);
如下圖所示,此時我們獲取到的employeeMapper其實是個代理對象:
關于MyBatis Plus執行流程與插件機制原理的深入淺析就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。