-
-
MyBatis允许在已映射语句执行过程中的某一点进行 拦截调用。
-
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed);
ParameterHandler (getParameterObject, setParameters);
ResultSetHandler (handleResultSets, handleOutputParameters);
StatementHandler (prepare, parameterize, batch, update, query);
一、插件开发步骤
-
编写插件实现Interceptor接口,并使用 @Intercepts注解完成插件签名。
/** * 完成插件签名: * 告诉MyBatis当前插件用来拦截哪个对象的哪个方法 */ @Intercepts({ @Signature(type= StatementHandler.class,method="parameterize",args=java.sql.Statement.class) }) public class FirstPlugin implements Interceptor {}
- 在全局配置文件中注册插件
<!--plugins:注册插件 --> <plugins> <plugin interceptor="com.jdy.mybatis2020.MyPlugin.FirstPlugin"> <property name="username" value="root"/> <property name="password" value="root"/> </plugin> <plugin interceptor="com.jdy.mybatis2020.MyPlugin.FirstPlugin"></plugin> </plugins>
二、插件原理
-
按照插件注解声明,按照插件配置顺序调用插件plugin方 法,生成被拦截对象的动态代理。
-
多个插件依次生成目标对象的代理对象,层层包裹,先声 明的先包裹;形成代理链。
-
目标方法执行时依次从外到内执行插件的intercept方法。
-
多个插件情况下,我们往往需要在某个插件中分离出目标 对象。可以借助MyBatis提供的SystemMetaObject类来进行获 取最后一层的h以及target属性的值。
三、Interceptor接口
-
Intercept:拦截目标方法执行
-
plugin:生成动态代理对象,可以使用MyBatis提 供的Plugin类的wrap方法
-
/** * 完成插件签名: * 告诉MyBatis当前插件用来拦截哪个对象的哪个方法 */ @Intercepts({ @Signature(type = StatementHandler.class, method = "parameterize", args = java.sql.Statement.class) }) public class FirstPlugin implements Interceptor { /** * 拦截目标方法执行 * * @param invocation * @return * @throws Throwable */ @Override public Object intercept(Invocation invocation) throws Throwable { // TODO Auto-generated method stub System.out.println("MyFirstPlugin...intercept:" + invocation.getMethod()); //动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工 Object target = invocation.getTarget(); System.out.println("当前拦截到的对象:" + target); //拿到:StatementHandler==>ParameterHandler===>parameterObject //拿到target的元数据 MetaObject metaObject = SystemMetaObject.forObject(target); Object value = metaObject.getValue("parameterHandler.parameterObject"); System.out.println("sql语句用的参数是:" + value); //修改完sql语句要用的参数 metaObject.setValue("parameterHandler.parameterObject", 11); //执行目标方法 Object proceed = invocation.proceed(); //返回执行后的返回值 return proceed; } /** * 生成动态代理对象,可以使用MyBatis提 供的Plugin类的wrap方法 * * @param target * @return */ @Override public Object plugin(Object target) { // TODO Auto-generated method stub //我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象 System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象" + target); Object wrap = Plugin.wrap(target, this); //返回为当前target创建的动态代理 return wrap; } /** * 注入插件配置时设置的属性 * * @param properties */ @Override public void setProperties(Properties properties) { // TODO Auto-generated method stub System.out.println("插件配置的信息:" + properties); } }
常用代码:从代理链中分离真实被代理对象
//1、分离代理对象。由于会形成多次代理,所以需要通过一个 while 循环分离出最终被代理对象,从而方便提取信息 MetaObject metaObject = SystemMetaObject. forObject(target); while (metaObject.hasGetter("h")) { Object h = metaObject.getValue("h"); metaObject = SystemMetaObject. forObject(h); } //2、获取到代理对象中包含的被代理的真实对象 Object obj = metaObject.getValue("target"); //3、获取被代理对象的MetaObject方便进行信息提取 MetaObject forObject = SystemMetaObject.forObject(obj);