简单了解mybatis拦截器实现原理及实例
副标题[/!--empirenews.page--]
短视频,自媒体,达人种草一站服务 这篇文章主要介绍了简单了解mybatis拦截器实现原理及实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 例行惯例,先看些基本概念: 1 拦截器的作用就是我们可以拦截某些方法的调用,在目标方法前后加上我们自己逻辑 2 Mybatis拦截器设计的一个初衷是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。 自定义拦截器 * mybatis 自定义拦截器 * 三步骤: * 1 实现 {@link Interceptor} 接口 * 2 添加拦截注解 {@link Intercepts} * 3 配置文件中添加拦截器 * 1 实现 {@link Interceptor} 接口 * 具体作用可以看下面代码每个方法的注释 * 2 添加拦截注解 {@link Intercepts} * mybatis 拦截器默认可拦截的类型只有四种,即四种接口类型 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler * 对于我们的自定义拦截器必须使用 mybatis 提供的注解来指明我们要拦截的是四类中的哪一个类接口 * 具体规则如下: * a:Intercepts 拦截器: 标识我的类是一个拦截器 * b:Signature 署名: 则是指明我们的拦截器需要拦截哪一个接口的哪一个方法 * type 对应四类接口中的某一个,比如是 Executor * method 对应接口中的哪类方法,比如 Executor 的 update 方法 * args 对应接口中的哪一个方法,比如 Executor 中 query 因为重载原因,方法有多个,args 就是指明参数类型,从而确定是哪一个方法 * 3 配置文件中添加拦截器 * 拦截器其实就是一个 plugin,在 mybatis 核心配置文件中我们需要配置我们的 plugin : * plugin interceptor="liu.york.mybatis.study.plugin.MyInterceptor" * property value="LiuYork"/ * property value="123456"/ * /plugin * 拦截器顺序 * 1 不同拦截器顺序: * Executor - ParameterHandler - StatementHandler - ResultSetHandler * 2 对于同一个类型的拦截器的不同对象拦截顺序: * 在 mybatis 核心配置文件根据配置的位置,拦截顺序是 从上往下 @Intercepts({ @Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}), @Signature(method = "query", type = StatementHandler.class, args = {Statement.class, ResultHandler.class}) public class MyInterceptor implements Interceptor { * 这个方法很好理解 * 作用只有一个:我们不是拦截方法吗,拦截之后我们要做什么事情呢? * 这个方法里面就是我们要做的事情 * 解释这个方法前,我们一定要理解方法参数 {@link Invocation} 是个什么鬼? * 1 我们知道,mybatis拦截器默认只能拦截四种类型 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler * 2 不管是哪种代理,代理的目标对象就是我们要拦截对象,举例说明: * 比如我们要拦截 {@link Executor#update(MappedStatement ms, Object parameter)} 方法, * 那么 Invocation 就是这个对象,Invocation 里面有三个参数 target method args * target 就是 Executor * method 就是 update * args 就是 MappedStatement ms, Object parameter * 如果还是不能理解,我再举一个需求案例:看下面方法代码里面的需求 * 该方法在运行时调用 @Override public Object intercept(Invocation invocation) throws Throwable { * 需求:我们需要对所有更新操作前打印查询语句的 sql 日志 * 那我就可以让我们的自定义拦截器 MyInterceptor 拦截 Executor 的 update 方法,在 update 执行前打印sql日志 * 比如我们拦截点是 Executor 的 update 方法 : int update(MappedStatement ms, Object parameter) * 那当我们日志打印成功之后,我们是不是还需要调用这个query方法呢,如何如调用呢? * 所以就出现了 Invocation 对象,它这个时候其实就是一个 Executor,而且 method 对应的就是 query 方法,我们 * 想要调用这个方法,只需要执行 invocation.proceed() /* 因为我拦截的就是Executor,所以我可以强转为 Executor,默认情况下,这个Executor 是个 SimpleExecutor */ Executor executor = (Executor)invocation.getTarget(); * Executor 的 update 方法里面有一个参数 MappedStatement,它是包含了 sql 语句的,所以我获取这个对象 * 以下是伪代码,思路: * 1 通过反射从 Executor 对象中获取 MappedStatement 对象 * 2 从 MappedStatement 对象中获取 SqlSource 对象 * 3 然后从 SqlSource 对象中获取获取 BoundSql 对象 * 4 最后通过 BoundSql#getSql 方法获取 sql MappedStatement mappedStatement = ReflectUtil.getMethodField(executor, MappedStatement.class); SqlSource sqlSource = ReflectUtil.getField(mappedStatement, SqlSource.class); BoundSql boundSql = sqlSource.getBoundSql(args); String sql = boundSql.getSql(); logger.info(sql); * 现在日志已经打印,需要调用目标对象的方法完成 update 操作 * 我们直接调用 invocation.proceed() 方法 * 进入源码其实就是一个常见的反射调用 method.invoke(target, args) * target 对应 Executor对象 * method 对应 Executor的update方法 * args 对应 Executor的update方法的参数 return invocation.proceed(); * 这个方法也很好理解 * 作用就只有一个:那就是Mybatis在创建拦截器代理时候会判断一次,当前这个类 MyInterceptor 到底需不需要生成一个代理进行拦截, * 如果需要拦截,就生成一个代理对象,这个代理就是一个 {@link Plugin},它实现了jdk的动态代理接口 {@link InvocationHandler}, * 如果不需要代理,则直接返回目标对象本身 * Mybatis为什么会判断一次是否需要代理呢? * 默认情况下,Mybatis只能拦截四种类型的接口:Executor、StatementHandler、ParameterHandler 和 ResultSetHandler * 通过 {@link Intercepts} 和 {@link Signature} 两个注解 (编辑:海南站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |