mybatis源码配置文件解析之四:解析plugins标签
在前边的博客在分析了mybatis解析typeAliases标签,《mybatis源码配置文件解析之三:解析typeAliases标签》。下面来看解析plugins标签的过程。
一、概述
在mybatis的核心配置文件(mybatis-config.xml)文件中,有关plugins的配置如下,
<!-- 拦截器 -->
<plugins>
<plugin interceptor="cn.com.mybatis.plugins.MyInterceptor" />
</plugins>
在mybatis的plugins叫做插件,其实也可以理解为拦截器。在plugins标签中配置plugin子标签,plugin子标签可以配置多个,多个子标签是有顺序的。除了上面的配置方式在每个plugin下还可以配置property子标签,
<!-- 拦截器 -->
<plugins>
<plugin interceptor="cn.com.mybatis.plugins.MyInterceptor" />
<plugin interceptor="cn.com.mybatis.plugins.MyInterceptor2">
<property name="name" value="mybatis"/>
<property name="age" value="10"/>
</plugin>
</plugins>
上面的配置在第二个拦截器中新增了两个属性name和age,这两个属性会被解析为Properties对象。
在上面可以看到一个重要的配置是plugin标签中的interceptor属性,该属性配置的是一个拦截器类,对应该类有什么要求那,一个普通的类就可以吗,答案是就是一个普通的类,但必须满足下面的几个条件,才可以被mybatis作为插件使用,
- 实现mybatis中的Interceptor接口,并重写其中的三个方法;
- 在类上声明@Intercepts注解,该注解标明该拦截器拦截的方法,一个例子如下,
@Intercepts({@Signature(type=Executor.class,method="query",args= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
拦截的是实现了Executor接口的query方法,后面的args配置的该方法的入参。在mybatis中默认拦截以下接口的方法,
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
Executor(执行器)、ParameterHandler(参数处理器)、ResultSetHandler(结果集处理器)、StatementHandler(SQL语法构建器)在mybatis中是四个顶级接口,贯穿了mybatis执行sql的全过程,从这里可以看出mybatis的拦截器的强大。
下面看下我的拦截器MyInterceptor的源码,
package cn.com.mybatis.plugins; import java.util.Properties; import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds; @Intercepts({@Signature(type=Executor.class,method="query",args= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class MyInterceptor implements Interceptor{ /**
* incation 封装了拦截的类,方法,方法参数
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
// TODO Auto-generated method stub
//执行被拦截的类中的方法 Object o=invocation.proceed();
return o;
} /**
* 返回一个JDK代理对象
*/
@Override
public Object plugin(Object target) {
// TODO Auto-generated method stub
return Plugin.wrap(target, this);
}
/**
* 允许在配置文件中配置一些属性即
* <plugin interceptor="">
* <property name ="" value =""/>
* </plugin>
*/
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub } }
二、详述
上面,了解了plugis标签的基本配置及使用,下面看mybatis是如何解析的。
在XMLConfigBuilder类中的parseConfiguration方法
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//解析properties标签
propertiesElement(root.evalNode("properties"));
//解析settings标签,1、把<setting>标签解析为Properties对象
Properties settings = settingsAsProperties(root.evalNode("settings"));
/*2、对<settings>标签中的<setting>标签中的内容进行解析,这里解析的是<setting name="vfsImpl" value=",">
* VFS是mybatis中用来表示虚拟文件系统的一个抽象类,用来查找指定路径下的资源。上面的key为vfsImpl的value可以是VFS的具体实现,必须
* 是权限类名,多个使用逗号隔开,如果存在则设置到configuration中的vfsImpl属性中,如果存在多个,则设置到configuration中的仅是最后一个
* */
loadCustomVfs(settings);
//解析别名标签,例<typeAlias alias="user" type="cn.com.bean.User"/>
typeAliasesElement(root.evalNode("typeAliases"));
//解析插件标签
pluginElement(root.evalNode("plugins"));
//解析objectFactory标签,此标签的作用是mybatis每次创建结果对象的新实例时都会使用ObjectFactory,如果不设置
//则默认使用DefaultObjectFactory来创建,设置之后使用设置的
objectFactoryElement(root.evalNode("objectFactory"));
//解析objectWrapperFactory标签
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析reflectorFactory标签
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//解析environments标签
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
//解析<mappers>标签
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
从上面的代码中,我们找到下面这行解析plugins标签的代码,
//解析插件标签
pluginElement(root.evalNode("plugins"));
上面折行代码即在解析配置文件中的plugis标签,其定义如下
/**
* 解析<plugin>标签
* @param parent
* @throws Exception
*/
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//1、获得interceptor属性
String interceptor = child.getStringAttribute("interceptor");
//2、把plugin标签下的property标签的内容解析为Properties对象
Properties properties = child.getChildrenAsProperties();
//3、使用配置的interceptor生成一个实例,配置的interceptor需要实现interceptor接口
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
//4、执行interceptor的setProperties方法,把配置的properties标签中的值设置到setProperties中
interceptorInstance.setProperties(properties);
//5、添加interceptor
configuration.addInterceptor(interceptorInstance);
}
}
}
1、解析标签
从上面的代码中可以看到在循环plugin标签,取得interceptor配置的类的全限类名,然后获得plugin标签下的property标签的内容,调用getChildrenAsProperties方法,
public Properties getChildrenAsProperties() {
Properties properties = new Properties();
for (XNode child : getChildren()) {
String name = child.getStringAttribute("name");
String value = child.getStringAttribute("value");
if (name != null && value != null) {
properties.setProperty(name, value);
}
}
return properties;
}
解析完property标签后,下面会生成interceptor实例。
2、生成interceptor实例
上面解析完plugin标签的内容后会生成interceptor实例,且调用其setProperty方法,
//使用配置的interceptor生成一个实例,配置的interceptor需要实现interceptor接口
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
//执行interceptor的setProperties方法,把配置的properties标签中的值设置到setProperties中
interceptorInstance.setProperties(properties);
从上面的代码可以看到把配置的interceptor全限类名解析为一个Class对象,然后调用其默认的构造方法获得一个实例,然后调用了其setProperties方法,即把在标签中配置的property属性值注入到interceptor实例中,在我的例子中即调用下面的方法,
/**
* 允许在配置文件中配置一些属性即
* <plugin interceptor="">
* <property name ="" value =""/>
* </plugin>
*/
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
this.properties=properties;
}
这样就可以把标签中的内容转化到interceptor实例中的属性。
3、添加到configuration中
经过上面的两个步骤的分析,拦截器已经从配置文件中的配置转化为了一个实例,下面就是要放入核心配置类configuration中,
configuration.addInterceptor(interceptorInstance);
看addInterceptor方法,该方法是configuration类中的方法,
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
调用了interceptorChain的addInterceptor方法,interceptorChain使用下面的方式进行初始化,
//拦截器链
protected final InterceptorChain interceptorChain = new InterceptorChain();
其addInterceptor方法如下,

可以看到最终放到了interceptors中,这是一个ArrayList。
最终完成了解析plugins标签并把拦截器实例放到configuration中的过程。
三、总结
本文分析了mybatis中plugins标签的解析过程,该标签的解析过程相对简单,重点是拦截器背后的原理,是如何起到拦截作用,待下次分析。
有不正之处,欢迎指正,感谢!
mybatis源码配置文件解析之四:解析plugins标签的更多相关文章
- mybatis源码配置文件解析之五:解析mappers标签
在上篇博客中分析了plugins标签,<mybatis源码配置文件解析之四:解析plugins标签>,了解了其使用方式及背后的原理.现在来分析<mappers>标签. 一.概述 ...
- mybatis源码配置文件解析之二:解析settings标签
在前边的博客中分析了mybatis解析properties标签,<mybatis源码配置文件解析之一:解析properties标签>.下面来看解析settings标签的过程. 一.概述 在 ...
- mybatis源码配置文件解析之三:解析typeAliases标签
在前边的博客在分析了mybatis解析settings标签,<mybatis源码配置文件解析之二:解析settings标签>.下面来看解析typeAliases标签的过程. 一.概述 在m ...
- mybatis源码配置文件解析之五:解析mappers标签(解析XML映射文件)
在上篇文章中分析了mybatis解析<mappers>标签,<mybatis源码配置文件解析之五:解析mappers标签>重点分析了如何解析<mappers>标签中 ...
- MyBatis 源码分析 - 映射文件解析过程
1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
- Spring mybatis源码篇章-MybatisDAO文件解析(一)
前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-SqlSessionFactory 加载指定的mybatis主文件 Mybatis模板文件,其中的属性 ...
- Spring mybatis源码篇章-MybatisDAO文件解析(二)
前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...
- mybatis源码配置文件解析之一:解析properties标签
mybatis作为日常开发的常用ORM框架,在开发中起着很重要的作用,了解其源码对日常的开发有很大的帮助.源码版本为:3-3.4.x,可执行到github进行下载. 从这篇文章开始逐一分析mybati ...
- mybatis源码配置文件解析之五:解析mappers标签流程图
前面几篇博客分析了mybatis解析mappers标签的过程,主要分为解析package和mapper子标签.补充一张解析的总体过程流程图,画的不好,多多谅解,感谢.
随机推荐
- 如何从0创建一个react项目
1. 确保本机电脑安装了yarn和node: 2. 在需要安装的文件夹目录下输入:create-react-app +(项目名称): PS:上图使用的软件为webStorm 3. 此时一个简单的re ...
- HTML静态页面项目:英雄联盟官网网站 的实现
效果: 源码与素材:链接: https://pan.baidu.com/s/1OuJd1lfEV7mrnf0I6FXm4A 提取码: 5c6j 复制这段内容后打开百度网盘手机App,操作更方便哦
- css 段落文字换行问题
项目中遇到的一个小问题,以前没有注意到: 超链接超出父级元素,想着给a标签加宽度但是没有效果... 后来发现两个很好用的css属性 1.word-wrap 用来控制换行 取值: (1)normal ...
- Java实现 LeetCode 45 跳跃游戏 II(二)
45. 跳跃游戏 II 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 你的目标是使用最少的跳跃次数到达数组的最后一个位置. 示例: 输入: [ ...
- Java中BigDecimal类的常用方法
1.简介 BigDecimal类位于java.math.BigDecimal包下.使用此类可以完成大的小数操作,而且也可以使用此类进行精确的四舍五入,这一点在开发中经常使用. 对于不需要任何准确计算精 ...
- java算法集训结果填空题练习1
1 空瓶换汽水 浪费可耻,节约光荣.饮料店节日搞活动:不用付费,用3个某饮料的空瓶就可以换一瓶该饮料.刚好小明前两天买了2瓶该饮料喝完了,瓶子还在.他耍了个小聪明,向老板借了一个空瓶,凑成3个,换了一 ...
- bfs算法总结
BFS(广度优先搜索) 类似于树的按层遍历 常用实现方法:队列 模板: 注意: 只能用来求解无权图的最短路径问题 队列:用来存储每一层便利得到的节点 标记:对于遍历过的结点,应将其标记,以防重复访问 ...
- iOS-AutoLayout中动画使用的细节 和 iOS layout机制
在Main.storyboard拖入一个UIView,随便设置一个背景色, 使用autolayout 为紫色的view添加约束 :(0,0,100,100) , 为该view添加动画代码如下: #i ...
- 构建自己的jar包上传至Mvaen中央仓库和版本更新
构建自己的jar包上传至Mvaen中央仓库和版本更新 一直羡慕别人制造轮子,开源项目,供别人使用:我也想这样,可以自己才疏学浅,本次就将自己写小工具上传到Maven的中央仓库. 一步一步详细教程演示如 ...
- 基于JQuery的简单富文本编辑器
利用jQuery实现最简单的编辑器 我试了很多种方法,目前最快捷能够实现及其简单的编辑可以使用 document.execCommand("ForeColor", "fa ...