关于mybatis的插件,我想你们也都用过,就好比最经常使用的逆向工程,根据表结构生成model,dao,xml文件,还有分页插件,那这些插件的工做原理是怎么样的呢,就好比分页插件,它为何能改变咱们在xml文件中写的sql语句,本文将带你们一块儿来了解mybatis的插件机制。(因为本人也是在不断学习,文中不免有错误或不足之处,还望指正,本文基于mybatis3.3.0版本),下面将围绕这几个方面java
一、插件入口,即怎么把插件注入到mybatis代码里面spring
二、插件能拦截哪些类或哪些方法sql
三、举例简易分表插件原理数据库
在先了解前,咱们来一段自定义mybatis插件的代码apache
1 import org.apache.ibatis.executor.statement.StatementHandler; 2 import org.apache.ibatis.plugin.*; 3 4 import java.sql.Connection; 5 import java.util.Properties; 6 7 @Intercepts( 8 value = { 9 @Signature( 10 type = StatementHandler.class, 11 method = "prepare", 12 args = {Connection.class} // 不一样版本的prepare方法参数不同,高版本的还有一个Integer参数 13 ) 14 } 15 ) 16 public class PluginDemo implements Interceptor { 17 @Override 18 public Object intercept(Invocation invocation) throws Throwable { 19 return invocation.proceed(); 20 } 21 22 @Override 23 public Object plugin(Object target) { 24 return Plugin.wrap(target,this); 25 } 26 27 @Override 28 public void setProperties(Properties properties) { 29 30 }
要自定义mybatis插件,必须得实现Interceptor接口,这个接口有三个抽象方法数组
一、intercept,这个方法是mybatis的核心方法,要实现自定义逻辑,基本都是改造这个方法,其中invocation参数能够经过反射要获取原始方法和对应参数信息session
二、plugin,它的做用是用来生成一个拦截对方,也就是代理对象,使得被代理的对象必定会通过intercept方法,一般都会使用mybatis提供的工具类Plugin来获取代理对象,若是有本身独特需求,能够自定义mybatis
三、setProperties,这个方法就是用来设置插件的一些属性app
其中@intercepts注解就是用来标明拦截哪些类,哪些方法。ide
当咱们脱离spring容器来使用mybatis的时候,咱们一般是这样写代码的
而在mybatisConfig.xml文件中,咱们有配置数据库链接信息,插件,别名,mapper文件映射地址等信息,(舒适提示,这些配置有必定顺序,若是不按照顺序配置,则mybatis解析时会抛出异常,详细配置信息能够参考mbatis的dtd文件配置)既然在配置文件中配置了,那确定也会被解析掉,以下图,在XmlConfigBuild中进行解析
解析后,插件信息会被存储到configuation中InterceptorChain集合里面,这个configuation在mybatis运行周期是一个单例的,它负责存储全部的配置信息
最终,mybatis的插件信息完整的注入到了configuation里面。
在正常开发流程中,咱们基本都是经过先获取sqlSession,若是不采用自定义配置,在默认的sqlsession实现就是defaultSqlSession,在该类的一个方法里就隐含了融合插件,
其中会获取到executor,这个类是经过configuation获取的,
最终融合插件的方法就是图中红框的代码。它会为原始的executor类生成代理类。从而你在执行executor类的一些方法时,好比query,update方法,会先生成对应的代理对象,myabtis采用的是jdk的动态代理,代理后,你执行executor类的query,update方法时会自动转接到你自定义的插件intercept方法里面,也能够理解为覆盖原来的方法。
经过这,咱们大概也能够猜想的出,mybatis插件要拦截的类,很大缘由在configuation类中有生成,也不带你们绕弯子了,来看截图
拦截statementHandler接口的实现类,它经过routingStatementHandler来实现路由,最终定向到prepareStatementHandler类
拦截parameterHandler。这个接口是用来封装参数用的,也就是最终将参数设置到sql里面
拦截ResultSetHandler接口,它主要用于处理sql运行返回的结果
总结,mybatis能够拦截的类分四大类
1、executor,executor类能够说是执行sql的全过程,如组装参数,sql改造,结果处理,比较普遍,但实际用的很少
2、StatementHandler,这个是执行sql的过程,能够获取到待执行的sql,可用来改造sql,如分页,分表,最常拦截的类
3、paremeterHandler,这个用来拦截sql的参数,能够自定义参数组装规则
4、resultHandler,这个用来处理结果
1 import org.apache.ibatis.executor.statement.StatementHandler; 2 import org.apache.ibatis.mapping.BoundSql; 3 import org.apache.ibatis.plugin.*; 4 import org.apache.ibatis.reflection.MetaObject; 5 import org.apache.ibatis.reflection.SystemMetaObject; 6 7 import java.sql.Connection; 8 import java.util.Properties; 9 10 @Intercepts( 11 value = { 12 @Signature( 13 type = StatementHandler.class, 14 method = "prepare", 15 args = {Connection.class} 16 ) 17 } 18 ) 19 public class PluginDemo implements Interceptor { 20 @Override 21 public Object intercept(Invocation invocation) throws Throwable { 22 23 /** 24 * 获取被拦截的目前类,在这里是拦截了statementHandler,全部目前类也就是它 25 * 经过这个类咱们能够拿到待执行的sql语句,一般使用mataObject工具类来获取 26 * 关于这个工具类,你们可自行了解,我的认为这个工具类很强大 27 */ 28 StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); 29 MetaObject metaObject = SystemMetaObject.forObject(statementHandler); 30 /** 31 * 先解释下为何写成delegate.boundSql就能够拿到boundSql类 32 * 从前面也能够得知,statementHandler的默认实现是routingStatementHandler。 33 * 这个类有一个属性statementHandler,属性名就叫delegate,而这个属性的默认实现又是preparedStatementHandler 34 * 后面这个类又有属性boundSql,因此,最终造成的写法就是delegate.boundSql。 35 * 因此这也体现了MetaObject工具类的强大,能够经过实例传参,就能够根据属性名获取对应属性值 36 */ 37 BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql"); 38 39 // 待执行的sql,在这里也就是预编译后的sql,即参数位都是?号 40 String sql = boundSql.getSql(); 41 /** 42 * 既然拿到了预编译后的sql,那就能够按照你本身的想法随心所欲,如分页,按年分表等等 43 * 分表的话,我的推荐druid的sql解析器,我认为仍是不错的,你们能够自行了解 44 * 最后改造完sql,别忘了把它设置回去 45 * metaObject.setValue("delegate.boundSql.sql",sql); 46 * invocation.proceed,即原始方法的执行 47 * 注意点就是,由于mybatis插件采用的是代理模式,因此若是存在多个插件,会造成多个代理 48 * 你若是要拿到最原始的对象,还得进一步进行分解 49 * 如:while(metaObject.getValue(""h) != null){ 50 * Object obj = metaObject.getValue("h"); 51 * .... 52 * } 53 */ 54 return invocation.proceed(); 55 } 56 57 @Override 58 public Object plugin(Object target) { 59 return Plugin.wrap(target,this); 60 } 61 62 @Override 63 public void setProperties(Properties properties) { 64 65 } 66 }
-----------------------------------------------------------------------------------------------------------------------------分界线-------------------------------------------------------------------------------------
以上就是所有内容