系列
- MyBatis攔截器原理介紹
- Mybatis攔截器改寫請(qǐng)求參數(shù)和結(jié)果
- Mybatis 插件兼容動(dòng)態(tài)SQL
- mybatis 參數(shù)解析流程附相關(guān)案例
- 再談mybatis執(zhí)行流程
Mybatis攔截器介紹
MyBatis 允許你在映射語句執(zhí)行過程中的某一點(diǎn)進(jìn)行攔截調(diào)用塑陵。默認(rèn)情況下,MyBatis 允許使用插件來攔截的方法調(diào)用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 攔截執(zhí)行器的方法
- ParameterHandler (getParameterObject, setParameters) 攔截參數(shù)的處理
- ResultSetHandler (handleResultSets, handleOutputParameters) 攔截結(jié)果集的處理
- StatementHandler (prepare, parameterize, batch, update, query) 攔截Sql語法和會(huì)話構(gòu)建的處理
- mybatis在執(zhí)行過程中按照Executor => StatementHandler => ParameterHandler => ResultSetHandler也颤。
- Executor在執(zhí)行過程中會(huì)創(chuàng)建StatementHandler,在創(chuàng)建StatementHandler過程中會(huì)創(chuàng)建 ParameterHandler和ResultSetHandler葫隙。
Mybatis攔截器的使用
---------定義攔截器
@Intercepts({
@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@Signature(type = Executor.class, method = "createCacheKey", args = {MappedStatement.class, Object.class, RowBounds.class, BoundSql.class})
})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need
Object returnObject = invocation.proceed();
// implement post processing if need
return returnObject;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
---------mybatis全局xml配置
<plugins>
<plugin interceptor="org.format.mybatis.cache.interceptor.ExamplePlugin"></plugin>
</plugins>
自定義一個(gè)mybatis的攔截器步驟包含:
- 定義實(shí)現(xiàn)org.apache.ibatis.plugin.Interceptor接口的類娄帖。
- 在mybatis的全局配置xml中配置插件plugin。
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps) throws SQLException;
}
public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
public interface StatementHandler {
Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
void parameterize(Statement statement) throws SQLException;
void batch(Statement statement) throws SQLException;
int update(Statement statement) hrows SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler) hrows SQLException;
<E> Cursor<E> queryCursor(Statement statement) hrows SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
攔截點(diǎn)說明:
- 不同攔截點(diǎn)能夠支持?jǐn)r截的方法麦撵。
- 攔截方法的type上遥、method搏屑、args等都取自上述方法。
Mybatis攔截器原理
攔截器的原理核心的內(nèi)容在于攔截器配置解析粉楚、攔截器的職責(zé)鏈構(gòu)造辣恋、攔截器的執(zhí)行,串聯(lián)上述功能后就能傳統(tǒng)整體的流程模软。
攔截器解析
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins")); // 解析插件相關(guān)的參數(shù)
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
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).getDeclaredConstructor().newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance); // 保存攔截器
}
}
}
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>(); // 全局變量保存所有的攔截器
// 織入攔截器
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
// 負(fù)責(zé)保存所有的攔截器
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
// 獲取所有攔截器
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
攔截器解析說明:
- 負(fù)責(zé)解析XML配置文件解析所有的攔截器對(duì)象保存到攔截器鏈InterceptorChain 伟骨。
- InterceptorChain通過pluginAll植入攔截器。
攔截器織入
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 插入攔截器
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
// 插入攔截器
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
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);
return statementHandler;
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 插入攔截器
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
攔截器織入說明:
- 針對(duì)ParameterHandler 燃异、ResultSetHandler 携狭、StatementHandler 、Executor 的攔截器的植入都是通過interceptorChain.pluginAll來實(shí)現(xiàn)回俐。
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
public Object pluginAll(Object target) {
// 遍歷所有的攔截器負(fù)責(zé)進(jìn)行植入
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
}
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
// 通過wrap織入單個(gè)攔截器
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {
// NOP
}
}
public class Plugin implements InvocationHandler {
public static Object wrap(Object target, Interceptor interceptor) {
// 解析攔截器的注解獲取攔截的攔截點(diǎn)和對(duì)應(yīng)的攔截方法
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
// 獲取被攔截對(duì)應(yīng)的信息構(gòu)建JDK的動(dòng)態(tài)代理
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 針對(duì)注解指定的interface進(jìn)行攔截
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
}
攔截器織入說明:
- 攔截器織入依賴于JDK自帶的Proxy.newProxyInstance動(dòng)態(tài)代理來實(shí)現(xiàn)逛腿。
- 返回的對(duì)象是個(gè)Plugin對(duì)象的動(dòng)態(tài)代理。
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
// 1仅颇、獲取所有攔截的Intercepts注解
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
// 2单默、獲取注解的內(nèi)容
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
// 3、遍歷注解按照type + method維度進(jìn)行構(gòu)建
for (Signature sig : sigs) {
Set<Method> methods = MapUtil.computeIfAbsent(signatureMap, sig.type(), k -> new HashSet<>());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
Set<Class<?>> interfaces = new HashSet<>();
while (type != null) {
// 獲取攔截點(diǎn)對(duì)應(yīng)的Interfaces并且在攔截點(diǎn)注解配置的的interface用以構(gòu)建動(dòng)態(tài)代理
for (Class<?> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class<?>[0]);
}
攔截器織入說明:
- 通過解析注解@Intercepts注解@Signature來構(gòu)建class + method兩個(gè)維度的map對(duì)象灵莲。
- 以@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)為例,會(huì)構(gòu)建key為ParameterHandler殴俱,value為setParameters的map
攔截器執(zhí)行
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 判斷是否是被攔截的方法政冻,如果是就通過攔截器進(jìn)行處理
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
// 由攔截器interceptor負(fù)責(zé)處理
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
}
JDK動(dòng)態(tài)代理
public class Main {
public static void main(String[] args) throws Exception {
final Dog dog = new Dog();
IDog proxy = (IDog)Proxy.newProxyInstance(Dog.class.getClassLoader(), Dog.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(dog, args);
}
});
proxy.eat();
Thread.sleep(100000);
}
}
-------------------------對(duì)應(yīng)生成的動(dòng)態(tài)代碼
package com.sun.proxy;
import com.sunboy.IDog;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements IDog {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.sunboy.IDog").getMethod("eat", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void eat() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
- JDK動(dòng)態(tài)代理生成的代理類如$Proxy0枚抵,通過arthas去反編譯類。
- JDK動(dòng)態(tài)代理通過反射獲取Method對(duì)象用以后續(xù)的執(zhí)行明场。