九、插件

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

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

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

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed);

ParameterHandler (getParameterObject, setParameters); 
 
ResultSetHandler (handleResultSets, handleOutputParameters);

StatementHandler (prepare, parameterize, batch, update, query);

一、插件开发步骤

  • 编写插件实现Interceptor接口,并使用 @Intercepts注解完成插件签名。

/**
 * 完成插件签名:
 *        告诉MyBatis当前插件用来拦截哪个对象的哪个方法
 */
@Intercepts({
        @Signature(type= StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class FirstPlugin implements Interceptor {}
  • 在全局配置文件中注册插件
<!--plugins:注册插件  -->
    <plugins>
        <plugin interceptor="com.jdy.mybatis2020.MyPlugin.FirstPlugin">
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </plugin>
        <plugin interceptor="com.jdy.mybatis2020.MyPlugin.FirstPlugin"></plugin>
    </plugins>

二、插件原理

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

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

  • 目标方法执行时依次从外到内执行插件的intercept方法。

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

三、Interceptor接口

  • Intercept:拦截目标方法执行

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

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

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

    /**
     * 拦截目标方法执行
     *
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("MyFirstPlugin...intercept:" + invocation.getMethod());
        //动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工
        Object target = invocation.getTarget();
        System.out.println("当前拦截到的对象:" + target);
        //拿到:StatementHandler==>ParameterHandler===>parameterObject
        //拿到target的元数据
        MetaObject metaObject = SystemMetaObject.forObject(target);
        Object value = metaObject.getValue("parameterHandler.parameterObject");
        System.out.println("sql语句用的参数是:" + value);
        //修改完sql语句要用的参数
        metaObject.setValue("parameterHandler.parameterObject", 11);
        //执行目标方法
        Object proceed = invocation.proceed();
        //返回执行后的返回值
        return proceed;
    }

    /**
     * 生成动态代理对象,可以使用MyBatis提 供的Plugin类的wrap方法
     *
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        // TODO Auto-generated method stub
        //我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
        System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象" + target);
        Object wrap = Plugin.wrap(target, this);
        //返回为当前target创建的动态代理
        return wrap;
    }

    /**
     * 注入插件配置时设置的属性
     *
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        // TODO Auto-generated method stub
        System.out.println("插件配置的信息:" + properties);
    }
}

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

//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);
原文地址:https://www.cnblogs.com/jdy1022/p/14202192.html