table of Contents
@
If you have not had a custom interceptor, you can see my previous article . If you do not know how to use the JDK dynamic proxy, I can see this article . Chain of Responsibility design pattern is very simple to understand, you can look online to find examples.
mybatis
Plug the principle of using a dynamic agent and the chain of responsibility to achieve.
1 which methods to intercept
In front of said, you can comment Intecepts
and Signature
to specify which method interception. However, it does not mean that all of the methods can be intercepted.
Method mybatis interceptor intercepted, there are the following types:
1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
2. ParameterHandler (getParameterObject, setParameters)
3. ResultSetHandler (handleResultSets, handleOutputParameters)
4. StatementHandler (prepare, parameterize, batch, update, query)
Why are these methods can intercept it?
In mybatis
the above several classes are SqlSession
four objects. SqlSession
These objects are achieved by the operation of the database, the result of the processing. Therefore, in the process, the method is to intercept these objects is a very important role.
And it is reflected in the source code, in Configuration
class, the importance of this class do not do too much exposition, you can see the previous article.
Xml parsed into the total Configuration
process, the need for more than a few new class. The above will call back in a few class interceptorChain#pluginAll
method.
2 How the Agent
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;
}
/**
* 添加拦截器
*
* @param interceptor
*/
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
/**
* 获取拦截器列表
*
* @return
*/
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
It can be seen that the interceptor will call all the plugin
way to return a proxy object after the final layers of agents. Pay attention to the layers of agents here.
If there are A, B, C three interceptor (the same signature), in this case, layers are encapsulated. Finally, when executed, it is A> B> C> target.proceed ()> C> B> A.
3 proxy object
InterceptorChain
Each interceptor will call the plugin
method. This method will return the corresponding proxy object.
/**
* 拦截器接口
*
* @author Clinton Begin
*/
public interface Interceptor {
/**
* 执行拦截逻辑的方法
*
* @param invocation 调用信息
* @return 调用结果
* @throws Throwable 异常
*/
Object intercept(Invocation invocation) throws Throwable;
/**
* 代理
*
* @param target
* @return
*/
Object plugin(Object target);
/**
* 根据配置来初始化 Interceptor 方法
* @param properties
*/
void setProperties(Properties properties);
}
Which plugin
is we need to achieve. The mybatis also provides us with a very convenient way.
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,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
When we rewrite plugin method, simply call the above method can be. It will return Plugin
an object of this class.
public class Plugin implements InvocationHandler
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 获取方法所在类中, 可以被拦截的所有的方法
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
// 如果需要被拦截, 则调用 interceptor.intercept
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);
}
}
Since JDK dynamic proxy is the interface level. Thus, agents that all methods of the class interface. However, not all methods are to be proxied, therefore, to distinguish the signature process by the information in the notes.
4 Chain of Responsibility design pattern
In the process of using plug-in, the chain of responsibility design pattern is reflected in the dynamic proxy deeply nested Agent enhancements. Reflected in the interceptorChain#pluginAll
method. It will call upon layers of proxy. Principle mybatis plug - reflects the chain of responsibility and dynamic proxies