通过上一篇博客《深入 Mybatis 插件机制》了解了插件原理,并且 Mybatis 所有插件都保存在 ConfigurationinterceptorChain 属性中,在 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 注册了 SqlSessionFactorySqlSessionTemplate 等几个重要 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 中, 最后再调用 SqlSessionFactoryBeangetObject() 生成 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();
}

SqlSessionFactoryBeangetObject() 函数内部会调用 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.xmlplugins 标签中指定插件,例如:

<?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

SqlSessionFactoryBeanbuildSqlSessionFactory() 方法构建 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 插件注册方式相关推荐

  1. 对应到对象 数据库驼峰_从源码角度理解Mybatis字段映射(一) - 驼峰式命名

    凯伦说,公众号ID: KailunTalk,努力写出最优质的技术文章,欢迎关注探讨. 在上篇博客-[JDBC] 处理ResultSet,构建Java对象中提到,我们需要分析Mybatis在转换Resu ...

  2. 从源码角度分析 Mybatis 工作原理

    作者:vivo互联网服务器团队-Zhang Peng 一.MyBatis 完整示例 这里,我将以一个入门级的示例来演示 MyBatis 是如何工作的. 注:本文后面章节中的原理.源码部分也将基于这个示 ...

  3. SpringCloud组件 源码剖析:Eureka服务注册方式流程全面分析

    在SpringCloud组件:Eureka服务注册是采用主机名还是IP地址?文章中我们讲到了服务注册的几种注册方式,那么这几种注册方式的源码是怎么实现的呢?我们带着这一个疑问来阅读本章内容能够让你更深 ...

  4. Mybatis底层原理学习(二):从源码角度分析一次查询操作过程

    在阅读这篇文章之前,建议先阅读一下我之前写的两篇文章,对理解这篇文章很有帮助,特别是Mybatis新手: 写给mybatis小白的入门指南 mybatis底层原理学习(一):SqlSessionFac ...

  5. 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  6. 从源码角度解析Android中APK安装过程

    从源码角度解析Android中APK的安装过程 1. Android中APK简介 Android应用Apk的安装有如下四种方式: 1.1 系统应用安装 没有安装界面,在开机时自动完成 1.2 网络下载 ...

  7. 源码通透-mybatis源码分析以及整合spring过程

    源码通透-mybatis源码分析以及整合spring过程 mybatis源码分析版本:mybaits3 (3.5.0-SNAPSHOT) mybatis源码下载地址:https://github.co ...

  8. 从源码角度看Android系统SystemServer进程启动过程

    SystemServer进程是由Zygote进程fork生成,进程名为system_server,主要用于创建系统服务. 备注:本文将结合Android8.0的源码看SystemServer进程的启动 ...

  9. 从源码角度看Android系统Zygote进程启动过程

    在Android系统中,DVM.ART.应用程序进程和SystemServer进程都是由Zygote进程创建的,因此Zygote又称为"孵化器".它是通过fork的形式来创建应用程 ...

  10. Android -- 带你从源码角度领悟Dagger2入门到放弃(一)

    1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了. 2,接入项目 在项目的G ...

最新文章

  1. 李开复预测:未来20年 AI将深刻影响五大产业
  2. 中间件应用程序错误 修复方案_Qtum Neutron:新一代区块链虚拟机中间件会给Qtum带来哪些新功能?...
  3. 一则关于运算符的小例
  4. 7.2 PCA-机器学习笔记-斯坦福吴恩达教授
  5. 一维正态分布、二维正态分布的matlab实现
  6. 宏BOOST_TEST_ALL_WITH的用法实例
  7. 利用dispatch_once创建单例
  8. mysql分片建表语句_Mysql元数据如何生成Hive建表语句注释脚本详解
  9. python图形化编程工具哪个好_mPython(图形化编程软件) V0.5.0 官方版
  10. 学习生物信息的系列书籍
  11. 首页 、引导页、版本
  12. pip导包CalledProcessError: Command ‘(‘lsb_release‘, ‘-a‘)‘异常处理
  13. suse 网卡 bond
  14. 【Linux】五分钟搞定 Linux 文档全部知识,就看这篇文章
  15. idea 配置SVN
  16. 美著名电话飞客去世 可凭口哨打免费电话(图)
  17. 流量清洗是什么意思?
  18. Coap在Andorid中的简单应用
  19. DNA序列编码中Hairpin的定义和计算
  20. php实现pdhf2加密,搞了一天半了,可恶的加密代码解决方案

热门文章

  1. 应急响应样本分析查杀集合
  2. Javaweb面试题(一)———更新中
  3. 数据库基础:select基本查询语句
  4. js 封装websocket (包括心跳检测,断线重连等)
  5. c语言色环电阻计算器源代码,色环电阻计算器最新版
  6. 阿里部署edas,采用hsf框架,启动项目时无法启动的问题。
  7. 习题4-11 兔子繁衍问题
  8. html5打印样式没有加载,cad打印样式不见了(cad没有打印样式表)
  9. mac下的svn工具——Versions使用详解
  10. Java, 基础(面试题)总结(分享-交流)