Plugin 是扩展 MyBatis 的重要方式:
插件代码:
// ExamplePlugin.java@Intercepts({@Signature(type= Executor.class,method = "update",args = {MappedStatement.class,Object.class})})public class ExamplePlugin implements Interceptor {private Properties properties = new Properties();public Object intercept(Invocation invocation) throws Throwable {// implement pre processing if needObject returnObject = invocation.proceed();// implement post processing if needreturn returnObject;}public void setProperties(Properties properties) {this.properties = properties;}}
配置文件:
<!-- mybatis-config.xml --><plugins><plugin interceptor="org.mybatis.example.ExamplePlugin"><property name="someProperty" value="100"/></plugin></plugins>
说明:
interceptor
,使用注解 @Intercepts
指定应该怎么拦截,@Signature
就是具体的拦截规则。mybatis-config.xml
中配置 <plugins>
, 注意:需要在<environments>
标签之前配置(mybatis3.dtd
配置文件规范)。@Signature
注解 args 需要指定这两个参数吗?其实就是你拦截的 method 也是 update
他的参数类型,里面他会通过反射进行调用,所有需要参数类型,来确认是哪个 method,就这么简单。<Plugin>
标签// XMLConfigBuilderprivate void parseConfiguration(XNode root) {try {// 略...// issue #117 read properties first// 解析 <properties>propertiesElement(root.evalNode("properties"));// 解析 <plugins>pluginElement(root.evalNode("plugins"));// 解析 <objectFactory>objectFactoryElement(root.evalNode("objectFactory"));// 略...} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}// XMLConfigBuilderprivate 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);}}}
说明:
configuration
中,调用 addInterceptor
,最终添加到 InterceptorChain
中。plugin 采用的是一个 责任链 的方式调用,代码如下:
// InterceptorChainpublic class InterceptorChain {private final List<Interceptor> interceptors = new ArrayList<>();public Object pluginAll(Object target) {// tip: 插件调用链for (Interceptor interceptor : interceptors) {// 获取 interceptor 插件,返回一个 proxy 对象target = interceptor.plugin(target);}return target;}// 略...}// Interceptorpublic interface Interceptor {Object intercept(Invocation invocation) throws Throwable;default Object plugin(Object target) {// 包装一下,返回一个 proxyreturn Plugin.wrap(target, this);}default void setProperties(Properties properties) {// NOP}}// Pluginpublic class Plugin implements InvocationHandler {public static Object wrap(Object target, Interceptor interceptor) {// tips: 用于包装一个 plugin,的代理对象Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);Class<?> type = target.getClass();Class<?>[] interfaces = getAllInterfaces(type, signatureMap);if (interfaces.length > 0) {// 创建 proxy 代理对象(Plugin)return Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}return target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// tip: 调用 Plugin Proxy对象 -> 根据对象的 method 获取方法的class// tip: -> 根据Class获取需不需要拦截,需要拦截就调用 intercept 进行拦截处理// 1、通过 method.getDeclaringClass() 获取,这个方法的 class// 2、获取 @Intercepts @Signature 注解的信息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);}}}
说明:
InterceptorChain
是一个 责任链,里面是我们添加的 plugin
。
重点在,interceptor.plugin
这个方法,里面创建了一个 Plugin
; Plugin
他是一个代理对象,实现了 InvocationHandler
这个接口。
invoke 方法这里比较有趣,每个 proxy 保存了注解解析的信息,也就是需要拦截的方法,在调用 proxy 的时候,会进入 invoke,通过 method 反推 class,然后再判断是否需要拦截。
Execute、ParameterHandler、ResultSetHandler、StatementHandler 都会调用插件(具体都在实在 Configuration 里面)。
// Configurationpublic 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) {// 路由选择 StatementHandlerStatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);// 调用所有 pluginstatementHandler = (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);}// 开启缓存 默认trueif (cacheEnabled) {executor = new CachingExecutor(executor);}// 插件(这是个 proxy)executor = (Executor) interceptorChain.pluginAll(executor);return executor;}
interceptorChain.pluginAll
的方法都会创建对于的,代理对象,然后每次调用的时候,有限经过 Plugin 代理对象。// Configurationpublic 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);}// 开启缓存 默认trueif (cacheEnabled) {executor = new CachingExecutor(executor);}// 插件(这是个 proxy)executor = (Executor) interceptorChain.pluginAll(executor);return executor;}
// Pluginprivate static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {// 获取 Interceptor 的 @Intercepts 注解信息Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);// issue #251if (interceptsAnnotation == null) {throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());}// 获取 @Signature 签名信息Signature[] sigs = interceptsAnnotation.value();Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();// 每个 @Signature 都是一个 methodfor (Signature sig : sigs) {Set<Method> methods = signatureMap.computeIfAbsent(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;}
完结~