servlet API提供了一个过滤器接口,如果在开发web APP应用程序时创建的Java类实现了该接口,则该Java类称为过滤器过滤器过滤器
过滤器技术允许在访问某个目标资源之前监听访问请求和响应。 简单地说,web容器可以进行与对某个资源的访问前监听相关联的处理,并且,某个资源可以在向web容器返回响应之前对监听进行处理。
分析源代码的主要目的是什么? 创建FilterChain,了解在代码中如何调用servlet,以及filter如何通过名为doFilter ()的方法实现递归调用
springboot依赖关系:
org.spring帧work.boot
spring-boot-starter-web
分析前查看两幅图,了解filter的执行时机
添加过滤器之前:
添加文件后:
另外,过滤器链不是责任链的设计模式。 因为一个请求可以由链上的多个过滤器处理。 但是,对于责任链来说,一个请求只能由链中的一个handler来处理。
1、建立筛选链
主要看这种方法org.Apache.catalina.core.applicationfilterfactory # createfilterchain
返回空值;
} else {
applicationfilterchainfilterchain=null;
请求语句接口(if ) {
请求req=(请求)请求;
if(globals.is_security_enabled ) {
过滤器chain=newapplicationfilterchain (;
} else {
filter chain=(applicationfilterchain ) req.getFilterChain );
if (过滤器通道==null ) {
过滤器chain=newapplicationfilterchain (;
req.set filter chain (过滤器chain );
}
}
} else {
过滤器chain=newapplicationfilterchain (;
}
//执行以上语句以创建空的过滤器通道对象
过滤器chain.set servlet; //设置调用的servlet
filter chain.setservletsupportsasync (wrapper.isasyncsupported );
标准上下文内容=(标准上下文) wrapper.getParent );
filter map [ ] filter maps=context.findfiltermaps (; //获取所有过滤器的映射对象,保存各过滤器的元数据信息
过滤器地图!=null filterMaps.length!=0}{
.
过滤器映射过滤器映射;
应用程序文件配置文件配置文件; //此对象的保存者filter的bean引用由filterChain引用
for(var12=0; var12 var11; var12({
过滤器映射=var 10 [ var 12 ];
匹配分布器(if ) /请求路径和过滤器设置的路径匹配模式与
过滤器配置=(applicationfilterconfig ) context.findfilterconfig (filter map.getfilter name ) );
过滤器配置!=空) {
过滤器chain.add filter (过滤器配置);
}
}
}
.
for(var12=0; var12 var11; var12({
过滤器映射=var 10 [
var12];if (matchDispatcher(filterMap, dispatcher) && matchFiltersServlet(filterMap, servletName)) {//执行的servlet和filter的selvetNames进行匹配
filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
if (filterConfig != null) {
filterChain.addFilter(filterConfig);
}
}
}
return filterChain;//filterChain创建完成返回
} else {
return filterChain;
}
}
}
2、接下来看如何去调用执行的servlet和形成多个filter形成套娃式的递归调用的
org/apache/catalina/core/StandardWrapperValve.class:153 这个是filterChain的调用入口了
其主要执行的话是下面这个方法
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (this.pos < this.n) {//filtechain就保存filterConfig的长度-》n和当前执行到的filter下标-》post
ApplicationFilterConfig filterConfig = this.filters[this.pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
}
//下面两种方式来调用filter重写的filter方法,参数就是filterChian、request和response,然后在filter我们又调用了filterChain的dofilter(),所以就形成了这么个套娃式的递归调用
if (Globals.IS_SECURITY_ENABLED) {
Principal principal = ((HttpServletRequest)request).getUserPrincipal();
Object[] args = new Object[]{request, response, this};
SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (ServletException | RuntimeException | IOException var15) {
throw var15;
} catch (Throwable var16) {
Throwable e = ExceptionUtils.unwrapInvocationTargetException(var16);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
} else {
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !this.servletSupportsAsync) {
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
}
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse && Globals.IS_SECURITY_ENABLED) {
Principal principal = ((HttpServletRequest)request).getUserPrincipal();
Object[] args = new Object[]{request, response};
SecurityUtil.doAsPrivilege("service", this.servlet, classTypeUsedInService, args, principal);
} else {
this.servlet.service(request, response);//当所有filter都被调用之后就执行了servlet,servlet之后就逐渐的递归返回了
}
} catch (ServletException | RuntimeException | IOException var17) {
throw var17;
} catch (Throwable var18) {
Throwable e = ExceptionUtils.unwrapInvocationTargetException(var18);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set((Object)null);
lastServicedResponse.set((Object)null);
}
}
}
}
整个请求中过滤链的组装和调用过程就是这样了。
进行springboot整合过滤器的过程中可能会遇到过滤器执行两次的问题,针对这个问题可能出现的一种原因就是因为在请求执行完之后浏览器会再发一次请求.ico的请求,具体内容可以参考: