最近为公司一个项目写一个简单的网关,其实也就是一个filter做一些token解析/校验。但是光这一个filter就不简单,现在就说说里面运用的设计模式。
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层,体会到了设计的精妙。
还是以具体场景为例,我在写网关过滤器的时候需要在过滤器中临时新加一些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 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 getHeaders(String name) {
Enumeration 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 getHeaderNames() {
// create a set of the custom header names
Set set = new HashSet<>(customHeaders.keySet());
// now add the headers from the wrapped request object
@SuppressWarnings("unchecked")
Enumeration 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的内容,但是设计模式处处都有,所以多看一些优秀的源码来提高自己的编码水平还是有道理的