一、mybatis支持插件开发
插件是用来改变或者扩展mybatis的原有的功能,mybaits的插件就是通过继承Interceptor拦截器实现的;
mybatis中能使用插件进行拦截的接口和方法如下:
Executor(update、query 、 flushStatment 、 commit 、 rollback 、 getTransaction 、 close 、 isClose)
StatementHandler(prepare 、 paramterize 、 batch 、 update 、 query)
ParameterHandler( getParameterObject 、 setParameters )
ResultSetHandler( handleResultSets 、 handleCursorResultSets 、 handleOutputParameters )
二、mybatis插件实现
1、实现插件
MyBatis 插件的实现必须实现 Interceptor 接口, 该接口有如下三个方法
- org.apache.ibatis.plugin.Interceptor.intercept(Invocation):插件对业务进行增强的核心方法;
- org.apache.ibatis.plugin.Interceptor.plugin(Object):target
是被拦截的对象,它的作用就 是给被拦截的对象生成一个代理对象; - org.apache.ibatis.plugin.Interceptor.setProperties(Properties):读取在
plugin 中设置的参数;
@Intercepts({
//拦截StatementHandler类的query方法
@Signature(type=StatementHandler.class,method="query",args={Statement.class, ResultHandler.class})
// @Signature(type=StatementHandler.class,method="query",args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
})
public class testInterceptor implements Interceptor {
private long threshold;
@Override
public Object intercept(Invocation invocation) throws Throwable {
long begin = System.currentTimeMillis();
Object ret = invocation.proceed();
//处理逻辑
return ret;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
this.threshold = Long.valueOf(properties.getProperty("threshold"));
}
}
2、mybatis.config.xml文件中引入插件
<plugins>
<plugin interceptor="com.mybatis.Interceptors.testInterceptor">
<property name="threshold" value="10"/>
</plugin>
</plugins>
三、mybatis插件实现源码分析
1、插件注入
在解析这核心配置文件mybatis-config.xml文件时就会解析这个插件节点,解析后,会将这些类加到configuration的InterceptorChain 对象中
pluginElement(root.evalNode("plugins"));
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
2、先找到BaseExecutor类的doQuery方法
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
flushStatements();
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
3、查看configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);这个方法
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);//加入所有插件实际上就是创建statementHandler 的代理对象
return statementHandler;
}
4、查看interceptorChain.pluginAll方法
public Object pluginAll(Object target) {
//遍历publins里面所有我们定义的interceptors,这里只定了一个ThresholdInterceptor类,所以查看这个ThresholdInterceptor的plugin方法
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
5、interceptor实现类的pulugin方法
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
//Plugin的wrap方法:
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
//实现类,这个实现类的invoke()方法又调用interceptor的interceptor方法,所以最后调的就是我们实现的拦截类的interceptor方法
new Plugin(target, interceptor, signatureMap));
}
return target;
}
new plugin()对象的invoke()方法:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
所以在调用stmt = handler.prepare(connection, transaction.getTimeout());这个方法时,会调用插件的invoke方法。
总结: 实际上插件也是通过动态代理实现的,在有插件的地方判断这个对象是否需要增强,如果需要增强就返回增强的代理类
四、mybaits支持插件采用了责任链模式
责任链模式: 就是把一件工作分别经过链上的各个节点,让这些节点依次处理这个工作;和装饰器模式不同,每个节点都知道后继者是谁;适合为完成同一个请求需要多个处理类的场景;
每个节点都知道后继者是谁,但现在一遍节点顺序都通过配置文件来定义,比如mybaits plugins的顺序是在mybatis-config.xml文件中定义的
<plugins>
<plugin interceptor="com.mybatis.Interceptors.ThresholdInterceptor">
<property name="threshold" value="10"/>
</plugin>
<plugin interceptor="com.test.mybatis.PageInterceptor">
<property name="pageSizeZero" value="true" />
</plugin>
</plugins>
要素分析
Handler:定义了一个处理请求的标准接口;
ConcreteHandler:具体的处理者,处理它负责的部分,根据业务可以结束处理流程,也可以将请求转发给它的后继者;
client :发送者,发起请求的客户端;
责任链模式优点:
降低耦合度。它将请求的发送者和接收者解耦。
简化了对象。使得对象不需要知道链的结构。
增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
增加新的请求处理类很方便。