设计模式之责任链模式-包含例子

79 阅读3分钟

什么是责任链模式?

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

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

最经典的是servlet的Filter、Spring中各种拦截器

责任链模式的真实场景

一个请求经过A处理,在经过B处理,在经过C处理,会两种处理方式:

  1. 请求会经过A、B、C所有都处理一遍,不会终止。
  2. 请求经过链条中,经过某一处理时会存在中断的情况,并且不会往下执行。比如经过B处理时中断,那么C处理就不会执行。
  3. Servlet中的责任链的模式,实现的效果先经过每个Request,然后经过每个Response过滤。如下图:

image.png

第一种不会终止的例子

import java.util.ArrayList;
import java.util.List;

interface MyStringFilter {

    /**
     * @param message 需要处理的消息
     * @return 返回处理后的消息
     */
    String process(String message);
}


/**
 * 敏感词过滤替换
 */
class SensitiveStringFilter implements MyStringFilter {
    @Override
    public String process(String message) {
        System.out.println("执行敏感词替换......");
        return message.replaceAll("1024", "1000");
    }
}

/**
 * 脚本过滤替换,如<scripr>alter("11")</scripr>
 */
class HtmlStringFilter implements MyStringFilter {
    @Override
    public String process(String message) {
        System.out.println("脚本过滤替换......");
        return message.replaceAll("<", "[").replaceAll(">", "]");
    }
}



/**
 * 什么都不影响,实际在摸鱼
 */
class NothingFilter implements MyStringFilter {
    @Override
    public String process(String message) {
        //doing 模拟不经过处理,但不影响
        System.out.println("什么都不影响,实际在摸鱼......");
        return message;
    }
}


/**
 * 定义个责任链的链条
 */
class StringChainFilter {
    List<MyStringFilter> myStringFilterList = new ArrayList<>();

    public StringChainFilter add(MyStringFilter filter) {
        myStringFilterList.add(filter);
        return this;
    }

    public String process(String message) {
        String processAfter = null;
        for (int i = 0; i < myStringFilterList.size(); i++) {
            processAfter = myStringFilterList.get(i).process(message);
        }
        return processAfter;
    }
}

//测试
class DemoTest {
    public static void main(String[] args) {
        String message = "你好,这是测试责任链模式字符串,<script>alter('1');</script>,1024 快乐";

        StringChainFilter chainFilter = new StringChainFilter();
        chainFilter.add(new SensitiveStringFilter())
        .add(new HtmlStringFilter())
        .add(new NothingFilter());

        String afterMessage = chainFilter.process(message);

        System.out.println("处理前字符串=" + message);
        System.out.println("处理后字符串=" + afterMessage);
    }
}
运行的结果:

执行敏感词替换......
脚本过滤替换......
什么都不影响,实际在摸鱼......
处理前字符串=你好,这是测试责任链模式字符串,<script>alter('1');</script>,1024 快乐
处理后字符串=你好,这是测试责任链模式字符串,<script>alter('1');</script>,1000 快乐

第二种会终止的例子

import java.util.ArrayList;
import java.util.List;

/**
 * Created by lizhiming on 2022/1/20.
 */
public interface MyStringStopFilter {
    /**
     * @param message 需要处理的消息
     * @return 是否需要终止,返回true责任链中断
     */
    boolean process(Message message);
}

