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

责任链模式(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 <br>
 * 2) Logging and Auditing Filters <br>
 * 3) Image conversion Filters <br>
 * 4) Data compression Filters <br>
 * 5) Encryption Filters <br>
 * 6) Tokenizing Filters <br>
 * 7) Filters that trigger resource access events <br>
 * 8) XSL/T filters <br>
 * 9) Mime-type chain Filter <br>
 * @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”的方式来实现责任链。
在这里插入图片描述

Dobbo 中的Filter

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

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

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    // 只获取满足条件的Filter
    List<Filter> 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<T> next = last;
            last = new Invoker<T>() {
                ...
                public Result invoke(Invocation invocation) throws RpcException {
                    return filter.invoke(next, invocation);
                }
                ...
            };
        }
    }
    return last;
}

在这里插入图片描述
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;
    }
}

在这里插入图片描述
综合三种方式,Servlet是相对比较清晰,又易于实现的方式,而Dubbo和Mybatis则适合在原有代码基础上,增加责任链模式代码改动量最小的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值