最近为公司一个项目写一个简单的网关,其实也就是一个filter做一些token解析/校验。但是光这一个filter就不简单,现在就说说里面运用的设计模式。

Filter的核心-责任链模式

java优雅使用责任链模式 spring 责任链模式_java优雅使用责任链模式


Filter: 属于javaweb中一个重要的组件,对请求资源进行处理(request和response)。

FilterChain: 管理着所有filter的顺序和执行,就像一个链条。通过这种链式串联,我们就可以对同一种对象资源实现不同业务场景的处理,达到业务解耦。

再看看spring里的源码实现:

FilterChain实现:

public final class ApplicationFilterChain implements FilterChain {

    /**
     * Filters.
     */
    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
    
    /**
     * The int which is used to maintain the current position
     * in the filter chain.
     */
    private int pos = 0;

    /**
     * The int which gives the current number of filters in the chain.
     */
    private int n = 0;
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {
            internalDoFilter(request,response);
    }


private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) {
                 ApplicationFilterConfig filterConfig = filters[pos++];
                Filter filter = filterConfig.getFilter();
                filter.doFilter(request, response, this);
       }  
}
    
}

Filter的一个实现:

public class GateWayFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
       //这里填充业务代码
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

我删除了绝大部分跟涉及模式无关的代码,这个FilterChain 和 Filter 做了什么就很清晰了。
FilterChain 拥有 所有filter集合的配置类(ApplicationFilterConfig),每次请求都会 初始化 pos(filter数组下标) 和 n (filter过滤器总数)。 然后每次走完第一个 filter,都会执行 filterChain对象 的 doFilter继续执行下一个filter。

filterChain.doFilter(servletRequest, servletResponse);

责任链模式让我们的request 和response经过数十个filter的处理后(各个filter之间不耦合,可拓展性强),最终到达spring 的endpoint层,体会到了设计的精妙。

Filter的得力助手-装饰器模式

还是以具体场景为例,我在写网关过滤器的时候需要在过滤器中临时新加一些header信息,本来以为request会提供addHeader的方法,但是很遗憾,这个方法需要自己来实现。很明显的是,我们需要增强HttpServletRequest接口。这时候HttpServletRequestWrapper装饰器类登场了,它的构造器只接收一个HttpServletRequest对象,并且可以方便覆写HttpServletRequest的方法。

public class HttpServletRequestWrapper extends ServletRequestWrapper implements
        HttpServletRequest {

    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     *
     * @throws java.lang.IllegalArgumentException
     *             if the request is null
     */
    public HttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }
    
	。。。。。。
 }

我们写一个继承HttpServletRequestWrapper的类。增加一个map变量,然后新增一个putHeader方法,用来实现我们增加header信息的目的,但是这样别人调用getHeaders方法还是取不到我们新增的键值对,我们需要复写getHeaders方法来实现目的。

class MutableHttpServletRequest extends HttpServletRequestWrapper {
        private final Map<String, String> customHeaders;

        public MutableHttpServletRequest(HttpServletRequest request) {
            super(request);
            this.customHeaders = new HashMap<>();
        }

        public void putHeader(String name, String value) {
            this.customHeaders.put(name, value);
        }

        @Override
        public Enumeration<String> getHeaders(String name) {
            Enumeration<String> headers = super.getHeaders(name);
            if (null == headers || !headers.hasMoreElements()) {
                String value = customHeaders.get(name);
                if (StringUtils.isNotBlank(value)) {
                    return Collections.enumeration(Arrays.asList(value));
                }
            } else {
                return headers;
            }
            return null;
        }

        @Override
        public String getHeader(String name) {
            // check the custom headers first
            String headerValue = customHeaders.get(name);

            if (StringUtils.isNotBlank(headerValue)) {
                return headerValue;
            }
            // else return from into the original wrapped object
            return super.getHeader(name);
        }

        @Override
        public Enumeration<String> getHeaderNames() {
            // create a set of the custom header names
            Set<String> set = new HashSet<>(customHeaders.keySet());

            // now add the headers from the wrapped request object
            @SuppressWarnings("unchecked")
            Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
            while (e.hasMoreElements()) {
                // add the names of the request headers into the list
                String n = e.nextElement();
                set.add(n);
            }

            // create an enumeration from the set and return
            return Collections.enumeration(set);
        }
    }
结语

虽然filter并不是spring的内容,但是设计模式处处都有,所以多看一些优秀的源码来提高自己的编码水平还是有道理的