class Message {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "Message{" +
                "message='" + message + ''' +
                '}';
    }
}


/**
 * 敏感词过滤替换
 */
class SensitiveStopStringFilter implements MyStringStopFilter {
    @Override
    public boolean process(Message message) {
        System.out.println("执行敏感词替换......");
        String msg = message.getMessage().replaceAll("1024", "1000");
        message.setMessage(msg);
        return true;
    }
}


/**
 * 脚本过滤替换,如<scripr>alter("11")</scripr>
 */
class HtmlStopStringFilter implements MyStringStopFilter {
    @Override
    public boolean process(Message message) {
        System.out.println("脚本过滤替换......");
        String msg = message.getMessage().replaceAll("<", "[").replaceAll(">", "]");
        message.setMessage(msg);
        return false;
    }
}


/**
 * 什么都不影响,实际在摸鱼
 */
class NothingStopStringFilter implements MyStringStopFilter {
    @Override
    public boolean process(Message message) {
        //doing 模拟不经过处理,但不影响
        System.out.println("什么都不影响,实际在摸鱼......");
        return false;
    }
}

/**
 * 处理责任链
 */
class MyStringStopChailFilter {
    List<MyStringStopFilter> myStringStopFilterList = new ArrayList<>();

    public MyStringStopChailFilter add(MyStringStopFilter myStringStopFilter) {
        myStringStopFilterList.add(myStringStopFilter);
        return this;
    }

    public void process(Message message) {
        for (int i = 0; i < myStringStopFilterList.size(); i++) {
            //如果处理返回true,则不往下进行
            if (myStringStopFilterList.get(i).process(message)) {
                break;
            }
        }
    }
}


/**
 * 测试
 */
class MyStringStopFilterDemo {
    public static void main(String[] args) {
        String messageStr = "你好,这是测试责任链模式字符串,<script>alter('1');</script>,1024 快乐";
        Message message = new Message();
        message.setMessage(messageStr);

        System.out.println("处理前字符串=" + message.getMessage());

     
//定义责任链处理顺序, SensitiveStopStringFilter 会中断,
//NothingStopStringFilter不会运行
        MyStringStopChailFilter myStringStopChailFilter = new MyStringStopChailFilter();
        myStringStopChailFilter.add(new HtmlStopStringFilter())
                .add(new SensitiveStopStringFilter())
                .add(new NothingStopStringFilter());
        myStringStopChailFilter.process(message);


        System.out.println("处理后字符串=" + message.getMessage());

    }
}
运行结果:

处理前字符串=你好,这是测试责任链模式字符串,<script>alter('1');</script>,1024 快乐
脚本过滤替换......
执行敏感词替换......
处理后字符串=你好,这是测试责任链模式字符串,[script]alter('1');[/script]1000 快乐

第三种先经过Request,在经过Response

import lombok.Data;

import java.util.ArrayList;
import java.util.List;


@Data
class Request {
    private String requestStr;
}

@Data
class Response {
    private String responseStr;
}


interface FilterRequestResponse {
    void doFilter(Request request, Response response, FilterChain filterChain);
}


class HtmlFilter implements FilterRequestResponse {

    @Override
    public void doFilter(Request request, Response response, FilterChain filterChain) {
        //先执行filter前面的request
        String str = request.getRequestStr();
        str = str.replaceAll(">", "]").replaceAll("<", "[");
        request.setRequestStr(str);

        filterChain.doFilter(request, response);


        response.setResponseStr(response.getResponseStr() + "---经过HtmlFilter处理");
    }
}


class SensitiveFilter implements FilterRequestResponse {

    @Override
    public void doFilter(Request request, Response response, FilterChain filterChain) {
        //先执行filter前面的request
        String str = request.getRequestStr();
        str = str.replaceAll("1024", "1000");
        request.setRequestStr(str);

        filterChain.doFilter(request, response);

        response.setResponseStr(response.getResponseStr() + "---经过SensitiveFilter处理");
    }
}


class FilterChain {
    List<FilterRequestResponse> filterRequestResponses = new ArrayList<>();
    //当前的责任链中的初始位置
    int orderIndex = 0;

    public FilterChain add(FilterRequestResponse filterRequestResponse) {
        filterRequestResponses.add(filterRequestResponse);
        return this;
    }

    public void doFilter(Request request, Response response) {
        //如果当前位置和责任链长度一样,说明每个链条都执行过,就返回
        if (orderIndex == filterRequestResponses.size()) {
            return;
        }
        //拿出第一个过滤器
        FilterRequestResponse requestResponse = filterRequestResponses.get(orderIndex);
        orderIndex++; //++之后,下次运行到上一句就是下一个

        //执行当前责任链的
        requestResponse.doFilter(request, response, this);
    }
}

class Test {
    public static void main(String[] args) {
        Request request = new Request();
        request.setRequestStr("大家好<script>,欢迎,欢迎来到1024 ");
        Response response = new Response();
        response.setResponseStr("我是需要返回值处理的");

        FilterChain filterChain = new FilterChain();
        filterChain.add(new HtmlFilter()).add(new SensitiveFilter());

        filterChain.doFilter(request, response);

        System.out.println(request.getRequestStr());
        System.out.println(response.getResponseStr());
    }
}
运行结果:

大家好[script],欢迎,欢迎来到1000 
我是需要返回值处理的---经过SensitiveFilter处理---经过HtmlFilter处理

servlet中的filter

参考源码:org.apache.catalina.core.ApplicationFilterChain#internalDoFilter
private void internalDoFilter(ServletRequest request,
                              ServletResponse response)
    throws IOException, ServletException {

    // Call the next filter if there is one
    // pos 当前执行filter的偏移量
    // n 当前filter的数量
    if (pos < n) {
        //偏移量+1
        ApplicationFilterConfig filterConfig = filters[pos++];
        try {
            //拿出一个filter
            Filter filter = filterConfig.getFilter();

            if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                    filterConfig.getFilterDef().getAsyncSupported())) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
            }
            if( Globals.IS_SECURITY_ENABLED ) {
                final ServletRequest req = request;
                final ServletResponse res = response;
                Principal principal =
                    ((HttpServletRequest) req).getUserPrincipal();

                Object[] args = new Object[]{req, res, this};
                SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
            } else {
                //执行具体的filter
                filter.doFilter(request, response, this);
            }
        } catch (IOException | ServletException | RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            e = ExceptionUtils.unwrapInvocationTargetException(e);
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(sm.getString("filterChain.filter"), e);
        }
        return;
    }

    // We fell off the end of the chain -- call the servlet instance
    try {
        if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
            lastServicedRequest.set(request);
            lastServicedResponse.set(response);
        }

        if (request.isAsyncSupported() && !servletSupportsAsync) {
            request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                    Boolean.FALSE);
        }
        // Use potentially wrapped request from this point
        if ((request instanceof HttpServletRequest) &&
                (response instanceof HttpServletResponse) &&
                Globals.IS_SECURITY_ENABLED ) {
            final ServletRequest req = request;
            final ServletResponse res = response;
            Principal principal =
                ((HttpServletRequest) req).getUserPrincipal();
            Object[] args = new Object[]{req, res};
            SecurityUtil.doAsPrivilege("service",
                                       servlet,
                                       classTypeUsedInService,
                                       args,
                                       principal);
        } else {
            //filter全部执行完成,继续往下走
            servlet.service(request, response);
        }
    } catch (IOException | ServletException | RuntimeException e) {
        throw e;
    } catch (Throwable e) {
        e = ExceptionUtils.unwrapInvocationTargetException(e);
        ExceptionUtils.handleThrowable(e);
        throw new ServletException(sm.getString("filterChain.servlet"), e);
    } finally {
        if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
            lastServicedRequest.set(null);
            lastServicedResponse.set(null);
        }
    }
}