Mybatis自定义插件

发布时间:2020-06-10 23:17:05 作者:石小令
来源:网络 阅读:539

相信工作中用mybatis的同学大部分都使用过PageHelper分布插件,最近也是想了解一下PageHelper的实现原理,PageHelper也是通过mybatis的插件来实现的。具体怎么去实现一个mybatis插件下面做具体的介绍。

1.mybatis插件机制

工作中遇到过一个场景,打印mybatis的执行sql日志到公司日志平台。那么就需要自定义mybatis插件来实现,在执行sql之前,希望能够拦截到mybatis的执行sql,然后使用公司的日志框架打印日志。
myba支持拦截的方法:

在自定义mybatis插件的时候,需要指定自己所需要的拦截方式,例如我上面工作中是需要使用StatementHandler。

2.mybatis插件使用到的设计模式

mybatis插件中主要使用到了两种设计模式:动态代理和责任链模式;

1)责任链模式

责任链模式
在说mybatis中的责任链之前,我们先回想一下责任链模式:
Mybatis自定义插件
责任链模式中涉及两个角色:

2)动态代理

mybatis中的Executor、ParameterHandler、ResultSetHandler、StatementHandler它们都是通过Configuration.new()方法来创建的,而Configuration.new()方法实际上调用的是InterceptorChain.pluginAll()方法来生成代理对象,所以通过Configuration.new*()系列方法得到的对象实际是一个代理对象。
以Configuration.newExecutor()方法为例介绍:
1.Configuration.newExecutor()

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
            executorType = executorType == null ? this.defaultExecutorType : executorType;
            executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
            Object 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 (this.cacheEnabled) {
                    executor = new CachingExecutor((Executor)executor);
            }
            Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
            return executor;
    }

2.interceptorChain.pluginAll(executor)

public Object pluginAll(Object target) {
            Interceptor interceptor;
            for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
                    interceptor = (Interceptor)var2.next();
            }
            return target;
    }

3.target = interceptor.plugin(target)

interceptor.plugin()方法实际上是我们自定义插件的plugin方法。
一般我们这个方法的实现是通过Plugin.wrap来生成代理

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

4. Plugin.wrap

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

3.实现自定义Mybatis插件

mybatis中使用的拦截器都需要实现Interceptor接口

public interface Interceptor {
    Object intercept(Invocation var1) throws Throwable;
    Object plugin(Object var1);
    void setProperties(Properties var1);
}

用户自定义的拦截器除了继承Interceptor接口,还需要使用@Intercepts和@Signature两个注解标识。

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {
            MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class
    }),
    @Signature(type = Executor.class, method = "query", args = {
            MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class,
            CacheKey.class, BoundSql.class
    })})

例如上面的定义:

将我们自定义的mybatis的插件配置到mybatis-config.xml中

<plugins>
            <plugin interceptor="com.xxx.XXXInterceptor">
                    <!-- 对拦截器中的属性进行初始化 -->
                    <property name="name" value="Bob"/>
            </plugin>
    </plugins>

4.案例

例如,我们比较常用的对sql进行监控,监控sql的执行时长及慢查询等,那么我们就可以通过编写MonitorInterceptor拦截器

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {
                MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class
        }),
        @Signature(type = Executor.class, method = "query", args = {
                MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class,
                CacheKey.class, BoundSql.class
        })})
@Slf4j
public class MonitorInercepor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            Object[] args = invocation.getArgs();
            MappedStatement ms = (MappedStatement) args[0];
            Object parameter = args[1];
            long start = System.currentTimeMillis();
            Object result = invocation.proceed();  // 获取代理
            long end = System.currentTimeMillis();
            long cost = end - start;
            log.debug("[TimerInterceptor] execute [{}] cost [{}] ms, parameter:{}", ms.getId(), cost, parameter);
            if (cost > 1000) {
                log.warn("Sql语句执行时间超过1秒钟,请检查优化,方法:{},耗时:{}ms,参数:{}", ms.getId(), cost, parameter);
            }
            return result;
        } catch (Throwable r) {
            log.error(r.getMessage(), r);
        }
        return invocation.proceed();
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) { }
}
推荐阅读:
  1. PHP Smarty 模板 自定义插件
  2. nagios自定义插件

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mybatis mybatis自定义插件 mybatis插件机制

上一篇:Django 后台上传图片前端无法展示

下一篇:4Python全栈之路系列之Django模型

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》