从源码角度探索 Mybatis 插件注册方式
通过上一篇博客《深入 Mybatis 插件机制》了解了插件原理,并且 Mybatis 所有插件都保存在 Configuration
的 interceptorChain
属性中,在 Configuration
中提供了注册插件方法 addInterceptor(Interceptor interceptor)
将插件添加到 interceptorChain
,那用户怎么向 Configuration
注册插件呢?
结合源码,本文分享了四种注册 Mybatis 插件方式:
- 直接注册到 Spring IOC 容器;
- 通过定制化器
ConfigurationCustomizer
定制Configuration
; - 通过 Mybatis 配置文件注册;
- 曲线救国 —— 通过注入 SqlSessionFactory 获取 Configuration 注册;
一、直接注册到 Spring IOC 容器
要向 Configuration
注册插件,就需要了解 Configuration
保存在哪里,创建时机等。
在 mybatis-spring-boot-autoconfigure
Jar 包的 spring.factories
文件中配置了 Mybatis 自动配置类 MybatisAutoConfiguration
,在 MybatisAutoConfiguration
中,Mybatis 向 Spring IOC 注册了 SqlSessionFactory
, SqlSessionTemplate
等几个重要 Bean。 SqlSessionFactory
中就包含 Configuration
实例,并且在创建 SqlSessionTemplate
最主要的工作就是初始化 Configuration
。
MybatisAutoConfiguration
构造函数:
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration {private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);private final MybatisProperties properties;private final Interceptor[] interceptors;private final ResourceLoader resourceLoader;private final DatabaseIdProvider databaseIdProvider;private final List<ConfigurationCustomizer> configurationCustomizers;public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {this.properties = properties;this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable();this.resourceLoader = resourceLoader;this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable();this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable();}// ...
}
在 MybatisAutoConfiguration
构造函数中,会把 Spring IOC 容器中的 Mybatis
插件注入到属性 interceptors
中,在创建 SqlSessionFactory
并初始化 Configuration
时会用到该属性。
在构造 SqlSessionFactory
方法 sqlSessionFactory(DataSource dataSource)
内部,会将构造函数注入的这些属性设置到 SqlSessionFactoryBean
中,其中包括将属性 interceptors
设置到SqlSessionFactoryBean
中, 最后再调用 SqlSessionFactoryBean
的 getObject()
生成 SqlSessionFactory
实例:
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factory = new SqlSessionFactoryBean();// ...if (!ObjectUtils.isEmpty(this.interceptors)) {factory.setPlugins(this.interceptors);}// ...return factory.getObject();
}
SqlSessionFactoryBean
类 getObject()
函数内部会调用 afterPropertiesSet()
函数:
@Override
public SqlSessionFactory getObject() throws Exception {if (this.sqlSessionFactory == null) {afterPropertiesSet();}return this.sqlSessionFactory;
}
afterPropertiesSet()
函数会调用 buildSqlSessionFactory()
函数构建 SqlSessionFactory
:
@Override
public void afterPropertiesSet() throws Exception {notNull(dataSource, "Property 'dataSource' is required");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() 方法内,会将刚才设置到 plugins
属性的插件注册到 Configuration
中:
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {if (!isEmpty(this.plugins)) {for (Interceptor plugin : this.plugins) {configuration.addInterceptor(plugin);if (LOGGER.isDebugEnabled()) {LOGGER.debug("Registered plugin: '" + plugin + "'");}}}
}
所以,这里提供了一种向 Configuration
注册插件方法 —— 直接将插件注册到 Spring IOC 容器中。
例如:
@Bean
public PageHelper pageHelper(){Properties properties = new Properties();properties.setProperty("helperDialect", "mysql");PageHelper pageHelper = new PageHelper();pageHelper.setProperties(properties);return pageHelper;
}
这种方式注册插件大致流程如下图:
二、通过定制化器 ConfigurationCustomizer 定制 Configuration
在 Spring 中,比较重要类一般会提供定制化器 xxxCustomizer
供用户定制,比如 Jackson 提供 Jackson2ObjectMapperBuilderCustomizer
定制 Jackson 序列化等功能。同样 Configuration
也提供定制化接口 ConfigurationCustomizer
:
public interface ConfigurationCustomizer {/*** Customize the given a {@link Configuration} object.* @param configuration the configuration object to customize*/void customize(Configuration configuration);}
ConfigurationCustomizer
接口只包含方法 customize(Configuration configuration)
,用户可以拿到 Configuration
参数,以对其实现定制操作。
在 MybatisAutoConfiguration
类的 sqlSessionFactory(DataSource dataSource)
方法内会回调所有 ConfigurationCustomizer
接口的customize(Configuration configuration)
方法:
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factory = new SqlSessionFactoryBean();// ...if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {for (ConfigurationCustomizer customizer : this.configurationCustomizers) {customizer.customize(configuration);}}// ...}
例如,注册分页插件,也可以通过如下方式注册:
@Bean
ConfigurationCustomizer mybatisConfigurationCustomizer() {return new ConfigurationCustomizer() {@Overridepublic void customize(org.apache.ibatis.session.Configuration configuration) {Properties properties = new Properties();properties.setProperty("helperDialect", "mysql");PageHelper pageHelper = new PageHelper();pageHelper.setProperties(properties);configuration.addInterceptor(pageHelper);}};
}
三、通过 Mybatis 配置文件注册
这种方式也是最常见的一种注册 Mybatis 插件方式,通过这种方式注册首先在 Mybatis 配置文件 mybatis-config.xml
的 plugins
标签中指定插件,例如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><plugins><plugin interceptor="com.github.pagehelper.PageHelper"><property name="dialect" value="mysql"/></plugin></plugins>
</configuration>
然后在 Spring 配置文件 application.xml
文件中通过参数 mybatis.config-location
告诉 Spring Mybatis 配置文件地址:
mybatis:mapper-locations: classpath:static/mybatis/*.xmlconfig-location: classpath:mybatis-config.xml
在 SqlSessionFactoryBean
类 buildSqlSessionFactory()
方法构建 SqlSessionFactory
时会解析配置文件 mybatis.config-location
,并将解析到的插件注册到 Configuration
中。
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {// ...if (this.configuration != null) {// ...} else if (this.configLocation != null) {xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);configuration = xmlConfigBuilder.getConfiguration();}// ...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();}}// ...
}
四、曲线救国 —— 通过注入 SqlSessionFactory 获取 Configuration 注册
要注册插件,首先要获取 Configuration
,而 Configuration
实例有存在于 SqlSessionFactory
,但是 SqlSessionFactory
是被 Spring IOC 容器管理的。那这不就好办了吗,可以在任何 Spring 管理的 Bean 中注入 SqlSessionFactory
实例,再获取其 configuration
,然后注册插件不就可以了吗,但是这种方式不便于管理 Mybatis 插件,因为很难知道某个插件在哪个 Bean 中注册的。
@Configuration
public class InterceptorConfig implements InitializingBean {private final SqlSessionFactory sqlSessionFactory;public InterceptorConfig(SqlSessionFactory sqlSessionFactory) {this.sqlSessionFactory = sqlSessionFactory;}@Overridepublic void afterPropertiesSet() throws Exception {org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();Properties properties = new Properties();properties.setProperty("helperDialect", "mysql");PageHelper pageHelper = new PageHelper();pageHelper.setProperties(properties);configuration.addInterceptor(pageHelper);}
}
从源码角度探索 Mybatis 插件注册方式相关推荐
- 对应到对象 数据库驼峰_从源码角度理解Mybatis字段映射(一) - 驼峰式命名
凯伦说,公众号ID: KailunTalk,努力写出最优质的技术文章,欢迎关注探讨. 在上篇博客-[JDBC] 处理ResultSet,构建Java对象中提到,我们需要分析Mybatis在转换Resu ...
- 从源码角度分析 Mybatis 工作原理
作者:vivo互联网服务器团队-Zhang Peng 一.MyBatis 完整示例 这里,我将以一个入门级的示例来演示 MyBatis 是如何工作的. 注:本文后面章节中的原理.源码部分也将基于这个示 ...
- SpringCloud组件 源码剖析:Eureka服务注册方式流程全面分析
在SpringCloud组件:Eureka服务注册是采用主机名还是IP地址?文章中我们讲到了服务注册的几种注册方式,那么这几种注册方式的源码是怎么实现的呢?我们带着这一个疑问来阅读本章内容能够让你更深 ...
- Mybatis底层原理学习(二):从源码角度分析一次查询操作过程
在阅读这篇文章之前,建议先阅读一下我之前写的两篇文章,对理解这篇文章很有帮助,特别是Mybatis新手: 写给mybatis小白的入门指南 mybatis底层原理学习(一):SqlSessionFac ...
- 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 从源码角度解析Android中APK安装过程
从源码角度解析Android中APK的安装过程 1. Android中APK简介 Android应用Apk的安装有如下四种方式: 1.1 系统应用安装 没有安装界面,在开机时自动完成 1.2 网络下载 ...
- 源码通透-mybatis源码分析以及整合spring过程
源码通透-mybatis源码分析以及整合spring过程 mybatis源码分析版本:mybaits3 (3.5.0-SNAPSHOT) mybatis源码下载地址:https://github.co ...
- 从源码角度看Android系统SystemServer进程启动过程
SystemServer进程是由Zygote进程fork生成,进程名为system_server,主要用于创建系统服务. 备注:本文将结合Android8.0的源码看SystemServer进程的启动 ...
- 从源码角度看Android系统Zygote进程启动过程
在Android系统中,DVM.ART.应用程序进程和SystemServer进程都是由Zygote进程创建的,因此Zygote又称为"孵化器".它是通过fork的形式来创建应用程 ...
- Android -- 带你从源码角度领悟Dagger2入门到放弃(一)
1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了. 2,接入项目 在项目的G ...
最新文章
- 李开复预测:未来20年 AI将深刻影响五大产业
- 中间件应用程序错误 修复方案_Qtum Neutron:新一代区块链虚拟机中间件会给Qtum带来哪些新功能?...
- 一则关于运算符的小例
- 7.2 PCA-机器学习笔记-斯坦福吴恩达教授
- 一维正态分布、二维正态分布的matlab实现
- 宏BOOST_TEST_ALL_WITH的用法实例
- 利用dispatch_once创建单例
- mysql分片建表语句_Mysql元数据如何生成Hive建表语句注释脚本详解
- python图形化编程工具哪个好_mPython(图形化编程软件) V0.5.0 官方版
- 学习生物信息的系列书籍
- 首页 、引导页、版本
- pip导包CalledProcessError: Command ‘(‘lsb_release‘, ‘-a‘)‘异常处理
- suse 网卡 bond
- 【Linux】五分钟搞定 Linux 文档全部知识,就看这篇文章
- idea 配置SVN
- 美著名电话飞客去世 可凭口哨打免费电话(图)
- 流量清洗是什么意思?
- Coap在Andorid中的简单应用
- DNA序列编码中Hairpin的定义和计算
- php实现pdhf2加密,搞了一天半了,可恶的加密代码解决方案