Plugin Introduction
Generally, open-source frameworks provide plugins or extension points for developers to extend on their own. The benefits are obvious: first, it increases the flexibility of the framework; second, developers can extend the framework according to actual needs to make it work better.
Taking MyBatis as an example, we can implement pagination, table sharding, and monitoring functions based on the MyBatis plugin mechanism. Since plugins are unrelated to business and businesses cannot perceive the existence of plugins, plugins can be seamlessly integrated to achieve enhancement invisibly.
MyBatis plugin mechanism allows users to extend MyBatis functionality by implementing plugin interfaces, intercepting SQL statement execution process for custom processing.
Basic Concepts
MyBatis plugin mechanism is based on Java’s interceptor pattern, using plugin classes to intercept and modify MyBatis core behavior. Plugins can insert custom code when performing specific operations (such as query, update, delete, etc.).
Scope of Action
The objects of plugin action are MyBatis internal core components: Executor, StatementHandler, ParameterHandler, and ResultSetHandler:
- Executor: Operations that execute SQL
- StatementHandler: Handles SQL statement generation, parameter setting, etc.
- ResultSetHandler: Handles result set mapping
- ParameterHandler: Handles SQL statement parameter setting
Common Scenarios
- SQL logging
- SQL performance monitoring
- Transaction control
- Caching
- SQL rewriting
Advantages
- Flexibility: Allows developers to flexibly add or modify functions without modifying MyBatis core code
- Extensibility: Can easily extend MyBatis to meet various business needs
- Customizability: Plugins can be customized according to business needs
Disadvantages
- Complex Debugging: Since plugins are implemented through proxy pattern, debugging may increase complexity
- Performance Overhead: Every SQL execution needs to go through plugin interception and processing
Plugin Introduction
MyBatis, as a widely used excellent ORM open-source framework, provides a simple and easy-to-use plugin extension mechanism for four major components: Executor, StatementHandler, ParameterHandler, ResultSetHandler.
MyBatis allows intercepting the following methods:
- Executor: update, query, commit, rollback, etc.
- StatementHandler: prepare, parameterize, batch, updates query, etc.
- ParameterHandler: getParameterObject, setParameters methods
- ResultSetHandler: handlerResultSets, handleOutputParameters, etc.
Plugin Principles
When the four core objects are created, each created object is not returned directly. Instead, it goes through interceptorChain.pluginAll(parameterHandler).
public ParameterHandler createParameterHandler(
MappedStatement mappedStatement,
Object parameter,
BoundSql boundSql,
InterceptorChain interceptorChain) {
ParameterHandler parameterHandler =
mappedStatement.getLang().createParameterHandler(mappedStatement, parameter, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.applyInterceptors(parameterHandler);
return parameterHandler;
}
public Object applyInterceptors(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
interceptorChain saves all interceptors, and the interceptor chain calls interceptors in sequence to intercept or enhance the target.
Custom Plugin
The MyBatis plugin interface Interceptor contains three methods:
interceptormethod: The core method of the pluginpluginmethod: Generates a proxy object for the targetsetPropertiesmethod: Passes parameters required by the plugin
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
})
public class ExamplePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
}
}
Configure the plugin:
<plugins>
<plugin interceptor="icu.wzk.interceptor.ExamplePlugin"></plugin>
</plugins>
Source Code Analysis
Plugin implements the InvocationHandler interface, and the invoke method detects all intercepted methods:
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);
}
}
The Invocation class is used to store the target class, method, and method parameter list:
public class Invocation {
private final Object target;
private final Method method;
private final Object[] args;
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}