随笔 - 1162  文章 - 0  评论 - 16  阅读 - 59万 

一、插件开发

  MyBatis在四大对象的创建过程中,都会有插件进行介入插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果。

  MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。

  默认情况下, MyBatis 允许使用插件来拦截的方法调用包括:

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

  

  在四大对象创建的时候

  1、每个创建出来的对象不是直接返回的,而是通过 interceptorChain.pluginAll(parameterHandler);

  2、获取到所有的 Interceptor(拦截器)(插件需要实现的接口),调用 Interceptor.plugin(target):返回 target 包装后的对象;

  3、插件机制,可以使用插件为目标对象创建一个代理对象:AOP(面向切面)

    我们的插件可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行:

    核心代码:

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

 

 

二、开发单个插件

  1、插件开发步骤

    ① 编写 Interceptor 的实现类;

    ② 使用 @Intercepts 注解完成插件签名;

    ③ 将写好的插件注册到全局配置文件中;

    案例:

复制代码
/**
 * 完成插件签名
 *      告诉MyBatis当前插件用用来拦截哪个对象的哪个方法
 */
@Intercepts(
        {
                @Signature(type = StatementHandler.class, method = "parameterize", args = java.sql.Statement.class)
        }
)
public class MyFirstPlugin implements Interceptor {

    /**
     * intercept:拦截
     *      拦截目标对象的目标方法的执行。
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("MyFirstPlugin...intercept:" + invocation.getMethod());

        //metaObject.setValue("parameterHandler.parameterObject", "3");

        //执行目标方法
        Object proceed = invocation.proceed();

        //返回执行后的返回值
        return proceed;
    }

    /**
     * plugin:包装
     *      包装目标对象的;包装,为目标对象创建一个代理对象
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        System.out.println("MyFirstPlugin...plugin:MyBatis将要包装的对象" + target);
        //借助Plugin的 wrap 方法来使用当前Interceptor 包装我们目标对象
        Object wrap = Plugin.wrap(target, this);
        //返回当前 target 创建的动态代理
        return wrap;
    }

    /**
     * setProperties:
     *      将插件注册时 property 属性设置进来
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息" + properties);
    }
}
复制代码

 

    配置:

复制代码
    <!--
        plugins:注册插件
    -->
    <plugins>
        <plugin interceptor="com.njf.mybatis.plugin.MyFirstPlugin">
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </plugin>
    </plugins>
复制代码

 

    测试:

复制代码
     @Test
     public void testPlugin() throws IOException {
          //1、获取 sqlSessionFactory
          SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();

          //2、获取 sqlSession 实例,能直接执行已经映射的 SQL 语句
          SqlSession sqlSession = sqlSessionFactory.openSession();

          try {
               //3、获取接口的实现类对象
               /**
                * 推荐使用
                * 会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
                */
               EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
               //System.out.println(employeeMapper);
               //System.out.println(employeeMapper.getClass());
               Employee emp = employeeMapper.getEmpById(1);
               System.out.println(emp);
          } finally {
               sqlSession.close();
          }
     }
复制代码

 

    运行结果:

 

  2、运行原理

    

 

     插件会产生目标对象的代理对象。

 

三、多个插件

  如果我们此时又声明了一个插件,如下:

复制代码
/**
 * 完成插件签名
 *      告诉MyBatis当前插件用用来拦截哪个对象的哪个方法
 */
@Intercepts(
        {
                @Signature(type = StatementHandler.class, method = "parameterize", args = java.sql.Statement.class)
        }
)
public class MySecondPlugin implements Interceptor {

    /**
     * intercept:拦截
     *      拦截目标对象的目标方法的执行。
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("MySecondPlugin...intercept:" + invocation.getMethod());
        //执行目标方法
        Object proceed = invocation.proceed();

        //返回执行后的返回值
        return proceed;
    }

    /**
     * plugin:包装
     *      包装目标对象的;包装,为目标对象创建一个代理对象
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        System.out.println("MySecondPlugin...plugin:MyBatis将要包装的对象" + target);
        //借助Plugin的 wrap 方法来使用当前Interceptor 包装我们目标对象
        Object wrap = Plugin.wrap(target, this);
        //返回当前 target 创建的动态代理
        return wrap;
    }

    /**
     * setProperties:
     *      将插件注册时 property 属性设置进来
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息" + properties);
    }
}
复制代码

 

    配置信息:

复制代码
    <!--
        plugins:注册插件
    -->
    <plugins>
        <plugin interceptor="com.njf.mybatis.plugin.MyFirstPlugin">
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </plugin>
        <plugin interceptor="com.njf.mybatis.plugin.MySecondPlugin"></plugin>
    </plugins>
复制代码

 

 

  还是测试上面的方法,运行结果:

 

  原理分析:

    

 

 

     多个插件就会产生多层代理。

 

 

  多个插件原理:

    创建动态代理的时候,是按照插件配置顺序创建层层代理对象。

    执行目标方法的时候,是按照逆向顺序执行。

    

 

 

 

四、插件原理

  1、按照插件注解声明,按照插件配置顺序调用插件plugin方法,生成被拦截对象的动态代理;

  2、多个插件依次生成目标对象的代理对象,层层包裹, 先声明的先包裹;形成代理链;

  3、目标方法执行时依次从外到内执行插件的intercept方法;

    

 

  4、多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助MyBatis提供的SystemMetaObject类来进行获取最后一层的h以及target属性的值

五、Interceptor 接口

  Intercept:拦截目标方法执行

  plugin:生成动态代理对象,可以使用MyBatis提供的Plugin类的wrap方法

  setProperties:注入插件配置时设置的属性

  

 

 

  常用代码:从代理链中分离真实被代理对象

复制代码
        //1、分离代理对象。由于会形成多次代理,所以需要通过一个while 循环分离出最终被代理对象,从而方便提取信息
        MetaObject metaObject = SystemMetaObject.forObject(target);
        while (metaObject.hasGetter("h")) {
            Object h = metaObject.getValue("h");
            metaObject = SystemMetaObject.forObject(h);
        }
        //2、获取到代理对象中包含的被代理的真实对象
        Object obj = metaObject.getValue("target");
        //3、获取被代理对象的MetaObject方便进行信息提取
        MetaObject forObject = SystemMetaObject.forObject(obj);
复制代码

 

posted on   格物致知_Tony  阅读(366)  评论(0编辑  收藏  举报
编辑推荐:
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?
历史上的今天:
2019-09-19 数组模拟队列
2019-09-19 队列
点击右上角即可分享
微信分享提示

目录导航