Mybatis1.插件接口。
在Mybatis中使用插件的时候,就必须实现Interceptor接口。
public interface Interceptor{
Objectintercept(Invocation invocation) throws Throwable;
Objectplugin (Object target);
voidsetProperties(Properties properties);
}
Intercept方法:插件的核心方法,它将直接覆盖你所拦截对象原有的方法。参数Invocation对象,通过它可以反射调度原来对象的方法。
Plugin方法:target是拦截的对象,它的作用是给拦截对象生成一个代理对象,并返回它。为了方便Mybatis使用org.apache.ibatis.plugin.Plugin中的wrap静态方法提供生成代理对象,我们往往使用plugin方法便可以生成一个代理对象。
setProperties方法:允许在plugin元素中配置所需要的参数,方便在插件初始化的时候就被调用一次,然后把插件对象存存入到配置中,以便以后在取出。
2. 插件的初始化
插件的初始化是在Mybatis初始化的时候完成的,这点可以通过XMLConfigBuilder中的代码可以知道。
publicvoid pluginElment(XNode parent) throws Exception{
if(parent!=null){
for(XNode child:parent.getChildren()){
Stringinterceptor=child.getStringAtrribute("interceptor");
Propertiesproperties=child.getChildrenAsProperties();
Interceptor interceptorInstance=(Interceptor)resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
Configuration.addInterceptor(interceptorInstance);
}
}
}
在解析配置文件的时候,在Mybatis的上下文初始化过程中,就开始读入插件的节点和我们配置的信息,同时使用反射技术生成对应的插件实例,然后调用setProperties方法,设置我们配置的参数,然后将插件的实例保存到配置的对象中,以便以后的读取和使用它。插件的初始化是在Mybatis的初始化的时候进行的初始化,而不是在使用的时候才被初始化,这样可以提高性能。
3. 插件的代理和反射的设计
插件用的是责任链的模式实现的。
所谓责任链就是,一个对象在Mybatis中可能是四大对象(Excetor、StatementHandler、ParameterHandler、ResultHandler)中的一个,在多个角色中传递,处在责任链上的任何角色都有处理它的机会。
作用:让每一个责任链上的角色都有机会去拦截这个对象,在将来有新的角色也可以轻松拦截请求对象,进行处理。
Mybatis的责任链是用interceptorChain去定义的。我们知道plugin方法是生成代理对象的方法,当它取出插件的时候从Configuration对象中去取出。从第一对象开始,将对象传递给plugin方法,然后返回一个代理;如果存在第二个插件,那么我们就拿到第一个代理对象,传递给plugin方法在返回第一代理对象的代理….一次类推,有多少个拦截器就生成多少个代理对象。
在初始化的时候,我们一个个的加载插件实例,并用setProperties方法进行初始化,我们可以使用Mybatis提供的Plugin.wrap方法来生成代理对象,在一层层的使用Invaction对象的proceed()方法来推动代理对象的运行。在多个插件的环境下,调度proceed()方法时,Mybatis总是从最后一个代理对象运行到第一代理对象,最后是真实被拦截的对象方法被运行。
4.常用的工具类—MeteObject
在Mybatis插件中常用到一个工具类是MeteObject。
作用:可以有效的读取或者修改一些重要对象的属性。
常用的方法:
MeteObject forObject(Object object,ObjectFactoryobjectFactory,ObjectWrapperFactory objectWrapperFactory);
用于封装对象,这个是已经过时了,现在使用SystemMetaObject(Object obj)
Object getValue(String name)方法:用于获取对象的属性值,支持OGNL。
void setValue(String name,Object value)方法:用于修改对象的属性值,支持OGNL。
在Mybatis对象中大量使用这个类进行包装,包括四大对象,使得我们可以通过它来给四大对象的某些属性赋值从而满足我们的需要。
5. 插件的开发步骤
(1)确定需要拦截的签名
A:确定要拦截的对象
Exector对象:执行SQL全过程,包含组装参数,组装结果集返回和执行SQL过程,都可以拦截,较为广泛,一般在实际中用的不多。
StatementHandler对象:执行SQL的过程,我们可以重写执行SQL的过程,实际常用拦截对象。
ParamterHandler对象:主要是拦截执行SQL的参数组装,可以重写组装参数规则。
ResultSetHandler对象:主要是拦截执行结果的组装,可以重写组装结果规则。
B:拦截的方法和参数
public interface StatementHandler{
Statementprepare(Connection connection)throws SQLException;
voidparameterize(Statement statement)throws SQLException;
void batch(Statement statement)throwsSQLException;
int update(Statementstatement)throws SQLException;
ListQuery(Statementstatement,ResultHandler resultHandler)throws SQLException;
BoundSql getBoundSql();
ParameterHandlergetParameterHandler();
}
定义插件签名
@Inteceptor({@signature(type=Statement.class,method=”prepare”,args={Connection.class})
})
Public class Myplugin implements Interceptor{
}
(2)实现拦截的方法
package com.zl.mybatis.test;
import java.util.Properties;
import org.apache.ibatis.executor.Executor;
importorg.apache.ibatis.mapping.MappedStatement;
importorg.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
/**
*@author fab
*
*/
@Intercepts({ @Signature(type =Executor.class, // 确定拦截的对象
method = "update", // 确定拦截的方法
args = { MappedStatement.class,Object.class }// 确定拦截的参数
) })
public class MyPlugin implementsInterceptor {
Propertiesprops = null;
/**
* 代替拦截对象方法的内容
* @param invocation
* 责任链对象
*/
@Override
publicObject intercept(Invocation invocation) throws Throwable {
//TODO Auto-generated method stub
System.out.println("before.......");
//如果当前代理的是一个非代理对象,那么它就回调用真实拦截对象的方法,如果不是它将会调度下一个插件代理对象的invoke方法
Objectobject = invocation.proceed();
System.out.println("after.....");
returnobject;
}
/***
* 生成对象的代理,这用常用Mybatis提供的Plugin类的wrap方法
* @param target
* 被代理的对象
*/
@Override
publicObject plugin(Object target) {
//TODO Auto-generated method stub
//使用Mybatis提供的Plugin类生成代理对象
System.out.println("调用生成代理对象.....");
returnPlugin.wrap(target, this);
}
/***
* 获取插件配置的属性,在Mybatis的配置文件里面去配置
* @param props Mybatis的配置参数
*
*/
@Override
publicvoid setProperties(Properties props) {
//TODO Auto-generated method stub
System.out.println(props.get("dbType"));
this.props= props;
}
}
(3)配置
配置插件:
(4) 运行结果
调用生成代理对象.....
before.......
调用生成代理对象.....
调用生成代理对象.....
调用生成代理对象.....
after.....
7.简单说
Mybatis插件的实现方法:
需要实现Interceptor接口,实现三个方法。
public Object intercept(Invocation invocation);
作用:插件的具体实现的地方,这是最复杂的地方。
public Object plugin(Object target);
作用:利用Mybatis自身的Plugin的Wrap()方法来获得代理对象。
public void setProperties(Properties props);
作用:初始化一些基本设置,这些配置在Mybatis的.xml文件中体现。
需要明确:拦截那个对象,拦截那个方法,拦截那个参数。
8.总结
使用插件应注意的六点:
(1) 能不用插件尽量不用插件,因为它将修改Mybatis的底层设计。
(2) 插件生成的是层层代理对象的责任链模式,通过反射方法运行,性能不高,所以减少插件就能减少代理,从而提高系统的性能。
(3) 编写插件需要了解Mybatis的运行原理,了解四大对象及其方法的作用,准确判断需要拦截什么对象,什么方法,参数是什么,才能确定签名如何编写。
(4) 在插件中往往需要读取和修改Mybatis映射器的对象属性,你需要熟练掌握关于Mybatis映射器内部组成的知识。
(5) 插件的编写需要考虑全面,特别是多个插件层层代理的时候,要保证逻辑的正确性。
(6) 尽量小改动Mybatis底层的东西,以减少错误的发生。