插件

栏目: 数据库 · Mysql · 发布时间: 5年前

内容简介:使用的是责任链模式,在四大对象调度的时候,使用插件执行代码,去执行操作即初始化的时候在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
调用生成代理对象

在这里,完成一个插件的开发


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Introduction to Linear Optimization

Introduction to Linear Optimization

Dimitris Bertsimas、John N. Tsitsiklis / Athena Scientific / 1997-02-01 / USD 89.00

"The true merit of this book, however, lies in its pedagogical qualities which are so impressive..." "Throughout the book, the authors make serious efforts to give geometric and intuitive explanations......一起来看看 《Introduction to Linear Optimization》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具