透过源码学习设计模式1—Servlet Filter和责任链模式
Posted java达人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了透过源码学习设计模式1—Servlet Filter和责任链模式相关的知识,希望对你有一定的参考价值。
Servlet filter 一瞥
相信大家都熟悉Servlet中Filter过滤器,我们可以在servlet和servlet容器之间插入Filter过滤器来包装、预处理请求,或者包装、处理响应。过滤器本身不知道自己的顺序。而是由FilterChain按照web.xml中的配置顺序执行的,确切的说,按照<filter-mapping>
Filter实现类的示例如下:
import javax.servlet.*;
import java.util.*;
//实现 Filter 类
@Slf4j
public class LogFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
// 获取初始化参数
String preInfo = config.getInitParameter(“preInfo");
// 输出初始化参数
log.info("preInfo: " + preInfo);
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
// 取得根目录所对应的绝对路径:
String currentURL = request.getRequestURI();
currentURL = currentURL.replaceAll("//+","/");
// 把请求传回过滤链
chain.doFilter(request,response);
}
public void destroy( ){
/* 在 Filter 实例被 Web 容器从服务移除之前调用 */
}
}
每个Filter过滤器实现javax.servlet.Filter接口,其中包含一个doFilter()方法,该方法接受一个request,resonse对以及filterChain作为参数输入,filterChain实现javax.servlet.FilterChain接口(由servlet容器提供),当请求到来时,它将会管理与该请求相关的一系列过滤器的执行,当过滤器执行完毕,doFilter接下来就会调用servlet的service()方法。
想象一下,如果没有filter机制,我们可能会把所有的逻辑写在一处,里面堆积着一堆if….else,这样是违反“单一职责原则”的,就算我们把这些逻辑拆分为多个类,假如后期增加或删除某些节点,或者调整节点顺序,就要修改源码,进行严格测试。而servlet filter使用了责任链模式,很好地解决了这个设计问题。
责任链模式
责任链模式包含一份请求对象和一系列执行对象,每个执行对象都定义了可以执行哪些请求对象,剩下的请求对象就传递给链中的下一个执行对象。也可以添加新的执行对象到链尾,这样责任链和if ... else if ... else if ....... else … endif 在语义上比较类似。但他的条件模块可以在运行时动态编排和配置。
责任链模式解决的问题:
1. 请求者和接受者松散耦合
在责任链模式中,请求者并不知道接受者是谁,也不知道具体如何处理。请求者只负责向责任链发出请求就可以了,该模式下可以有多个接受者处理对象,每个接受者只负责处理自己的部分,其他的就交给其他的接受者去处理。请求在链中传递,接受者处理该请求,或者传递给链中下一个接受者。请求者不再和特定接受者紧密耦合。
2. 通过改变链内成员的或者调整它们的次序,允许你动态地新增或删除责任。
细看FilterChain
我们以tomcat中的ApplicationFilterFactory和ApplicationFilterChain为例,看看他做了什么,首先,我们看看它是怎么样动态编排一条责任链的呢:
ApplicationFilterFactory 的createFilterChain方法:
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
// 如果没有相关servlet,返回null
if (servlet == null)
return null;
// 创建、初始化一个FilterChain对象
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
// Security: Do not recycle
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// Request dispatcher in use
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// 获取上下文中的filterMapping
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// 如果没有 filter mappings, 完成。
if ((filterMaps == null) || (filterMaps.length == 0))
return (filterChain);
// Acquire the information we will need to match filter mappings
DispatcherType dispatcher =
(DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
}
String servletName = wrapper.getName();
// 添加相关path-mapped的filter到filterChain
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);
}
// Add filters that match on servlet name second
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Return the completed filter chain
return filterChain;
}
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);
后面截取的这段中,它会按路径(url path)匹配规则筛选Filter,匹配成功,则返回true,将该filter添加到filterChain,否则就不添加,这样针对不同的请求URL,形成了相应的filterChain,达到动态编排的效果。而在链具体执行过程中,FilterChain发挥了巨大的作用,看ApplicationFilterChain部分源码:
ApplicationFilterChain的dofilter方法和internalDoFilter()方法:
/**
*触发下一个filter,传递request,response,如果链中没有filter,就触发service()
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet exception occurs
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
internalDoFilter(request,response);
}
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// 如果有,获取下一个过滤器
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
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.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 {
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);
}
}
}
ApplicationFilterChain 会调用doFilter(request, response)方法,在doFilter里面会调用internalDoFilter(request,response)方法,该方法拿到每个过滤器,然后过滤器调用自身的doFilter(request, response, filterChain)方法(实现了Filter接口)。
Filter接口doFilter定义如下:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
过滤器链里面的filter在调用dofilter完成后,会继续调用chain.doFilter(request,response)方法,而这个chain其实就是ApplicationFilterChain,所以调用过程又回到了上面调用dofilter和调用internalDoFilter方法,以此类推直到里面的过滤器全部执行。通过FilterChain,每个过滤器相当于间接拥有了下一个Filter的引用,将请求在链中传递,接受者处理相应请求或者将请求传递给下一个接受者。
java达人
ID:drjava
(长按或扫码识别)
以上是关于透过源码学习设计模式1—Servlet Filter和责任链模式的主要内容,如果未能解决你的问题,请参考以下文章