欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MyBatis学习笔记——插件机制(AOP)

程序员文章站 2024-03-23 20:09:04
...

插件

MyBatis所述的插件功能,其实就是一个拦截器功能。

概述

1、在四大对象创建的时候,每个创建出来的对象不是直接返回的,而是通过interceptorChain.pluginAll(parameterHandler)返回的。
2、pluginAll获取到所有的Interceptor(拦截器)(插件需要实现的接口),调用Interceptor.pluginAll(target),返回target被包装后的对象
3、插件机制:我们可以使用插件为目标对象创建一个代理对象:AOP(面向切面)
我们的插件可以为四大对象创建出代理对象代理对象就可以拦截四大对象的每一个执行(方法)

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

编写单插件

实现接口

实现自定义插件,需要实现org.apache.ibatis.plugin.Interceptor接口。

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

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

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

    /** 
     * plugin:
     *      包装目标对象:包装,为目标对象创建一个代理对象
     */
    @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属性设置进来
     * 
     */
    @Override
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息:" + properties);
    }

}

注释,接口里面共有三个方法。
(1)intercept方法:
拦截目标方法,在其前后都可以进行业务逻辑,使用invocation.proceed();执行方法。
(2)plugin方法:
包装target目标为代理对象,可以自己写创建代理对象的逻辑,也可以使用MyBatis提供的快捷方式去创建代理对象Object wrap = Plugin.wrap(target, this);,第二个this就是当前的拦截器。
(3)setProperties方法:
在全局配置文件注册插件时,可以设置一些property属性,通过该方法的入参获取。
如以下的全局配置文件,设置了两个property,可以通过入参获取到:

    <!-- plugins:注册插件 -->
    <plugins>
        <plugin interceptor="com.shen.mybaties.dao.MyFirstPlugin">
            <property name="username" value="root" />
            <property name="password" value="123456" />
        </plugin>
    </plugins>

插件签名

光实现接口还不行,因为没有指定拦截哪一个类,拦截哪一个方法
需要在插件类上加一个@Intercepts,它有三个属性
(1)type:类类型,就是some.class
(2)method:需要拦截的方法名
(3)args:由于可能存在方法重载,故需要通过参数类型来确定拦截的方法,同样是类类型(.class

注册插件

全局配置文件加入如下配置:

<configuration>

    <!-- plugins:注册插件 -->
    <plugins>
        <plugin interceptor="com.shen.mybaties.dao.MyFirstPlugin">
            <property name="username" value="root" />
            <property name="password" value="123456" />
        </plugin>
    </plugins>

</configuration> 

测试

在plugin方法中,我们使用MyBatis提供的快捷方法Plugin.wrap(target, this);,可以快速得到一个代理对象。
MyBatis学习笔记——插件机制(AOP)
plugin产生的代理对象,其中target才是真正的对象。

编写多插件

模仿First插件。

运行流程

多个插件包装对象时,每次都进行一次包装,第二次传入的target为第一次包装的代理对象,造成一层包一层的情况。

for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
}

MyBatis学习笔记——插件机制(AOP)
创建动态代理的时候,是按照插件配置顺序创建层层代理的对象。
执行目标方法的时候,按照逆向顺序执行。

简单插件,修改查询参数

插件开发,主要需要知道被代理对象有哪些属性,如修改查询参数。

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MetaObject metaObject = SystemMetaObject.forObject(invocation.getTarget());
        Object value = metaObject.getValue("parameterHandler.parameterObject");
        metaObject.setValue("parameterHandler.parameterObject",2);
        // 执行目标方法
        Object proceed = invocation.proceed();
        // 返回执行后的返回值
        return proceed;
    }

这里,需要知道被代理对象,有一个属性为parameterHandler,属性的属性parameterObject是控制参数预编译的参数,我们需要修改这个。
使用MyBatis提供的SystemMetaObjectgetValuesetValue方法进行修改,达到目的。