设计模式-拦截过滤器
2020-11-27
简介
拦截过滤器模式(Intercepting Filter Pattern)用于对应用程序的请求或响应做一些预处理/后处理. 定义过滤器, 并在把请求传给实际目标应用程序之前应用在请求上. 过滤器可以做认证/授权/记录日志, 或者跟踪请求, 然后把请求传给相应的处理程序. 以下是这种设计模式的实体.
- 过滤器(Filter): 过滤器在请求处理程序执行请求之前或之后, 执行某些任务.
- 过滤器链(Filter Chain): 过滤器链带有多个过滤器, 并在 Target 上按照定义的顺序执行这些过滤器.
- 目标(Target): 目标对象是请求处理程序.
- 过滤管理器(Filter Manager): 过滤管理器管理过滤器和过滤器链.
- 客户端(Client): Client 是向 Target 对象发送请求的对象.
类图

和过滤器模式(Filter Pattern)的区别
过滤器模式中没有
FilterChain这个概念, 无法阻止对目标的调用. 而拦截过滤器模式由于有这个概念, 在每个Filter执行时选择是否执行FilterChain里的下一个Filter. 如果不执行, 则不会对目标进行调用过滤器模式是对已有对象列表过滤. 而拦截过滤器模式是对调用目标的单个请求的过滤
例子
拦截过滤器模式在 javax.servlet 中的应用
Filter接口
1 | public interface Filter { |
FilterChain接口
1 | public interface FilterChain { |
FilterChain实现类ApplicationFilterChain
1 | final class ApplicationFilterChain implements FilterChain { |
1. Filter初始化过程
ContextConfig#webConfig方法中扫描web.xml文件, 构建WebXml对象1
2
3
4
5
6
7
8
9
10
11
12
13
14/**
* Scan the web.xml files that apply to the web application and merge them
* using the rules defined in the spec. For the global web.xml files,
* where there is duplicate configuration, the most specific level wins. ie
* an application's web.xml takes precedence over the host level or global
* web.xml file.
*/
protected void webConfig() {
...
WebXml webXml = createWebXml();
...
configureContext(webXml);
...
}ContextConfig#configureContext方法将WebXml中解析到的FilterMap列表放入StandardContext中1
2
3
4
5
6
7private void configureContext(WebXml webxml) {
...
for (FilterMap filterMap : webxml.getFilterMappings()) {
context.addFilterMap(filterMap);
}
...
}
2. Filter执行过程
ApplicationDispatcher#doDispatch方法处理请求1
2
3
4private void doDispatch(ServletRequest request, ServletResponse response) throws ServletException, IOException {
...
invoke(state.outerRequest, state.outerResponse, state);
}ApplicationDispatcher#invoke调用方法创建FilterChain, 执行FilterChain的doFilter方法1
2
3
4
5
6
7
8
9
10
11
12
13private void invoke(ServletRequest request, ServletResponse response,
State state) throws IOException, ServletException {
...
ApplicationFilterChain filterChain = ApplicationFilterFactory. createFilterChain(request, wrapper, servlet);
try {
// for includes/forwards
if ((servlet != null) && (filterChain != null)) {
filterChain.doFilter(request, response);
}
// Servlet Service Method is called by the FilterChain
}
...
}ApplicationFilterFactory#createFilterChain提取StandardContext中的Filter列表放入新创建的FilterChain中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
...
filterChain = new ApplicationFilterChain();
...
// Add the relevant path-mapped filters to this filter chain
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
...
}FilterChain的doFilter方法中获取第一个Filter并执行其doFilter方法1
2
3
4
5
6
7
8
9
10
11
12public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
...
internalDoFilter(request,response);
...
}
private void internalDoFilter(ServletRequest request, ServletResponse response){
...
filter.doFilter(request, response, this);
...
}
应用
业务代码中应用的可行性探讨
参数校验上使用的可行性
通过拦截过滤器模式进行参数校验. 满足过滤器条件的继续执行, 不满足的直接返回.
使用注解关联调用类和过滤器