Comtech Gold中文网

mybatis拦截器注册初始化编写示例及如何生效详解

发布日期:2025-01-03 17:43    点击次数:56
Mybatis支持四种类型的拦截器 这一点可以从Mybatis的初始化类Configuration.java中得到验证(源码体不贴出了,改天分析Mybatis初始化过程的时候详细说)。具体包括: ParameterHandler拦截器ResultSetHandler拦截器StatementHandler拦截器Executor拦截器 四种拦截器分别有各自不同的用途,当我们熟悉Mybatis的运行机制之后,理解起来就相对容易一些。 目前,如果我们对Mybatis还不是很了解的话,也没有关系,不影响我们对Mybatis的拦截器做初步的了解。 我们不需要一次性对四种类型的拦截器都了解,因为他们的工作机制及底层原理大致相同。 我们今天以Executor拦截器为切入点,了解Mybatis拦截器的实现方法、以及初步分析其实现原理。 今天的目标是:用Mybatis拦截器技术,计算每一句sql语句的执行时长,并在控制台打印出来具体的sql语句及参数。 在此过程中,我们会了解: 编写Mybatis拦截器。Mybatis拦截器注册。Mybatis拦截器的初始化过程。Mybatis拦截器是如何生效的。 准备工作 Springboot项目,并引入Mybatis,pom文件加入依赖: 然后配置数据库访问、建表、创建mapper.xml文件及mapper对象,在mapper.xml中写一个简单的获取数据的sql、使用mapper对象通过该sql语句获取数据。 今天文章的主要目标是拦截器,所以以上关于通过Mybatis获取数据库数据的代码就不贴出了。 编写拦截器 Mybatis拦截器是AOP的一个具体实现,我们前面文章分析过AOP的实现原理其实就是动态代理,java实现动态代理有两种方式:cglib和java原生(我们前面有一篇文章专门分析过两者的区别),Mybatis拦截器是通过java原生的方式实现的。 其实我们实现的拦截器在java原生动态代理的框架中属于回调对象的一部分,回调对象其实是Plugin,Plugin对象持有Interceptor,Plugin的invoke方法才是JDK动态代理中的那个回调方法、其中会调用Interceptor的intercept方法,所以Plugin的invoke方法其实又类似于一个模板方法(这部分后面会有具体分析)。 所以Mybatis都已经替我们安排好了,我们的拦截器只需要实现这个intercept方法即可。 要实现的目标都在上面这段代码中,一目了然。 需要解释以下几点: @Intercepts注解:目的是为了告诉Mybatis当前拦截器的类型(开篇说的四种类型之一)、拦截方法名以及方法参数。Invocation:拦截器被调用的时候组装起来的一个包装对象,包含了被代理对象(原对象)、被代理的方法、以及方法调用参数等。通过Invocation.proceed()执行被代理对象的原方法,所以在该方法前、后可以添加我们自己的增强功能,比如计算sql语句执行时长就是在方法执行前、后分别获取系统时间并计算时间差即可。Executor有两个query方法,我们需要清楚地知道应用最终会调用Executor的哪个query方法,否则如果匹配不上的话就不会执行拦截。当然,我们也可以对多个方法执行拦截。invocation.getArgs()[0]获取到的是被代理方法的第一个参数,以此类推......可以获取到被代理方法的所有参数,所以在拦截器中可以有完整的被代理方法的执行现场,能做到一个拦截器理论上能做的任何事情。 好了,拦截器代码我们就完成了。 拦截器的注册 拦截器编写完成后,需要注册到Mybatis的InterceptorChain中才能生效。 我们可以看到Mybatis的拦截器又是一个chain的概念,所以我们是可以实现多个拦截器,每一个拦截器各自实现自己的目标的。 可以通过以下几种方式实现拦截器的注册: 在mybatis.xml文件中通过plugins标签配置通过配置类,创建ConfigurationCustomizer类实现customize方法Spring项目中将拦截器注册到Spring Ioc容器中 我们当前是基于Springboot的项目,所以上面代码中已经加了@Component注解,通过第3种方式完成注册,简单方便。 运行 拦截器准备好了,启动项目,随便跑一个数据查询的方法: 可以看到拦截器已经可以正常工作了。 上面我们已经实现了一个简单的Executor拦截器,下面我们要花点时间分析一下这个拦截器是怎么生效的。 拦截器的初始化 在尚未对Mybatis的初始化过程进行整体分析的情况下,想要彻底搞清楚拦截器的初始化过程多少有点困难,但是如果我们只看Mybatis初始化过程中与拦截器有关的部分的话,也不是不可以。 Mybatis初始化的过程中会通过SqlSessionFatoryBuilder创建SqlSessionFactory,SqlSessionFactory会持有Configuration对象。 而我们前面所说的注册Mybatis拦截器,不论以什么样的方式进行注册,其目的无非就是要让Mybatis启动、初始化的过程中,将拦截器注册到Configuration对象中。 比如我们上面所说的任何一种注册方式,最终SqlSessionFactoryBean都会将拦截器获取到plugins属性中,在buildSqlSessionFactory()方法中将拦截器注册到Configuration对象中: 最后调用SqlSessionFactoryBuilder的build方法创建SqlSessionFactory,我们从源码可以看到最终创建了DefaultSqlSessionFactory,并且将Configuration对象以参数的形式传递过去: 而DefaultSqlSessionFactory会持有该Configuration对象: 所以,Mybatis初始化的过程中会获取到我们注册的拦截器,该拦截器会注册到Configuration对象中,最终,SqlSesscionFactory对象会持有Configuration对象,从而持有该拦截器。 拦截器是如何生效的#openSession 那我们现在看一下,已经完成初始化的拦截器最终是如何生效的。 我们知道一条数据库操作语句的执行首先是要调用SqlSesscionFactory的openSession来获取sqlSession开始的。 上面我们已经看到初始化过程中创建的是DefaultSqlSessionFactory,所以我们直接看DefaultSqlSessionFactory的openSession方法。 最终会调用到openSessionFromDataSource或openSessionFromConnection,两个方法的结构差不太多,但是具体细节的区分今天就不做分析了。我们直接看openSessionFromDataSource: 关注的重点放在final Executor executor = configuration.newExecutor(tx, execType)上,我们去看一下Configuraton的这个方法: 方法最后阶段获取到Excutor后,调用interceptorChain.pluginAll,该方法逐个调用拦截器的plugin方法,拦截器的plugin方法调用Plugin的wrap方法: 最终通过动态代理的方式,返回该对象的一个代理对象,回调对象为持有原对象、拦截器、拦截方法签名的Plugin对象。 所以我们知道,openSession最终创建的DefaultSqlSession所持有的Executor其实是已经被拦截器处理过的代理对象。 根据我们对JDK代理的理解,最终Executor的方法被调用的时候,其实是要回调这个代理对象创建的时候的回调器的invoke方法的,也就是Plugin的invoke方法。 拦截器是如何生效的#Executor执行 上面一节分析了openSession过程中,Executor代理对象是如何被创建的。 接下来看一下具体的Executor的执行,本例拦截的是他的query方法。其实我们已经知道query方法执行的时候是要调用Plugin的invoke方法的。 代码其实比较简单: 获取到当前Executor对象的所有注册的拦截方法,比较当前调用的方法是否为拦截方法,是的话就调用拦截器的intercept方法......就是我们自己编写的拦截器的拦截方法。否则如果当前方法没有配置拦截的话就调用原方法。 调用拦截器的拦截方法的时候,创建了一个持有被代理对象target、拦截方法、拦截方法的调用参数...等数据的Invocation对象作为参数传进去。这也就是为什么我们在拦截器方法中能获取到这些数据的原因。 OK...还差一点,就是如果配置了多个代理器的话,调用顺序的问题。其实整体比较起来,Mybatis的源码感觉比Spring的简单了许多,拦截器注册之后在InterceptorChain也就是保存在ArrayList中,所以他本身应该是没有顺序的,想要控制调用顺序应该还得想其他办法。 以上就是mybatis拦截器注册初始化编写示例及如何生效详解的详细内容,更多关于mybatis拦截器的资料请关注脚本之家其它相关文章!

上一篇:高性能+高智能CPU!i7智能技术全接触
下一篇:北京地区北京EX3火热促销,最新报价8.99万!今日钜惠