官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#plugins

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。

例子:

@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class ExamplePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//执行前处理
Object returnObject = invocation.proceed();
//执行后处理
return returnObject;
} @Override
public Object plugin(Object target) {
     //生成代理对象,如果不是处理的接口,就返回原对象 
return Plugin.wrap(target, this);
} @Override
public void setProperties(Properties properties) {
}
}

在mybatis-config.xml中配置

    <plugins>
<plugin interceptor="org.xuan.springmvc.comp.ExamplePlugin" />
</plugins>

分析:

在处理mybatis-config.xml配置文件的时候读取到plugins节点时,会把插件加入到Configuration#interceptorChain里面

1.使用XMLConfigBuilder#parse解析配置文件

  public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}

在parseConfiguration中处理各种配置

  private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
//处理插件
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

然后解析plugins

  private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
//添加到配置类中
configuration.addInterceptor(interceptorInstance);
}
}
}

在执行mybatis期间,使用Configuration创建提供处理的4种对象的时候会去判断是否和插件处理的接口是否一致

  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
} public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
} public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
} public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
} public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}

因为插件实现了

    @Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}

如果是插件配置的处理接口的时候会返回一个代理对象

  public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}

MyBatis插件原理的更多相关文章

  1. Mybatis插件原理分析(二)

    在上一篇中Mybatis插件原理分析(一)中我们主要介绍了一下Mybatis插件相关的几个类的源码,并对源码进行了一些解释,接下来我们通过一个简单的插件实现来对Mybatis插件的运行流程进行分析. ...

  2. mybatis 插件原理

    [传送门]:mybatis 插件原理

  3. MyBATIS插件原理第一篇——技术基础(反射和JDK动态代理)(转)

    在介绍MyBATIS插件原理前我们需要先学习一下一些基础的知识,否则我们是很难理解MyBATIS的运行原理和插件原理的. MyBATIS最主要的是反射和动态代理技术,让我们首先先熟悉它们. 1:Jav ...

  4. Mybatis框架(8)---Mybatis插件原理

    Mybatis插件原理 在实际开发过程中,我们经常使用的Mybaits插件就是分页插件了,通过分页插件我们可以在不用写count语句和limit的情况下就可以获取分页后的数据,给我们开发带来很大 的便 ...

  5. Mybatis插件原理分析(一)

    我们首先介绍一下Mybatis插件相关的几个类,并对源码进行了简单的分析. Mybatis插件相关的接口或类有:Intercept.InterceptChain.Plugin和Invocation,这 ...

  6. Mybatis 插件原理解析

    SqlSessionFactory 是 MyBatis 核心类之一,其重要功能是创建 MyBatis 的核心接口 SqlSession.MyBatis 通过 SqlSessionFactoryBuil ...

  7. Mybatis插件原理分析(三)分页插件

    在Mybatis中插件最经常使用的是作为分页插件,接下来我们通过实现Interceptor来完成一个分页插件. 虽然Mybatis也提供了分页操作,通过在sqlSession的接口函数中设置RowBo ...

  8. mybatis插件机制及分页插件原理

    MyBatis 插件原理与自定义插件: MyBatis 通过提供插件机制,让我们可以根据自己的需要去增强MyBatis 的功能.需要注意的是,如果没有完全理解MyBatis 的运行原理和插件的工作方式 ...

  9. Mybatis: 插件及分页

    Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的). Mybatis支持对Executor.StatementHa ...

随机推荐

  1. PowerShell使用Clear-Content命令删除、清空文件内容的例子

    本文介绍PowerShell中如何将文件的内容进行删除或清空,使用VBScript要先创建FSO,然后再打开文件,再修改文件:PowerShell清空文件只要一句Clear-Content. 所谓清空 ...

  2. jupyter lab 安装

    在windows下安装jupyter 特别简单 首先你需要有Anaconda or python的环境变量,这里我就不说怎么安装环境变量了,网上一大堆教程 启动黑窗口,下载jupyter pip in ...

  3. configure,make和make install关系

    linux编译安装中configure.make和make install各自的作用 ./configure是用来检测你的安装平台的目标特征的.configure根据给定的参数和系统环境会生成Make ...

  4. UML学习(四)-----状态图

    状态图主要用于描述对象具有的各种状态.状态之间的转换过程以及触发状态转换的各种事件和条件. 1.状态图的组成 1.1 状态 主要用于描述一个对象在生命周期内的一个时间段.状态图中的状态包括状态名.内部 ...

  5. C语言如何才能使用bool类型

    解决办法:加上头文件#include<stdbool.h> ,就可以直接使用类型bool = true/false; #include<stdio.h> 2 #include& ...

  6. Python实现英文文章加密传送,收到后进行解密

    思路:将I Love You这样的字符串中的每一个字符,将他的Unicode码都就进行加或减去一个特定的数, 在传送过程中,如果被截获,获取的也是一段混乱的文章,当收到这段文章后,按相同的方式对Uni ...

  7. std::string与char*之临时缓冲区

    std::string与char*之临时缓冲区 原文:https://blog.csdn.net/hsshh1988/article/details/80689330 c++文件读取流程如下: ifs ...

  8. java大框架

    本文章,列出了一些程序员需要学习的技术和知识点,有些技术和知识点没有写道,欢迎大家进行修改和补充,有些技术公司用到,大家需要先学习,有些技术和知识点过时,大家可以了解.本人笔记连接[[http://2 ...

  9. css优先级及其对应的权重

    1.选择器的优先级 !important>内联选择器(style)>id选择器>类选择器 | 属性选择器 | 伪类选择器 > 元素选择器>通配符(*) 2.选择器的权重( ...

  10. C++ DLL导出的两种方式和链接的两种方式

    第一种 导出方式 extern "C" _declspec(dllexport) int Plus(int x, int y); extern "C" _dec ...