本文,我们来分享 MyBatis 的插件模块,对应 plugin 包。如下图所示:
在 《精尽 MyBatis 源码解析 —— 项目结构一览》 中,简单介绍了这个模块如下:
Mybatis 自身的功能虽然强大,但是并不能完美切合所有的应用场景,因此 MyBatis 提供了插件接口,我们可以通过添加用户自定义插件的方式对 MyBatis 进行扩展。用户自定义插件也可以改变 Mybatis 的默认行为,例如,我们可以拦截 SQL 语句并对其进行重写。
由于用户自定义插件会影响 MyBatis 的核心行为,在使用自定义插件之前,开发人员需要了解 MyBatis 内部的原理,这样才能编写出安全、高效的插件。
本文涉及的类如下图所示:
下面,我们逐个类来瞅瞅。
org.apache.ibatis.plugin.Interceptor ,拦截器接口。代码如下:
// Interceptor.java
|
我们来看看 org.apache.ibatis.plugin.PluginTest ,提供了 AlwaysMapPlugin 示例。代码如下:
|
<1> 处,通过 @Intercepts 和 @Signature 注解,定义了需要拦截的方法为 Map 类型、方法为 "get" 方法,方法参数为 Object.class 。详细解析,见 TODO。<2> 处,在实现方法 #plugin(Object target) 方法内部,他调用 Plugin#wrap(Object target, Interceptor interceptor) 方法,执行代理对象的创建。详细解析,见 TODO。<3> 处,在实现方法 #setProperties(Properties properties) 方法内部,暂未做任何实现。此处可以实现,若 AlwaysMapPlugin 有属性,可以从 properties 获取一些需要的属性值。<4> 处,在实现方法 #intercept(Invocation invocation) 方法,直接返回 "Always" 字符串。也就是说,当所有的 target 类型为 Map 类型,并且调用 Map#get(Object) 方法时,返回的都是 "Always" 。这个示例,胖友可以跑下 PluginTest 单元测试里的方法,可以更加好理解。
org.apache.ibatis.plugin.Invocation ,方法调用信息,作为 Interceptor#intercept(Invocation invocation) 的方法参数。代码如下:
// Invocation.java
|
org.apache.ibatis.plugin.InterceptorChain ,拦截器 Interceptor 链。
// InterceptorChain.java
|
#addInterceptor(Interceptor interceptor) 方法,添加拦截器。代码如下:
// InterceptorChain.java
|
该方法在 Configuration 的 #pluginElement(XNode parent) 方法中被调用,代码如下:
// XMLConfigBuilder.java
|
<1> 处,创建 Interceptor 对象,并调用 Interceptor#setProperties(properties) 方法,设置拦截器的属性。<2> 处,调用 Configuration#addInterceptor(interceptorInstance) 方法,添加到 Configuration.interceptorChain 中。代码如下:
// Configuration.java
|
#pluginAll(Object target) 方法,应用所有拦截器到指定目标对象。代码如下:
// InterceptorChain.java
|
该方法被 Configuration 的如下四处方法中调用:
org.apache.ibatis.plugin.@Intercepts ,拦截器注解。代码如下:
// Intercepts.java
|
org.apache.ibatis.plugin.@Signature ,方法签名的注解。代码如下:
// Signature.java
|
org.apache.ibatis.plugin.Plugin ,实现 InvocationHandler 接口,插件类,一方面提供创建动态代理对象的方法,另一方面实现对指定类的指定方法的拦截处理。
注意,Plugin 是 MyBatis 插件体系的核心类。
// Plugin.java
|
#wrap(Object target, Interceptor interceptor) 静态方法,创建目标类的代理对象。代码如下:
// Plugin.java
|
<1> 处,调用 #getSignatureMap(Interceptor interceptor) 方法,获得拦截的方法映射。代码如下:
// Plugin.java
|
@Intercepts 和 @Signature 注解。<2> 处,调用 #getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) 方法,获得目标类的接口集合。代码如下:
// Plugin.java
|
@Signature 注解的 type 属性,必须是接口。<3.1> 处,情况一,若有接口,则创建目标对象的 JDK Proxy 对象。<3.2> 处,情况二,若无接口,则返回原始的目标对象。
// Plugin.java
|
原文:https://www.cnblogs.com/siye1989/p/11624505.html