框架源码中的三种责任链模式

责任链模式(Chain of Responsibility Pattern),为请求创建一个接收者对象的链,对请求发送者和接收者进行解耦,属于行为型模式。

应用实例

  1. JS中的冒泡实践;
  2. Tomcat中对Encoding的处理;
  3. Strut2的拦截器;
  4. Java Servlet中的Filter;
  5. Dobbo中的FIiter;
  6. MyBatis中的Plugin插件;

Servlet中的Filter

Servlet API中定义了Filter和FilterChain接口,代码如下:

package javax.servlet;
import java.io.IOException;

public interface FilterChain {
    public void doFilter ( ServletRequest request, ServletResponse response ) throws IOException, ServletException;
}
package javax.servlet;
import java.io.IOException;

/**
 *  官方给出的实例如下面这些
 * 1) Authentication Filters 
* 2) Logging and Auditing Filters
* 3) Image conversion Filters
* 4) Data compression Filters
* 5) Encryption Filters
* 6) Tokenizing Filters
* 7) Filters that trigger resource access events
* 8) XSL/T filters
* 9) Mime-type chain Filter
* @since Servlet 2.3 */ public interface Filter { // 初始化方法,实例化的时候执行,只执行一次 public void init(FilterConfig filterConfig) throws ServletException; // 执行过滤 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; // 销毁方法,容器关闭的时候执行 public void destroy(); }

Tomcat提供了FilterChain的一个实现:ApplicationFilterChain,主要代码片段如下:

// 当前执行filter的offset
private int pos = 0;
// 当前filter的数量 
private int n; 
 //filter配置类,通过getFilter()方法获取Filter
private ApplicationFilterConfig[] filters; 
// 最终要执行的Servlet
private Servlet servlet

// 代码做了流程简化,去除了无关的操作
private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // 如果还有过滤器,就执行下一个
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = filterConfig.getFilter();
            // 此处把过滤器链,也就是当前对象(this)传给过滤器,过滤器执行后又反过来调用这个方法,相互调用构成链
            filter.doFilter(request, response, this);
            return;
        }
        // 当过滤器链执行完毕的时候,执行以下代码
        // 最终执行的Servlet
        servlet.service(request, response);
 }

流程如下,这里没有构建实际的链表,只是通过“A->B B->A”的方式来实现责任链。
框架源码中的三种责任链模式_第1张图片

Dobbo 中的Filter

Dubbo中使用不同的方法,通过把Filter封装成Invoker的匿名类,再构建成责任链,代码如下:

//  类com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper

private static  Invoker buildInvokerChain(final Invoker invoker, String key, String group) {
    Invoker last = invoker;
    // 只获取满足条件的Filter
    List filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
    if (filters.size() > 0) {
        for (int i = filters.size() - 1; i >= 0; i --) {
            final Filter filter = filters.get(i);
            final Invoker next = last;
            last = new Invoker() {
                ...
                public Result invoke(Invocation invocation) throws RpcException {
                    return filter.invoke(next, invocation);
                }
                ...
            };
        }
    }
    return last;
}

框架源码中的三种责任链模式_第2张图片
Dubbo则用Invoker把Filter用匿名类的形式封装起来,构建了一条具体的链式结构。

Mybatis中的Plugin

Mybatis可以配置各种Plugin,无论是官方提供的还是自己定义的,Plugin和Filter类似,就在执行Sql语句的时候做一些操作。Mybatis的责任链则是通过动态代理的方式,使用Plugin代理实际的Executor类。(这里实际还使用了组合模式,因为Plugin可以嵌套代理),核心代码如下:

public class Plugin implements InvocationHandler{
    private Object target;
    private Interceptor interceptor;
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (满足代理条件) {
            return interceptor.intercept(new Invocation(target, method, args));
        }
        return method.invoke(target, args);
    }
   
    //对传入的对象进行代理,可能是实际的Executor类,也可能是Plugin代理类
    public static Object wrap(Object target, Interceptor 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;
    }
}

框架源码中的三种责任链模式_第3张图片
综合三种方式,Servlet是相对比较清晰,又易于实现的方式,而Dubbo和Mybatis则适合在原有代码基础上,增加责任链模式代码改动量最小的。

你可能感兴趣的:(设计模式,java)