内容简介:使用的是责任链模式,在四大对象调度的时候,使用插件执行代码,去执行操作即初始化的时候在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插件到开源库
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。