1. 简介

一般情况下,开源框架会提供插件或者其它形式的扩展点,供开发者自行扩展。这样可以增加框架的灵活性,开发者可以结合实际需求,对框架进行扩展。Mybatis可以基于插件实现分页、分表、监控等功能。由于插件与业务无关,业务也无法感知插件的存在。因此可以无感植入插件,在无形中增强功能。

2. Mybatis插件介绍

  1. Mybatis支持插件对四大核心对象进行拦截
    1. Executor:执行器,update、query、commit、rollback等方法。
    2. StatementHandler:sql语法构建器,prepare、parameterize、batch、update、query等方法。
    3. ParameterHandler:参数处理器,getParameterObject、setParameterObject等方法。
    4. ResultSetHandler:结构集处理器,handleResultsets、handleOutputParameters等方法。

7. Mybatis插件 - 图1

3. Mybatis插件原理

总结:使用jdk动态代理为四大对象创建代理对象
在四大对象创建的时候:

  1. 每个对象创建出来不是直接返回的,而是通过interceptorChain(parameterHandler)方法进行处理。
  2. 获取到所有的Interceptor(拦截器)(插件需要实现的接口),调用interceptor.plugin(target),返回target包装后的对象
  3. 插件机制,我们可以使用插件为目标创建一个代理对象,AOP我们的插件可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行
  4. 以ParameterHandler为例看插件是如何进行代理的: ```java public ParameterHandler newParameterHandler(MappedStatement mappedStatement,Object object,BoundSql,boundSql,InterceptorChain interceptorChain) { ParameterHandler parameterHandler = mappedStatement.getLang.createParameterHandler(mappedStatement,object,sql); parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); return parameterHandler; }

public Object pluginAll(Object target) { for(Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }

  1. 5. interceptorChain保存了所有的拦截器(interceptors),是mybatis初始化的时候创建的。interceptor.plugin(target)中的target可以理解为mybatis的四大对象,返回的是被重重代理后的对象。
  2. 6. 自定义插件,比如拦截Executor中的query方法:
  3. 1. 定义一个类,实现Interceptor接口
  4. ```java
  5. @Interceptors({
  6. @Signature(
  7. type = Executor.class,
  8. method = "query",
  9. args = {MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class}
  10. )
  11. })
  12. public class ExamplePlugin implements Interceptor {
  13. //省略逻辑
  14. }
  1. 将插件配置到sqlMapperConfig中
    1. <plugins>
    2. <plugin interceptor = "com.test.plugin.ExamplePlugin"></plugin>
    3. </plugins>

    4. 自定义Mybatis插件

  1. 创建一个类

    1. @Intercepts({
    2. @Signature(
    3. // 拦截哪个类
    4. type = StatementHandler.class,
    5. // 拦截哪个方法
    6. method = "prepare",
    7. // 方法的参数,如果有方法重载就看参数
    8. args = {Connection.class,Integer.class}
    9. )
    10. })
    11. public class MyPlugin implements Interceptor {
    12. /**
    13. * 拦截方法:只要目标对象的目标方法被执行时,每次都会执行intercept方法
    14. * @param invocation
    15. * @return
    16. * @throws Throwable
    17. */
    18. @Override
    19. public Object intercept(Invocation invocation) throws Throwable {
    20. System.out.println("对方法进行了增强...");
    21. // 让原方法执行
    22. return invocation.proceed();
    23. }
    24. /**
    25. * 主要为了把当前的拦截器生成代理存到拦截链中
    26. * @param target
    27. * @return
    28. */
    29. @Override
    30. public Object plugin(Object target) {
    31. // this表示当前类(也就是自定义的拦截器)
    32. return Plugin.wrap(target,this);
    33. }
    34. /**
    35. * 获取配置文件的参数
    36. * @param properties
    37. */
    38. @Override
    39. public void setProperties(Properties properties) {
    40. }
    41. }
  2. 配置文件sqlMapConfig.xml

    1. <plugins>
    2. <plugin interceptor="com.lagou.plugin.MyPlugin"/>
    3. </plugins>