内容简介:使用的是责任链模式,在四大对象调度的时候,使用插件执行代码,去执行操作即初始化的时候在Config对象中,由于是单例,全局唯一,生成的插件对象保存进入数组中,这个插件需要实现一个接口,然后在使用插件的时候,通过责任链的方式,循环调用,进行处理.核心在于下面这一段
原理
使用的是责任链模式,在四大对象调度的时候,使用插件执行代码,去执行操作
即初始化的时候在Config对象中,由于是单例,全局唯一,生成的插件对象保存进入数组中,这个插件需要实现一个接口,然后在使用插件的时候,通过责任链的方式,循环调用,进行处理.
核心在于下面这一段
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
使用插件
使用插件需要实现接口Interceptor
package com.ming.MyBatis; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Invocation; import java.util.Properties; public class PluginElement implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { return null; } @Override public Object plugin(Object target) { return null; } @Override public void setProperties(Properties properties) { } }
插件代理和反射设计
首先进入执行器
executor = (Executor) interceptorChain.pluginAll(executor);
然后执行器执行在这
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
plugin为生成代理对象的方法,逐步传递.
一层一层拦截处理
MyBatis提供了插件类,用来生成代理对象,用来上方循环
/** * Copyright 2009-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ibatis.plugin; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.ibatis.reflection.ExceptionUtil; /** * @author Clinton Begin */ public class Plugin implements InvocationHandler { private final Object target; private final Interceptor interceptor; private final Map<Class<?>, Set<Method>> signatureMap; private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { this.target = target; this.interceptor = interceptor; this.signatureMap = signatureMap; } 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; } @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); } } private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) { 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()); } Signature[] sigs = interceptsAnnotation.value(); Map<Class<?>, Set<Method>> signatureMap = new HashMap<>(); for (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; } private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) { Set<Class<?>> interfaces = new HashSet<>(); while (type != null) { for (Class<?> c : type.getInterfaces()) { if (signatureMap.containsKey(c)) { interfaces.add(c); } } type = type.getSuperclass(); } return interfaces.toArray(new Class<?>[interfaces.size()]); } }
这是一个动态代理对象.
wrap是生成的动态代理对象.
invoke是调度的方法.
如果存在拦截签名,将会在这里调用
若不存在拦截签名,将会直接调用原生的方法
插件开发
看上方的代码,可以知道需要确定拦截签名用来调用开发好的插件
如果需要拦截StatementHandler 执行 SQL 的过程
需要先查看StatementHandler接口
/** * Copyright 2009-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ibatis.executor.statement; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.List; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.session.ResultHandler; /** * @author Clinton Begin */ 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) throws SQLException; <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor(Statement statement) throws SQLException; BoundSql getBoundSql(); ParameterHandler getParameterHandler(); }
开发插件需要实现Interceptor接口
通过看接口定义可以知道需要拦截
Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
方法
所以编写注解,提供签名如下
// 说明这是拦截器 @Intercepts({ // 需要拦截的方法 @Signature( type = StatementHandler.class, method = "prepare", args = { // 提供参数 Connection.class } ) })
下面需要实现该接口
package com.ming.MyBatis.plugin; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.*; import java.util.Properties; /** * 对数据库返回的数据进行限制插件 * @author ming */ // 说明这是拦截器 @Intercepts({ // 需要拦截的方法 @Signature( type = Executor.class, method = "update", args = { // 提供参数 MappedStatement.class, Object.class } ) }) public class MyPlugin implements Interceptor { Properties properties = null; /** * 用于替换拦截方法的内容,在这里,原始方法被拦截 * @param invocation 此为处理该内容的责任链 * @return * @throws Throwable */ @Override public Object intercept(Invocation invocation) throws Throwable { // 处理之前 System.out.println("before....."); // 进行调用真实的对象,否则调度到下一个代理对象invoke方法 Object object = invocation.proceed(); // 处理之后 System.out.println("after...."); return object; } /** * 生成对象代理,用于源码中的循环 即调用的是这一句 target = interceptor.plugin(target); * 在这里,targer为插件对象,然后在这里进行循环处理,使用默认的wrap进行调用拦截的方法 * @param target * @return */ @Override public Object plugin(Object target) { System.out.println("调用生成代理对象"); // 使用的是wrap方法,用于生成代理对象 return Plugin.wrap(target, this); } /** * 从配置文件中,获取配置属性 初始化的时候会调用此方法 * @param properties */ @Override public void setProperties(Properties properties) { System.out.println(properties.get("dbType")); this.properties = properties; } }
一个是用来调用真实方法的.一个是用来生成代理对象的时候循环生成.最后是读取内容
书写完成以后更改配置文件
<plugins> <plugin interceptor="com.ming.MyBatis.plugin.MyPlugin"> <property name="dbType" value="mysql"/> </plugin> </plugins>
然后运行.
查看日志信息,可以看到
[2019-04-21 01:16:41,506] Artifact mingmingxiao:war exploded: Artifact is deployed successfully [2019-04-21 01:16:41,506] Artifact mingmingxiao:war exploded: Deploy took 2,203 milliseconds mysql 调用生成代理对象 调用生成代理对象 调用生成代理对象 调用生成代理对象 调用生成代理对象 21-Apr-2019 01:16:49.028 信息 [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployDirectory 把web 应用程序部署到目录 [/home/ming/apache-tomcat-9.0.17/webapps/manager] 21-Apr-2019 01:16:49.107 信息 [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/home/ming/apache-tomcat-9.0.17/webapps/manager] has finished in [79] ms 调用生成代理对象
在这里,完成一个插件的开发
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- IDEA 插件:多线程文件下载插件开发
- 从头开发一个Flutter插件(二)高德地图定位插件
- Gradle插件开发系列之gradle插件调试方法
- Gradle插件开发系列之开发第一个gradle插件
- WordPress插件开发 -- 在插件使用数据库存储数据
- Gradle插件开发系列之发布gradle插件到开源库
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Learning jQuery
Jonathan Chaffer、Karl Swedberg / Packt Publishing / 2007-7-7 / GBP 24.99
jQuery is a powerful JavaScript library that can enhance your websites regardless of your background. In this book, creators of the popular jQuery learning resource, learningquery.com, share the......一起来看看 《Learning jQuery》 这本书的介绍吧!