http://liuluo129.iteye.com/blog/1965268
图来自上面连接
http://blog.****.net/geloin/article/details/7441330
整理于上面
Spring的web包中中有很多过滤器,这些过滤器位于org.springframework.web.filter并且理所当然地实现了javax.servlet.Filter,不过实现的方式有以下几类:
(1) 直接实现Filter,这一类过滤器只有CompositeFilter;
(2) 继承抽象类GenericFilterBean,该类实现了javax.servlet.Filter,这一类的过滤器只有一个,即DelegatingFilterProxy;
(3) 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;
(4) 继承抽象类AbstractRequestLoggingFilter,该类为OncePerRequestFilter的直接子类,这一类过滤器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。
本文要讲述的,即是GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter。
GenericFilterBean
抽象类GenericFilterBean实现了javax.servlet.Filter、org.springframework.beans.factory.BeanNameAware、org.springframework.context.EnvironmentAware、org.springframework.web.context.ServletContextAware、org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean五个接口,作用如下:
(1) Filter,实现过滤器;
(2) BeanNameAware,实现该接口的setBeanName方法,便于Bean管理器生成Bean;
(3) EnvironmentAware,实现该接口的setEnvironment方法,指明该Bean运行的环境;
(4) ServletContextAware,实现该接口的setServletContextAware方法,指明上下文;
(5) InitializingBean,实现该接口的afterPropertiesSet方法,指明设置属性生的操作;
(6) DisposableBean,实现该接口的destroy方法,用于回收资源。
GenericFilterBean的工作流程是:init-doFilter-destory,其中的init和destory在该类中实现,doFilter在具体实现类中实现。init的代码如下:
- /**
- * Standard way of initializing this filter.
- * Map config parameters onto bean properties of this filter, and
- * invoke subclass initialization.
- * @param filterConfig the configuration for this filter
- * @throws ServletException if bean properties are invalid (or required
- * properties are missing), or if subclass initialization fails.
- * @see #initFilterBean
- */
- public final void init(FilterConfig filterConfig) throws ServletException {
- Assert.notNull(filterConfig, "FilterConfig must not be null");
- if (logger.isDebugEnabled()) {
- logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
- }
- this.filterConfig = filterConfig;
- // Set bean properties from init parameters.
- try {
- PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- initBeanWrapper(bw);
- bw.setPropertyValues(pvs, true);
- }
- catch (BeansException ex) {
- String msg = "Failed to set bean properties on filter '" +
- filterConfig.getFilterName() + "': " + ex.getMessage();
- logger.error(msg, ex);
- throw new NestedServletException(msg, ex);
- }
- // Let subclasses do whatever initialization they like.
- initFilterBean();
- if (logger.isDebugEnabled()) {
- logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
- }
- }
该方法来自于javax.servlet.Filter,即过滤器的初始化,它的主要工作集中于以下几行代码:
- // 从properties文件中获取值,这里是web.xml
- PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
- // 设置bean适配器
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- // 设置上下文,这里的servletContext的设定继承自ServletContextAware的setter
- ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
- // 将上下文信息和环境信息设置到bean适配器中,这里的environment来自于EnvironmentAware的setter
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- // 初始化bean适配器
- initBeanWrapper(bw);
- // 将从properties中获取的资源放置到bean适配器
- bw.setPropertyValues(pvs, true);
- // 初始化bean
- initFilterBean();
其中initFilterBean方法在两个位置起作用,一处是上文所述的init方法,另一处是afterPropertiesSet方法,在调用该方法前,需要保证用于Filter的所有的bean都已被设置,该方法由子类实现。
GenericFilterBean中包含一个内部私有类FilterConfigPropertyValues,主要用于将web.xml中定义的init-param的值取出。
OncePerRequestFilter
抽象类oncePerRequestFilter继承自GenericFilterBean,它保留了GenericFilterBean中的所有方法并对之进行了扩展,在oncePerRequestFilter中的主要方法是doFilter,代码如下:
- /**
- * This <code>doFilter</code> implementation stores a request attribute for
- * "already filtered", proceeding without filtering again if the
- * attribute is already there.
- * @see #getAlreadyFilteredAttributeName
- * @see #shouldNotFilter
- * @see #doFilterInternal
- */
- public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
- throw new ServletException("OncePerRequestFilter just supports HTTP requests");
- }
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
- // 调用GenericFilterBean的getFilterName方法返回已过滤的属性名
- String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
- if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {
- // 未调用该过滤器或已过滤
- filterChain.doFilter(request, response);
- }
- else {
- // 进行过滤
- request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
- try {
- doFilterInternal(httpRequest, httpResponse, filterChain);
- }
- finally {
- // Remove the "already filtered" request attribute for this request.
- request.removeAttribute(alreadyFilteredAttributeName);
- }
- }
- }
在doFilter方法中,doFilterInternal方法由子类实现,主要作用是规定过滤的具体方法。
AbstractRequestLoggingFilter
AbstractRequestLoggingFilter继承了OncePerRequestFilter并实现了其doFilterInternal方法,该方法代码如下:
- /**
- * Forwards the request to the next filter in the chain and delegates down to the subclasses to perform the actual
- * request logging both before and after the request is processed.
- *
- * @see #beforeRequest
- * @see #afterRequest
- */
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- if (isIncludePayload()) {
- // 若日志中包含负载,则重置request
- request = new RequestCachingRequestWrapper(request);
- }
- // 过滤前执行的方法
- beforeRequest(request, getBeforeMessage(request));
- try {
- // 执行过滤
- filterChain.doFilter(request, response);
- }
- finally {
- // 过滤后执行的方法
- afterRequest(request, getAfterMessage(request));
- }
- }
doFilter方法中的beforeRequest和afterRequest方法由子类实现,RequestCachingRequestWrapper为AbstractRequestLoggingFilter的内部内,主要作用是重置request。
区别
我们在使用过滤器时,通常没必要知道GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter,但不防碍我们了解这几个类,就上文所述,
AbstractRequestLoggingFilter继承自OncePerRequestFilter,
OncePerRequestFilter继承自GenericFilterBean,
所以我们知道,genericFilterBean是任何类型的过滤器的一个比较方便的超类,
这个类主要实现的就是从web.xml文件中取得init-param中设定的值,然后对Filter进行初始化(当然,其子类可以覆盖init方法)。
OncePerRequestFilter继承自GenericFilterBean,那么它自然知道怎么去获取配置文件中的属性及其值,所以其重点不在于取值,而在于确保在接收到一个request后,每个filter只执行一次,它的子类只需要关注Filter的具体实现即doFilterInternal。
AbstractRequestLoggingFilter是对OncePerRequestFilter的扩展,它除了遗传了其父类及祖先类的所有功能外,还在doFilterInternal中决定了在过滤之前和之后执行的事件,它的子类关注的是beforeRequest和afterRequest。
总体来说,这三个类分别执行了Filter的某部分功能,当然,具体如何执行由它们的子类规定,若你需要实现自己的过滤器,也可以根据上文所述继承你所需要的类
==============================================================================================================
Spring MVC过滤器-上下文信息过滤器(AbstractRequestLoggingFilter及其子类)
http://blog.****.net/geloin/article/details/7443329
AbstractRequestLoggingFilter类定义了两个方法beforeRequest和afterRequest分别用于设定过滤前后执行的操作,
它有三个子类,
分别是CommonsRequestLoggingFilter、
ServletContextRequestLoggingFilter
和Log4jNestedDiagnosticContextFilter,
这三个子类分别实现了各自的beforeRequest和afterRequest。其中,
CommonsRequestLoggingFilter在过滤前后分别打印出一段debug的信息;
ServletContextRequestLoggingFilter在过滤前后分别向日志文件中写入一段日志信息,日志文件可由log4j.properties等指定;
Log4jNestedDiagnosticContextFilter则将日志信息存储到NDC中,NDC采用了一个类似栈的机制来push和pot上下文信息,每一个线程都独立地储存上下文信息,比如说一个servlet就可以针对 每一个request创建对应的NDC,储存客户端地址等信息。
当然,在AbstractRequestLoggingFilter中,还包含很多其他方法:
setIncludeQueryString:查询语句是否包含在日志文件中,true或false;
setIncludeClientInfo:客户地址和session id是否包含在日志中,true或false;
setIncludePayload:负载信息是否包含在日志信息中,true或false;
setMaxPayloadLength:最大负载量,int值;
setBeforeMessagePrefix:日志信息前的信息的前缀;
setBeforeMessageSuffix:日志信息前的信息的后缀;
setAfterMessagePrefix:日志信息后的信息的前缀;
setAfterMessageSuffix:日志信息后的信息的后缀。
以上这些信息均可以在init-param中进行设置。
日志信息在getBeforeMessage和getAfterMessage方法中通过createMessage创建,然后被doFilterInternal调用,
日志信息中至少包含uri信息,其他信息则根据init-param中所设定的值获取,
例如,当includeQueryString的值为true时,日志信息中除uri信息外,还包含query string信息。
由此可以,在spring中已定义的AbstractRequestLoggingFiter的子类过滤器均是用于输入上下文信息,只不过承载的方式不同,
其中CommonsRequestLoggingFilter将上下文信息直接打印;
ServletContextRequestLoggingFilter将上下文信息写入日志文件;
Log4jNestedDiagnosticContextFilter将上下文信息写入NDC中
==============================================================================================================
Spring MVC过滤器-HiddenHttpMethodFilter
http://blog.****.net/geloin/article/details/7444321
因为很少有人用到GET和POST以外的method, 而且只有部分浏览器不支持部分method put,deletede等
但是为了做到程序兼容性更好,spring3.0添加了一个过滤器HiddenHttpMethodFilter,可以将这些请求转换为标准的http方法,使得支持PUT与DELETE,就像可以使用GET、POST、一样,
HiddenHttpMethodFilter的父类是OncePerRequestFilter,它继承了父类的doFilterInternal方法,工作原理是
将jsp页面的form表单的method属性值在doFilterInternal方法中转化为标准的Http方法,即GET,、POST、 HEAD、OPTIONS、PUT、DELETE、
TRACE,然后到Controller中找到对应的方法。
例如,在使用注解时我们可能会在Controller中用于@RequestMapping(value = "list", method = RequestMethod.PUT),
所以如果你的表单中使用的是<form ...,那么这个表单会被提交到标了Controller的Method="PUT"的方法中
需要注意的是,由于doFilterInternal方法只对method为post的表单进行过滤,所以在页面中必须如下设置:
- <form action="..." method="<strong>post</strong>">
- <input type="hidden" name="_method" value="<strong>put</strong>" />
- ......
- </form>
而不是使用:
- <form action="..." method="put">
- ......
- </form>
- <filter>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <servlet-name>spring</servlet-name>
- </filter-mapping>
- <servlet>
- <servlet-name>spring</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:dispatcher.xml</param-value>
- </init-param>
- lt;/servlet>
- <servlet-mapping>
- <servlet-name>spring</servlet-name>
- <url-pattern>*.html</url-pattern>
- lt;/servlet-mapping>
同样的,作为Filter,可以在web.xml中配置HiddenHttpMethodFilter的参数,可配置的参数为methodParam,
如果不在web.xml里重新配置的话,default的值为 “_method”,而他在hidden里对应的value为http method的任意一个即可
- <input type="hidden" name="_method" value="<strong>put</strong>" />
如果不想用default的“_method”,可以在web.xml里重新配置,比如abcd_abc,提供这个修改可能是为了防止你的form表单里如果已经有“_method”的定义了,不能用于HiddenHttpMethodFilter实现的目的,通过它可以重写
- <input type="hidden" name="abcd_abc" value="<strong>put</strong>" />
==============================================================================================================
Spring MVC过滤器-HttpPutFormContentFilter
http://blog.****.net/geloin/article/details/7444590
在Spring MVC过滤器-HiddenHttpMethodFilter中我们提到,jsp或者说html中的form的method值只能为post或get,我们可以通过HiddenHttpMethodFilter获取put表单中的参数-值
而在Spring3.0中获取put表单的参数-值还有另一种方法,即使用HttpPutFormContentFilter过滤器
HttpPutFormContentFilter过滤器的作为就是获取put表单的值,并将之传递到Controller中标注了method为RequestMethod.put的方法中。
在web.xml中配置HttpPutFormContentFilter的代码类似如下:
- <filter>
- <filter-name>httpPutFormcontentFilter</filter-name>
- <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>httpPutFormContentFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
需要注意的是,该过滤器只能接受enctype值为application/x-www-form-urlencoded的表单,也就是说,在使用该过滤器时,form表单的代码必须如下
- <form action="" method="put" enctype="<strong>application/x-www-form-urlencoded</strong>">
- ......
- </form>
==============================================================================================================
ShallowEtagHeaderFilter
ShallowEtagHeaderFilter是spring提供的支持ETag的一个过滤器,所谓ETag是指被请求变量的实体值,是一个可以与Web资源关联的记号,而Web资源可以是一个Web页,也可以是JSON或XML文档,服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:
ETag:"50b1c1d4f775c61:df3"
客户端的查询更新格式是这样的:
If-None-Match : W / "50b1c1d4f775c61:df3"
如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。
ShallowEtagHeaderFilter会将JSP等的内容缓存,生成MD5的key,然后在response中作为Etage的header返回给客户端。下次客户端对相同的资源(或者说相同的url)发出请求时,客户端会将之前生成的key作为If-None-Match的值发送到server端。 Filter会客户端传来的值和服务器上的做比较,如果相同,则返回304;否则,将发送新的内容到客户端。
查看ShallowEtagHeaderFilter的源码如下:
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{
- ShallowEtagResponseWrapper responseWrapper = new ShallowEtagResponseWrapper(response, null);
- filterChain.doFilter(request, responseWrapper);
- // 由此可知,服务器仍会处理请求
- byte[] body = responseWrapper.toByteArray();
- int statusCode = responseWrapper.getStatusCode();
- if (isEligibleForEtag(request, responseWrapper, statusCode, body)) {
- String responseETag = generateETagHeaderValue(body);
- response.setHeader(HEADER_ETAG, responseETag);
- String requestETag = request.getHeader(HEADER_IF_NONE_MATCH);
- if (responseETag.equals(requestETag)) {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("ETag [" + responseETag + "] equal to If-None-Match, sending 304");
- }
- response.setStatus(304);
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag + "], sending normal response");
- }
- copyBodyToResponse(body, response);
- }
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("Response with status code [" + statusCode + "] not eligible for ETag");
- }
- copyBodyToResponse(body, response);
- }
- }
由源码可知,ShallowEtagHeaderFilter只能根据结果判断是否重新向客户端发送数据,并不会不处理请求,因此节省带宽,而不能提高服务器性能。
配置ShallowEtagHeaderFilter的代码如下:
- <filter>
- <filter-name>shallowEtagHeaderFilter</filter-name>
- <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</fliter-class>
- </filter>
- <filter-mapping>
- <filter-name>shallowEtagHeaderFilter</filter-name>
- <servlet-name>spring</servlet-name>
- </filter-mapping>
RequestContextFilter
这是在Spring2.0时添加的类,通过LocaleContextHolder和RequestContextHolder把Http request对象基于LocalThread绑定到请求提供服务的线程上。现在一般使用DispatcherServlet这个*分发器。现在RequestContextFilter过滤器主要用于第三方的Servlet,如JSF的FacesServlet。在Spring2.5之前都是使用该过滤器配置。配置如下:
- <filter>
- <filter-name>RequestContextFilter</filter-name>
- <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>RequestContextFilter</filter-name>
- <servlet-name>Faces Servlet</servlet-name>
- </filter-mapping>
http://liuluo129.iteye.com/blog/1965268
图来自上面连接
http://blog.****.net/geloin/article/details/7441330
整理于上面
Spring的web包中中有很多过滤器,这些过滤器位于org.springframework.web.filter并且理所当然地实现了javax.servlet.Filter,不过实现的方式有以下几类:
(1) 直接实现Filter,这一类过滤器只有CompositeFilter;
(2) 继承抽象类GenericFilterBean,该类实现了javax.servlet.Filter,这一类的过滤器只有一个,即DelegatingFilterProxy;
(3) 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;
(4) 继承抽象类AbstractRequestLoggingFilter,该类为OncePerRequestFilter的直接子类,这一类过滤器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。
本文要讲述的,即是GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter。
GenericFilterBean
抽象类GenericFilterBean实现了javax.servlet.Filter、org.springframework.beans.factory.BeanNameAware、org.springframework.context.EnvironmentAware、org.springframework.web.context.ServletContextAware、org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean五个接口,作用如下:
(1) Filter,实现过滤器;
(2) BeanNameAware,实现该接口的setBeanName方法,便于Bean管理器生成Bean;
(3) EnvironmentAware,实现该接口的setEnvironment方法,指明该Bean运行的环境;
(4) ServletContextAware,实现该接口的setServletContextAware方法,指明上下文;
(5) InitializingBean,实现该接口的afterPropertiesSet方法,指明设置属性生的操作;
(6) DisposableBean,实现该接口的destroy方法,用于回收资源。
GenericFilterBean的工作流程是:init-doFilter-destory,其中的init和destory在该类中实现,doFilter在具体实现类中实现。init的代码如下:
- /**
- * Standard way of initializing this filter.
- * Map config parameters onto bean properties of this filter, and
- * invoke subclass initialization.
- * @param filterConfig the configuration for this filter
- * @throws ServletException if bean properties are invalid (or required
- * properties are missing), or if subclass initialization fails.
- * @see #initFilterBean
- */
- public final void init(FilterConfig filterConfig) throws ServletException {
- Assert.notNull(filterConfig, "FilterConfig must not be null");
- if (logger.isDebugEnabled()) {
- logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
- }
- this.filterConfig = filterConfig;
- // Set bean properties from init parameters.
- try {
- PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- initBeanWrapper(bw);
- bw.setPropertyValues(pvs, true);
- }
- catch (BeansException ex) {
- String msg = "Failed to set bean properties on filter '" +
- filterConfig.getFilterName() + "': " + ex.getMessage();
- logger.error(msg, ex);
- throw new NestedServletException(msg, ex);
- }
- // Let subclasses do whatever initialization they like.
- initFilterBean();
- if (logger.isDebugEnabled()) {
- logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
- }
- }
该方法来自于javax.servlet.Filter,即过滤器的初始化,它的主要工作集中于以下几行代码:
- // 从properties文件中获取值,这里是web.xml
- PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
- // 设置bean适配器
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- // 设置上下文,这里的servletContext的设定继承自ServletContextAware的setter
- ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
- // 将上下文信息和环境信息设置到bean适配器中,这里的environment来自于EnvironmentAware的setter
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- // 初始化bean适配器
- initBeanWrapper(bw);
- // 将从properties中获取的资源放置到bean适配器
- bw.setPropertyValues(pvs, true);
- // 初始化bean
- initFilterBean();
其中initFilterBean方法在两个位置起作用,一处是上文所述的init方法,另一处是afterPropertiesSet方法,在调用该方法前,需要保证用于Filter的所有的bean都已被设置,该方法由子类实现。
GenericFilterBean中包含一个内部私有类FilterConfigPropertyValues,主要用于将web.xml中定义的init-param的值取出。
OncePerRequestFilter
抽象类oncePerRequestFilter继承自GenericFilterBean,它保留了GenericFilterBean中的所有方法并对之进行了扩展,在oncePerRequestFilter中的主要方法是doFilter,代码如下:
- /**
- * This <code>doFilter</code> implementation stores a request attribute for
- * "already filtered", proceeding without filtering again if the
- * attribute is already there.
- * @see #getAlreadyFilteredAttributeName
- * @see #shouldNotFilter
- * @see #doFilterInternal
- */
- public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
- throw new ServletException("OncePerRequestFilter just supports HTTP requests");
- }
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
- // 调用GenericFilterBean的getFilterName方法返回已过滤的属性名
- String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
- if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {
- // 未调用该过滤器或已过滤
- filterChain.doFilter(request, response);
- }
- else {
- // 进行过滤
- request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
- try {
- doFilterInternal(httpRequest, httpResponse, filterChain);
- }
- finally {
- // Remove the "already filtered" request attribute for this request.
- request.removeAttribute(alreadyFilteredAttributeName);
- }
- }
- }
在doFilter方法中,doFilterInternal方法由子类实现,主要作用是规定过滤的具体方法。
AbstractRequestLoggingFilter
AbstractRequestLoggingFilter继承了OncePerRequestFilter并实现了其doFilterInternal方法,该方法代码如下:
- /**
- * Forwards the request to the next filter in the chain and delegates down to the subclasses to perform the actual
- * request logging both before and after the request is processed.
- *
- * @see #beforeRequest
- * @see #afterRequest
- */
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- if (isIncludePayload()) {
- // 若日志中包含负载,则重置request
- request = new RequestCachingRequestWrapper(request);
- }
- // 过滤前执行的方法
- beforeRequest(request, getBeforeMessage(request));
- try {
- // 执行过滤
- filterChain.doFilter(request, response);
- }
- finally {
- // 过滤后执行的方法
- afterRequest(request, getAfterMessage(request));
- }
- }
doFilter方法中的beforeRequest和afterRequest方法由子类实现,RequestCachingRequestWrapper为AbstractRequestLoggingFilter的内部内,主要作用是重置request。
区别
我们在使用过滤器时,通常没必要知道GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter,但不防碍我们了解这几个类,就上文所述,
AbstractRequestLoggingFilter继承自OncePerRequestFilter,
OncePerRequestFilter继承自GenericFilterBean,
所以我们知道,genericFilterBean是任何类型的过滤器的一个比较方便的超类,
这个类主要实现的就是从web.xml文件中取得init-param中设定的值,然后对Filter进行初始化(当然,其子类可以覆盖init方法)。
OncePerRequestFilter继承自GenericFilterBean,那么它自然知道怎么去获取配置文件中的属性及其值,所以其重点不在于取值,而在于确保在接收到一个request后,每个filter只执行一次,它的子类只需要关注Filter的具体实现即doFilterInternal。
AbstractRequestLoggingFilter是对OncePerRequestFilter的扩展,它除了遗传了其父类及祖先类的所有功能外,还在doFilterInternal中决定了在过滤之前和之后执行的事件,它的子类关注的是beforeRequest和afterRequest。
总体来说,这三个类分别执行了Filter的某部分功能,当然,具体如何执行由它们的子类规定,若你需要实现自己的过滤器,也可以根据上文所述继承你所需要的类
==============================================================================================================
Spring MVC过滤器-上下文信息过滤器(AbstractRequestLoggingFilter及其子类)
http://blog.****.net/geloin/article/details/7443329
AbstractRequestLoggingFilter类定义了两个方法beforeRequest和afterRequest分别用于设定过滤前后执行的操作,
它有三个子类,
分别是CommonsRequestLoggingFilter、
ServletContextRequestLoggingFilter
和Log4jNestedDiagnosticContextFilter,
这三个子类分别实现了各自的beforeRequest和afterRequest。其中,
CommonsRequestLoggingFilter在过滤前后分别打印出一段debug的信息;
ServletContextRequestLoggingFilter在过滤前后分别向日志文件中写入一段日志信息,日志文件可由log4j.properties等指定;
Log4jNestedDiagnosticContextFilter则将日志信息存储到NDC中,NDC采用了一个类似栈的机制来push和pot上下文信息,每一个线程都独立地储存上下文信息,比如说一个servlet就可以针对 每一个request创建对应的NDC,储存客户端地址等信息。
当然,在AbstractRequestLoggingFilter中,还包含很多其他方法:
setIncludeQueryString:查询语句是否包含在日志文件中,true或false;
setIncludeClientInfo:客户地址和session id是否包含在日志中,true或false;
setIncludePayload:负载信息是否包含在日志信息中,true或false;
setMaxPayloadLength:最大负载量,int值;
setBeforeMessagePrefix:日志信息前的信息的前缀;
setBeforeMessageSuffix:日志信息前的信息的后缀;
setAfterMessagePrefix:日志信息后的信息的前缀;
setAfterMessageSuffix:日志信息后的信息的后缀。
以上这些信息均可以在init-param中进行设置。
日志信息在getBeforeMessage和getAfterMessage方法中通过createMessage创建,然后被doFilterInternal调用,
日志信息中至少包含uri信息,其他信息则根据init-param中所设定的值获取,
例如,当includeQueryString的值为true时,日志信息中除uri信息外,还包含query string信息。
由此可以,在spring中已定义的AbstractRequestLoggingFiter的子类过滤器均是用于输入上下文信息,只不过承载的方式不同,
其中CommonsRequestLoggingFilter将上下文信息直接打印;
ServletContextRequestLoggingFilter将上下文信息写入日志文件;
Log4jNestedDiagnosticContextFilter将上下文信息写入NDC中
==============================================================================================================
Spring MVC过滤器-HiddenHttpMethodFilter
http://blog.****.net/geloin/article/details/7444321
因为很少有人用到GET和POST以外的method, 而且只有部分浏览器不支持部分method put,deletede等
但是为了做到程序兼容性更好,spring3.0添加了一个过滤器HiddenHttpMethodFilter,可以将这些请求转换为标准的http方法,使得支持PUT与DELETE,就像可以使用GET、POST、一样,
HiddenHttpMethodFilter的父类是OncePerRequestFilter,它继承了父类的doFilterInternal方法,工作原理是
将jsp页面的form表单的method属性值在doFilterInternal方法中转化为标准的Http方法,即GET,、POST、 HEAD、OPTIONS、PUT、DELETE、
TRACE,然后到Controller中找到对应的方法。
例如,在使用注解时我们可能会在Controller中用于@RequestMapping(value = "list", method = RequestMethod.PUT),
所以如果你的表单中使用的是<form ...,那么这个表单会被提交到标了Controller的Method="PUT"的方法中
需要注意的是,由于doFilterInternal方法只对method为post的表单进行过滤,所以在页面中必须如下设置:
- <form action="..." method="<strong>post</strong>">
- <input type="hidden" name="_method" value="<strong>put</strong>" />
- ......
- </form>
而不是使用:
- <form action="..." method="put">
- ......
- </form>
- <filter>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <servlet-name>spring</servlet-name>
- </filter-mapping>
- <servlet>
- <servlet-name>spring</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:dispatcher.xml</param-value>
- </init-param>
- lt;/servlet>
- <servlet-mapping>
- <servlet-name>spring</servlet-name>
- <url-pattern>*.html</url-pattern>
- lt;/servlet-mapping>
同样的,作为Filter,可以在web.xml中配置HiddenHttpMethodFilter的参数,可配置的参数为methodParam,
如果不在web.xml里重新配置的话,default的值为 “_method”,而他在hidden里对应的value为http method的任意一个即可
- <input type="hidden" name="_method" value="<strong>put</strong>" />
如果不想用default的“_method”,可以在web.xml里重新配置,比如abcd_abc,提供这个修改可能是为了防止你的form表单里如果已经有“_method”的定义了,不能用于HiddenHttpMethodFilter实现的目的,通过它可以重写
- <input type="hidden" name="abcd_abc" value="<strong>put</strong>" />
==============================================================================================================
Spring MVC过滤器-HttpPutFormContentFilter
http://blog.****.net/geloin/article/details/7444590
在Spring MVC过滤器-HiddenHttpMethodFilter中我们提到,jsp或者说html中的form的method值只能为post或get,我们可以通过HiddenHttpMethodFilter获取put表单中的参数-值
而在Spring3.0中获取put表单的参数-值还有另一种方法,即使用HttpPutFormContentFilter过滤器
HttpPutFormContentFilter过滤器的作为就是获取put表单的值,并将之传递到Controller中标注了method为RequestMethod.put的方法中。
在web.xml中配置HttpPutFormContentFilter的代码类似如下:
- <filter>
- <filter-name>httpPutFormcontentFilter</filter-name>
- <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>httpPutFormContentFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
需要注意的是,该过滤器只能接受enctype值为application/x-www-form-urlencoded的表单,也就是说,在使用该过滤器时,form表单的代码必须如下
- <form action="" method="put" enctype="<strong>application/x-www-form-urlencoded</strong>">
- ......
- </form>
==============================================================================================================
ShallowEtagHeaderFilter
ShallowEtagHeaderFilter是spring提供的支持ETag的一个过滤器,所谓ETag是指被请求变量的实体值,是一个可以与Web资源关联的记号,而Web资源可以是一个Web页,也可以是JSON或XML文档,服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:
ETag:"50b1c1d4f775c61:df3"
客户端的查询更新格式是这样的:
If-None-Match : W / "50b1c1d4f775c61:df3"
如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。
ShallowEtagHeaderFilter会将JSP等的内容缓存,生成MD5的key,然后在response中作为Etage的header返回给客户端。下次客户端对相同的资源(或者说相同的url)发出请求时,客户端会将之前生成的key作为If-None-Match的值发送到server端。 Filter会客户端传来的值和服务器上的做比较,如果相同,则返回304;否则,将发送新的内容到客户端。
查看ShallowEtagHeaderFilter的源码如下:
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{
- ShallowEtagResponseWrapper responseWrapper = new ShallowEtagResponseWrapper(response, null);
- filterChain.doFilter(request, responseWrapper);
- // 由此可知,服务器仍会处理请求
- byte[] body = responseWrapper.toByteArray();
- int statusCode = responseWrapper.getStatusCode();
- if (isEligibleForEtag(request, responseWrapper, statusCode, body)) {
- String responseETag = generateETagHeaderValue(body);
- response.setHeader(HEADER_ETAG, responseETag);
- String requestETag = request.getHeader(HEADER_IF_NONE_MATCH);
- if (responseETag.equals(requestETag)) {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("ETag [" + responseETag + "] equal to If-None-Match, sending 304");
- }
- response.setStatus(304);
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag + "], sending normal response");
- }
- copyBodyToResponse(body, response);
- }
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("Response with status code [" + statusCode + "] not eligible for ETag");
- }
- copyBodyToResponse(body, response);
- }
- }
由源码可知,ShallowEtagHeaderFilter只能根据结果判断是否重新向客户端发送数据,并不会不处理请求,因此节省带宽,而不能提高服务器性能。
配置ShallowEtagHeaderFilter的代码如下:
- <filter>
- <filter-name>shallowEtagHeaderFilter</filter-name>
- <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</fliter-class>
- </filter>
- <filter-mapping>
- <filter-name>shallowEtagHeaderFilter</filter-name>
- <servlet-name>spring</servlet-name>
- </filter-mapping>
RequestContextFilter
这是在Spring2.0时添加的类,通过LocaleContextHolder和RequestContextHolder把Http request对象基于LocalThread绑定到请求提供服务的线程上。现在一般使用DispatcherServlet这个*分发器。现在RequestContextFilter过滤器主要用于第三方的Servlet,如JSF的FacesServlet。在Spring2.5之前都是使用该过滤器配置。配置如下:
- <filter>
- <filter-name>RequestContextFilter</filter-name>
- <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>RequestContextFilter</filter-name>
- <servlet-name>Faces Servlet</servlet-name>
- </filter-mapping>
http://liuluo129.iteye.com/blog/1965268
图来自上面连接
http://blog.****.net/geloin/article/details/7441330
整理于上面
Spring的web包中中有很多过滤器,这些过滤器位于org.springframework.web.filter并且理所当然地实现了javax.servlet.Filter,不过实现的方式有以下几类:
(1) 直接实现Filter,这一类过滤器只有CompositeFilter;
(2) 继承抽象类GenericFilterBean,该类实现了javax.servlet.Filter,这一类的过滤器只有一个,即DelegatingFilterProxy;
(3) 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;
(4) 继承抽象类AbstractRequestLoggingFilter,该类为OncePerRequestFilter的直接子类,这一类过滤器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。
本文要讲述的,即是GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter。
GenericFilterBean
抽象类GenericFilterBean实现了javax.servlet.Filter、org.springframework.beans.factory.BeanNameAware、org.springframework.context.EnvironmentAware、org.springframework.web.context.ServletContextAware、org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean五个接口,作用如下:
(1) Filter,实现过滤器;
(2) BeanNameAware,实现该接口的setBeanName方法,便于Bean管理器生成Bean;
(3) EnvironmentAware,实现该接口的setEnvironment方法,指明该Bean运行的环境;
(4) ServletContextAware,实现该接口的setServletContextAware方法,指明上下文;
(5) InitializingBean,实现该接口的afterPropertiesSet方法,指明设置属性生的操作;
(6) DisposableBean,实现该接口的destroy方法,用于回收资源。
GenericFilterBean的工作流程是:init-doFilter-destory,其中的init和destory在该类中实现,doFilter在具体实现类中实现。init的代码如下:
- /**
- * Standard way of initializing this filter.
- * Map config parameters onto bean properties of this filter, and
- * invoke subclass initialization.
- * @param filterConfig the configuration for this filter
- * @throws ServletException if bean properties are invalid (or required
- * properties are missing), or if subclass initialization fails.
- * @see #initFilterBean
- */
- public final void init(FilterConfig filterConfig) throws ServletException {
- Assert.notNull(filterConfig, "FilterConfig must not be null");
- if (logger.isDebugEnabled()) {
- logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
- }
- this.filterConfig = filterConfig;
- // Set bean properties from init parameters.
- try {
- PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- initBeanWrapper(bw);
- bw.setPropertyValues(pvs, true);
- }
- catch (BeansException ex) {
- String msg = "Failed to set bean properties on filter '" +
- filterConfig.getFilterName() + "': " + ex.getMessage();
- logger.error(msg, ex);
- throw new NestedServletException(msg, ex);
- }
- // Let subclasses do whatever initialization they like.
- initFilterBean();
- if (logger.isDebugEnabled()) {
- logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
- }
- }
该方法来自于javax.servlet.Filter,即过滤器的初始化,它的主要工作集中于以下几行代码:
- // 从properties文件中获取值,这里是web.xml
- PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
- // 设置bean适配器
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- // 设置上下文,这里的servletContext的设定继承自ServletContextAware的setter
- ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
- // 将上下文信息和环境信息设置到bean适配器中,这里的environment来自于EnvironmentAware的setter
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- // 初始化bean适配器
- initBeanWrapper(bw);
- // 将从properties中获取的资源放置到bean适配器
- bw.setPropertyValues(pvs, true);
- // 初始化bean
- initFilterBean();
其中initFilterBean方法在两个位置起作用,一处是上文所述的init方法,另一处是afterPropertiesSet方法,在调用该方法前,需要保证用于Filter的所有的bean都已被设置,该方法由子类实现。
GenericFilterBean中包含一个内部私有类FilterConfigPropertyValues,主要用于将web.xml中定义的init-param的值取出。
OncePerRequestFilter
抽象类oncePerRequestFilter继承自GenericFilterBean,它保留了GenericFilterBean中的所有方法并对之进行了扩展,在oncePerRequestFilter中的主要方法是doFilter,代码如下:
- /**
- * This <code>doFilter</code> implementation stores a request attribute for
- * "already filtered", proceeding without filtering again if the
- * attribute is already there.
- * @see #getAlreadyFilteredAttributeName
- * @see #shouldNotFilter
- * @see #doFilterInternal
- */
- public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
- throw new ServletException("OncePerRequestFilter just supports HTTP requests");
- }
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
- // 调用GenericFilterBean的getFilterName方法返回已过滤的属性名
- String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
- if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {
- // 未调用该过滤器或已过滤
- filterChain.doFilter(request, response);
- }
- else {
- // 进行过滤
- request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
- try {
- doFilterInternal(httpRequest, httpResponse, filterChain);
- }
- finally {
- // Remove the "already filtered" request attribute for this request.
- request.removeAttribute(alreadyFilteredAttributeName);
- }
- }
- }
在doFilter方法中,doFilterInternal方法由子类实现,主要作用是规定过滤的具体方法。
AbstractRequestLoggingFilter
AbstractRequestLoggingFilter继承了OncePerRequestFilter并实现了其doFilterInternal方法,该方法代码如下:
- /**
- * Forwards the request to the next filter in the chain and delegates down to the subclasses to perform the actual
- * request logging both before and after the request is processed.
- *
- * @see #beforeRequest
- * @see #afterRequest
- */
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- if (isIncludePayload()) {
- // 若日志中包含负载,则重置request
- request = new RequestCachingRequestWrapper(request);
- }
- // 过滤前执行的方法
- beforeRequest(request, getBeforeMessage(request));
- try {
- // 执行过滤
- filterChain.doFilter(request, response);
- }
- finally {
- // 过滤后执行的方法
- afterRequest(request, getAfterMessage(request));
- }
- }
doFilter方法中的beforeRequest和afterRequest方法由子类实现,RequestCachingRequestWrapper为AbstractRequestLoggingFilter的内部内,主要作用是重置request。
区别
我们在使用过滤器时,通常没必要知道GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter,但不防碍我们了解这几个类,就上文所述,
AbstractRequestLoggingFilter继承自OncePerRequestFilter,
OncePerRequestFilter继承自GenericFilterBean,
所以我们知道,genericFilterBean是任何类型的过滤器的一个比较方便的超类,
这个类主要实现的就是从web.xml文件中取得init-param中设定的值,然后对Filter进行初始化(当然,其子类可以覆盖init方法)。
OncePerRequestFilter继承自GenericFilterBean,那么它自然知道怎么去获取配置文件中的属性及其值,所以其重点不在于取值,而在于确保在接收到一个request后,每个filter只执行一次,它的子类只需要关注Filter的具体实现即doFilterInternal。
AbstractRequestLoggingFilter是对OncePerRequestFilter的扩展,它除了遗传了其父类及祖先类的所有功能外,还在doFilterInternal中决定了在过滤之前和之后执行的事件,它的子类关注的是beforeRequest和afterRequest。
总体来说,这三个类分别执行了Filter的某部分功能,当然,具体如何执行由它们的子类规定,若你需要实现自己的过滤器,也可以根据上文所述继承你所需要的类
==============================================================================================================
Spring MVC过滤器-上下文信息过滤器(AbstractRequestLoggingFilter及其子类)
http://blog.****.net/geloin/article/details/7443329
AbstractRequestLoggingFilter类定义了两个方法beforeRequest和afterRequest分别用于设定过滤前后执行的操作,
它有三个子类,
分别是CommonsRequestLoggingFilter、
ServletContextRequestLoggingFilter
和Log4jNestedDiagnosticContextFilter,
这三个子类分别实现了各自的beforeRequest和afterRequest。其中,
CommonsRequestLoggingFilter在过滤前后分别打印出一段debug的信息;
ServletContextRequestLoggingFilter在过滤前后分别向日志文件中写入一段日志信息,日志文件可由log4j.properties等指定;
Log4jNestedDiagnosticContextFilter则将日志信息存储到NDC中,NDC采用了一个类似栈的机制来push和pot上下文信息,每一个线程都独立地储存上下文信息,比如说一个servlet就可以针对 每一个request创建对应的NDC,储存客户端地址等信息。
当然,在AbstractRequestLoggingFilter中,还包含很多其他方法:
setIncludeQueryString:查询语句是否包含在日志文件中,true或false;
setIncludeClientInfo:客户地址和session id是否包含在日志中,true或false;
setIncludePayload:负载信息是否包含在日志信息中,true或false;
setMaxPayloadLength:最大负载量,int值;
setBeforeMessagePrefix:日志信息前的信息的前缀;
setBeforeMessageSuffix:日志信息前的信息的后缀;
setAfterMessagePrefix:日志信息后的信息的前缀;
setAfterMessageSuffix:日志信息后的信息的后缀。
以上这些信息均可以在init-param中进行设置。
日志信息在getBeforeMessage和getAfterMessage方法中通过createMessage创建,然后被doFilterInternal调用,
日志信息中至少包含uri信息,其他信息则根据init-param中所设定的值获取,
例如,当includeQueryString的值为true时,日志信息中除uri信息外,还包含query string信息。
由此可以,在spring中已定义的AbstractRequestLoggingFiter的子类过滤器均是用于输入上下文信息,只不过承载的方式不同,
其中CommonsRequestLoggingFilter将上下文信息直接打印;
ServletContextRequestLoggingFilter将上下文信息写入日志文件;
Log4jNestedDiagnosticContextFilter将上下文信息写入NDC中
==============================================================================================================
Spring MVC过滤器-HiddenHttpMethodFilter
http://blog.****.net/geloin/article/details/7444321
因为很少有人用到GET和POST以外的method, 而且只有部分浏览器不支持部分method put,deletede等
但是为了做到程序兼容性更好,spring3.0添加了一个过滤器HiddenHttpMethodFilter,可以将这些请求转换为标准的http方法,使得支持PUT与DELETE,就像可以使用GET、POST、一样,
HiddenHttpMethodFilter的父类是OncePerRequestFilter,它继承了父类的doFilterInternal方法,工作原理是
将jsp页面的form表单的method属性值在doFilterInternal方法中转化为标准的Http方法,即GET,、POST、 HEAD、OPTIONS、PUT、DELETE、
TRACE,然后到Controller中找到对应的方法。
例如,在使用注解时我们可能会在Controller中用于@RequestMapping(value = "list", method = RequestMethod.PUT),
所以如果你的表单中使用的是<form ...,那么这个表单会被提交到标了Controller的Method="PUT"的方法中
需要注意的是,由于doFilterInternal方法只对method为post的表单进行过滤,所以在页面中必须如下设置:
- <form action="..." method="<strong>post</strong>">
- <input type="hidden" name="_method" value="<strong>put</strong>" />
- ......
- </form>
而不是使用:
- <form action="..." method="put">
- ......
- </form>
- <filter>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <servlet-name>spring</servlet-name>
- </filter-mapping>
- <servlet>
- <servlet-name>spring</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:dispatcher.xml</param-value>
- </init-param>
- lt;/servlet>
- <servlet-mapping>
- <servlet-name>spring</servlet-name>
- <url-pattern>*.html</url-pattern>
- lt;/servlet-mapping>
同样的,作为Filter,可以在web.xml中配置HiddenHttpMethodFilter的参数,可配置的参数为methodParam,
如果不在web.xml里重新配置的话,default的值为 “_method”,而他在hidden里对应的value为http method的任意一个即可
- <input type="hidden" name="_method" value="<strong>put</strong>" />
如果不想用default的“_method”,可以在web.xml里重新配置,比如abcd_abc,提供这个修改可能是为了防止你的form表单里如果已经有“_method”的定义了,不能用于HiddenHttpMethodFilter实现的目的,通过它可以重写
- <input type="hidden" name="abcd_abc" value="<strong>put</strong>" />
==============================================================================================================
Spring MVC过滤器-HttpPutFormContentFilter
http://blog.****.net/geloin/article/details/7444590
在Spring MVC过滤器-HiddenHttpMethodFilter中我们提到,jsp或者说html中的form的method值只能为post或get,我们可以通过HiddenHttpMethodFilter获取put表单中的参数-值
而在Spring3.0中获取put表单的参数-值还有另一种方法,即使用HttpPutFormContentFilter过滤器
HttpPutFormContentFilter过滤器的作为就是获取put表单的值,并将之传递到Controller中标注了method为RequestMethod.put的方法中。
在web.xml中配置HttpPutFormContentFilter的代码类似如下:
- <filter>
- <filter-name>httpPutFormcontentFilter</filter-name>
- <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>httpPutFormContentFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
需要注意的是,该过滤器只能接受enctype值为application/x-www-form-urlencoded的表单,也就是说,在使用该过滤器时,form表单的代码必须如下
- <form action="" method="put" enctype="<strong>application/x-www-form-urlencoded</strong>">
- ......
- </form>
==============================================================================================================
ShallowEtagHeaderFilter
ShallowEtagHeaderFilter是spring提供的支持ETag的一个过滤器,所谓ETag是指被请求变量的实体值,是一个可以与Web资源关联的记号,而Web资源可以是一个Web页,也可以是JSON或XML文档,服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:
ETag:"50b1c1d4f775c61:df3"
客户端的查询更新格式是这样的:
If-None-Match : W / "50b1c1d4f775c61:df3"
如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。
ShallowEtagHeaderFilter会将JSP等的内容缓存,生成MD5的key,然后在response中作为Etage的header返回给客户端。下次客户端对相同的资源(或者说相同的url)发出请求时,客户端会将之前生成的key作为If-None-Match的值发送到server端。 Filter会客户端传来的值和服务器上的做比较,如果相同,则返回304;否则,将发送新的内容到客户端。
查看ShallowEtagHeaderFilter的源码如下:
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{
- ShallowEtagResponseWrapper responseWrapper = new ShallowEtagResponseWrapper(response, null);
- filterChain.doFilter(request, responseWrapper);
- // 由此可知,服务器仍会处理请求
- byte[] body = responseWrapper.toByteArray();
- int statusCode = responseWrapper.getStatusCode();
- if (isEligibleForEtag(request, responseWrapper, statusCode, body)) {
- String responseETag = generateETagHeaderValue(body);
- response.setHeader(HEADER_ETAG, responseETag);
- String requestETag = request.getHeader(HEADER_IF_NONE_MATCH);
- if (responseETag.equals(requestETag)) {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("ETag [" + responseETag + "] equal to If-None-Match, sending 304");
- }
- response.setStatus(304);
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag + "], sending normal response");
- }
- copyBodyToResponse(body, response);
- }
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("Response with status code [" + statusCode + "] not eligible for ETag");
- }
- copyBodyToResponse(body, response);
- }
- }
由源码可知,ShallowEtagHeaderFilter只能根据结果判断是否重新向客户端发送数据,并不会不处理请求,因此节省带宽,而不能提高服务器性能。
配置ShallowEtagHeaderFilter的代码如下:
- <filter>
- <filter-name>shallowEtagHeaderFilter</filter-name>
- <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</fliter-class>
- </filter>
- <filter-mapping>
- <filter-name>shallowEtagHeaderFilter</filter-name>
- <servlet-name>spring</servlet-name>
- </filter-mapping>
RequestContextFilter
这是在Spring2.0时添加的类,通过LocaleContextHolder和RequestContextHolder把Http request对象基于LocalThread绑定到请求提供服务的线程上。现在一般使用DispatcherServlet这个*分发器。现在RequestContextFilter过滤器主要用于第三方的Servlet,如JSF的FacesServlet。在Spring2.5之前都是使用该过滤器配置。配置如下:
- <filter>
- <filter-name>RequestContextFilter</filter-name>
- <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>RequestContextFilter</filter-name>
- <servlet-name>Faces Servlet</servlet-name>
- </filter-mapping>
http://liuluo129.iteye.com/blog/1965268
图来自上面连接
http://blog.****.net/geloin/article/details/7441330
整理于上面
Spring的web包中中有很多过滤器,这些过滤器位于org.springframework.web.filter并且理所当然地实现了javax.servlet.Filter,不过实现的方式有以下几类:
(1) 直接实现Filter,这一类过滤器只有CompositeFilter;
(2) 继承抽象类GenericFilterBean,该类实现了javax.servlet.Filter,这一类的过滤器只有一个,即DelegatingFilterProxy;
(3) 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;
(4) 继承抽象类AbstractRequestLoggingFilter,该类为OncePerRequestFilter的直接子类,这一类过滤器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。
本文要讲述的,即是GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter。
GenericFilterBean
抽象类GenericFilterBean实现了javax.servlet.Filter、org.springframework.beans.factory.BeanNameAware、org.springframework.context.EnvironmentAware、org.springframework.web.context.ServletContextAware、org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean五个接口,作用如下:
(1) Filter,实现过滤器;
(2) BeanNameAware,实现该接口的setBeanName方法,便于Bean管理器生成Bean;
(3) EnvironmentAware,实现该接口的setEnvironment方法,指明该Bean运行的环境;
(4) ServletContextAware,实现该接口的setServletContextAware方法,指明上下文;
(5) InitializingBean,实现该接口的afterPropertiesSet方法,指明设置属性生的操作;
(6) DisposableBean,实现该接口的destroy方法,用于回收资源。
GenericFilterBean的工作流程是:init-doFilter-destory,其中的init和destory在该类中实现,doFilter在具体实现类中实现。init的代码如下:
- /**
- * Standard way of initializing this filter.
- * Map config parameters onto bean properties of this filter, and
- * invoke subclass initialization.
- * @param filterConfig the configuration for this filter
- * @throws ServletException if bean properties are invalid (or required
- * properties are missing), or if subclass initialization fails.
- * @see #initFilterBean
- */
- public final void init(FilterConfig filterConfig) throws ServletException {
- Assert.notNull(filterConfig, "FilterConfig must not be null");
- if (logger.isDebugEnabled()) {
- logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
- }
- this.filterConfig = filterConfig;
- // Set bean properties from init parameters.
- try {
- PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- initBeanWrapper(bw);
- bw.setPropertyValues(pvs, true);
- }
- catch (BeansException ex) {
- String msg = "Failed to set bean properties on filter '" +
- filterConfig.getFilterName() + "': " + ex.getMessage();
- logger.error(msg, ex);
- throw new NestedServletException(msg, ex);
- }
- // Let subclasses do whatever initialization they like.
- initFilterBean();
- if (logger.isDebugEnabled()) {
- logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
- }
- }
该方法来自于javax.servlet.Filter,即过滤器的初始化,它的主要工作集中于以下几行代码:
- // 从properties文件中获取值,这里是web.xml
- PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
- // 设置bean适配器
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- // 设置上下文,这里的servletContext的设定继承自ServletContextAware的setter
- ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
- // 将上下文信息和环境信息设置到bean适配器中,这里的environment来自于EnvironmentAware的setter
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- // 初始化bean适配器
- initBeanWrapper(bw);
- // 将从properties中获取的资源放置到bean适配器
- bw.setPropertyValues(pvs, true);
- // 初始化bean
- initFilterBean();
其中initFilterBean方法在两个位置起作用,一处是上文所述的init方法,另一处是afterPropertiesSet方法,在调用该方法前,需要保证用于Filter的所有的bean都已被设置,该方法由子类实现。
GenericFilterBean中包含一个内部私有类FilterConfigPropertyValues,主要用于将web.xml中定义的init-param的值取出。
OncePerRequestFilter
抽象类oncePerRequestFilter继承自GenericFilterBean,它保留了GenericFilterBean中的所有方法并对之进行了扩展,在oncePerRequestFilter中的主要方法是doFilter,代码如下:
- /**
- * This <code>doFilter</code> implementation stores a request attribute for
- * "already filtered", proceeding without filtering again if the
- * attribute is already there.
- * @see #getAlreadyFilteredAttributeName
- * @see #shouldNotFilter
- * @see #doFilterInternal
- */
- public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
- throw new ServletException("OncePerRequestFilter just supports HTTP requests");
- }
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
- // 调用GenericFilterBean的getFilterName方法返回已过滤的属性名
- String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
- if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {
- // 未调用该过滤器或已过滤
- filterChain.doFilter(request, response);
- }
- else {
- // 进行过滤
- request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
- try {
- doFilterInternal(httpRequest, httpResponse, filterChain);
- }
- finally {
- // Remove the "already filtered" request attribute for this request.
- request.removeAttribute(alreadyFilteredAttributeName);
- }
- }
- }
在doFilter方法中,doFilterInternal方法由子类实现,主要作用是规定过滤的具体方法。
AbstractRequestLoggingFilter
AbstractRequestLoggingFilter继承了OncePerRequestFilter并实现了其doFilterInternal方法,该方法代码如下:
- /**
- * Forwards the request to the next filter in the chain and delegates down to the subclasses to perform the actual
- * request logging both before and after the request is processed.
- *
- * @see #beforeRequest
- * @see #afterRequest
- */
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- if (isIncludePayload()) {
- // 若日志中包含负载,则重置request
- request = new RequestCachingRequestWrapper(request);
- }
- // 过滤前执行的方法
- beforeRequest(request, getBeforeMessage(request));
- try {
- // 执行过滤
- filterChain.doFilter(request, response);
- }
- finally {
- // 过滤后执行的方法
- afterRequest(request, getAfterMessage(request));
- }
- }
doFilter方法中的beforeRequest和afterRequest方法由子类实现,RequestCachingRequestWrapper为AbstractRequestLoggingFilter的内部内,主要作用是重置request。
区别
我们在使用过滤器时,通常没必要知道GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter,但不防碍我们了解这几个类,就上文所述,
AbstractRequestLoggingFilter继承自OncePerRequestFilter,
OncePerRequestFilter继承自GenericFilterBean,
所以我们知道,genericFilterBean是任何类型的过滤器的一个比较方便的超类,
这个类主要实现的就是从web.xml文件中取得init-param中设定的值,然后对Filter进行初始化(当然,其子类可以覆盖init方法)。
OncePerRequestFilter继承自GenericFilterBean,那么它自然知道怎么去获取配置文件中的属性及其值,所以其重点不在于取值,而在于确保在接收到一个request后,每个filter只执行一次,它的子类只需要关注Filter的具体实现即doFilterInternal。
AbstractRequestLoggingFilter是对OncePerRequestFilter的扩展,它除了遗传了其父类及祖先类的所有功能外,还在doFilterInternal中决定了在过滤之前和之后执行的事件,它的子类关注的是beforeRequest和afterRequest。
总体来说,这三个类分别执行了Filter的某部分功能,当然,具体如何执行由它们的子类规定,若你需要实现自己的过滤器,也可以根据上文所述继承你所需要的类
==============================================================================================================
Spring MVC过滤器-上下文信息过滤器(AbstractRequestLoggingFilter及其子类)
http://blog.****.net/geloin/article/details/7443329
AbstractRequestLoggingFilter类定义了两个方法beforeRequest和afterRequest分别用于设定过滤前后执行的操作,
它有三个子类,
分别是CommonsRequestLoggingFilter、
ServletContextRequestLoggingFilter
和Log4jNestedDiagnosticContextFilter,
这三个子类分别实现了各自的beforeRequest和afterRequest。其中,
CommonsRequestLoggingFilter在过滤前后分别打印出一段debug的信息;
ServletContextRequestLoggingFilter在过滤前后分别向日志文件中写入一段日志信息,日志文件可由log4j.properties等指定;
Log4jNestedDiagnosticContextFilter则将日志信息存储到NDC中,NDC采用了一个类似栈的机制来push和pot上下文信息,每一个线程都独立地储存上下文信息,比如说一个servlet就可以针对 每一个request创建对应的NDC,储存客户端地址等信息。
当然,在AbstractRequestLoggingFilter中,还包含很多其他方法:
setIncludeQueryString:查询语句是否包含在日志文件中,true或false;
setIncludeClientInfo:客户地址和session id是否包含在日志中,true或false;
setIncludePayload:负载信息是否包含在日志信息中,true或false;
setMaxPayloadLength:最大负载量,int值;
setBeforeMessagePrefix:日志信息前的信息的前缀;
setBeforeMessageSuffix:日志信息前的信息的后缀;
setAfterMessagePrefix:日志信息后的信息的前缀;
setAfterMessageSuffix:日志信息后的信息的后缀。
以上这些信息均可以在init-param中进行设置。
日志信息在getBeforeMessage和getAfterMessage方法中通过createMessage创建,然后被doFilterInternal调用,
日志信息中至少包含uri信息,其他信息则根据init-param中所设定的值获取,
例如,当includeQueryString的值为true时,日志信息中除uri信息外,还包含query string信息。
由此可以,在spring中已定义的AbstractRequestLoggingFiter的子类过滤器均是用于输入上下文信息,只不过承载的方式不同,
其中CommonsRequestLoggingFilter将上下文信息直接打印;
ServletContextRequestLoggingFilter将上下文信息写入日志文件;
Log4jNestedDiagnosticContextFilter将上下文信息写入NDC中
==============================================================================================================
Spring MVC过滤器-HiddenHttpMethodFilter
http://blog.****.net/geloin/article/details/7444321
因为很少有人用到GET和POST以外的method, 而且只有部分浏览器不支持部分method put,deletede等
但是为了做到程序兼容性更好,spring3.0添加了一个过滤器HiddenHttpMethodFilter,可以将这些请求转换为标准的http方法,使得支持PUT与DELETE,就像可以使用GET、POST、一样,
HiddenHttpMethodFilter的父类是OncePerRequestFilter,它继承了父类的doFilterInternal方法,工作原理是
将jsp页面的form表单的method属性值在doFilterInternal方法中转化为标准的Http方法,即GET,、POST、 HEAD、OPTIONS、PUT、DELETE、
TRACE,然后到Controller中找到对应的方法。
例如,在使用注解时我们可能会在Controller中用于@RequestMapping(value = "list", method = RequestMethod.PUT),
所以如果你的表单中使用的是<form ...,那么这个表单会被提交到标了Controller的Method="PUT"的方法中
需要注意的是,由于doFilterInternal方法只对method为post的表单进行过滤,所以在页面中必须如下设置:
- <form action="..." method="<strong>post</strong>">
- <input type="hidden" name="_method" value="<strong>put</strong>" />
- ......
- </form>
而不是使用:
- <form action="..." method="put">
- ......
- </form>
- <filter>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <servlet-name>spring</servlet-name>
- </filter-mapping>
- <servlet>
- <servlet-name>spring</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:dispatcher.xml</param-value>
- </init-param>
- lt;/servlet>
- <servlet-mapping>
- <servlet-name>spring</servlet-name>
- <url-pattern>*.html</url-pattern>
- lt;/servlet-mapping>
同样的,作为Filter,可以在web.xml中配置HiddenHttpMethodFilter的参数,可配置的参数为methodParam,
如果不在web.xml里重新配置的话,default的值为 “_method”,而他在hidden里对应的value为http method的任意一个即可
- <input type="hidden" name="_method" value="<strong>put</strong>" />
如果不想用default的“_method”,可以在web.xml里重新配置,比如abcd_abc,提供这个修改可能是为了防止你的form表单里如果已经有“_method”的定义了,不能用于HiddenHttpMethodFilter实现的目的,通过它可以重写
- <input type="hidden" name="abcd_abc" value="<strong>put</strong>" />
==============================================================================================================
Spring MVC过滤器-HttpPutFormContentFilter
http://blog.****.net/geloin/article/details/7444590
在Spring MVC过滤器-HiddenHttpMethodFilter中我们提到,jsp或者说html中的form的method值只能为post或get,我们可以通过HiddenHttpMethodFilter获取put表单中的参数-值
而在Spring3.0中获取put表单的参数-值还有另一种方法,即使用HttpPutFormContentFilter过滤器
HttpPutFormContentFilter过滤器的作为就是获取put表单的值,并将之传递到Controller中标注了method为RequestMethod.put的方法中。
在web.xml中配置HttpPutFormContentFilter的代码类似如下:
- <filter>
- <filter-name>httpPutFormcontentFilter</filter-name>
- <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>httpPutFormContentFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
需要注意的是,该过滤器只能接受enctype值为application/x-www-form-urlencoded的表单,也就是说,在使用该过滤器时,form表单的代码必须如下
- <form action="" method="put" enctype="<strong>application/x-www-form-urlencoded</strong>">
- ......
- </form>
==============================================================================================================
ShallowEtagHeaderFilter
ShallowEtagHeaderFilter是spring提供的支持ETag的一个过滤器,所谓ETag是指被请求变量的实体值,是一个可以与Web资源关联的记号,而Web资源可以是一个Web页,也可以是JSON或XML文档,服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:
ETag:"50b1c1d4f775c61:df3"
客户端的查询更新格式是这样的:
If-None-Match : W / "50b1c1d4f775c61:df3"
如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。
ShallowEtagHeaderFilter会将JSP等的内容缓存,生成MD5的key,然后在response中作为Etage的header返回给客户端。下次客户端对相同的资源(或者说相同的url)发出请求时,客户端会将之前生成的key作为If-None-Match的值发送到server端。 Filter会客户端传来的值和服务器上的做比较,如果相同,则返回304;否则,将发送新的内容到客户端。
查看ShallowEtagHeaderFilter的源码如下:
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{
- ShallowEtagResponseWrapper responseWrapper = new ShallowEtagResponseWrapper(response, null);
- filterChain.doFilter(request, responseWrapper);
- // 由此可知,服务器仍会处理请求
- byte[] body = responseWrapper.toByteArray();
- int statusCode = responseWrapper.getStatusCode();
- if (isEligibleForEtag(request, responseWrapper, statusCode, body)) {
- String responseETag = generateETagHeaderValue(body);
- response.setHeader(HEADER_ETAG, responseETag);
- String requestETag = request.getHeader(HEADER_IF_NONE_MATCH);
- if (responseETag.equals(requestETag)) {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("ETag [" + responseETag + "] equal to If-None-Match, sending 304");
- }
- response.setStatus(304);
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag + "], sending normal response");
- }
- copyBodyToResponse(body, response);
- }
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("Response with status code [" + statusCode + "] not eligible for ETag");
- }
- copyBodyToResponse(body, response);
- }
- }
由源码可知,ShallowEtagHeaderFilter只能根据结果判断是否重新向客户端发送数据,并不会不处理请求,因此节省带宽,而不能提高服务器性能。
配置ShallowEtagHeaderFilter的代码如下:
- <filter>
- <filter-name>shallowEtagHeaderFilter</filter-name>
- <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</fliter-class>
- </filter>
- <filter-mapping>
- <filter-name>shallowEtagHeaderFilter</filter-name>
- <servlet-name>spring</servlet-name>
- </filter-mapping>
RequestContextFilter
这是在Spring2.0时添加的类,通过LocaleContextHolder和RequestContextHolder把Http request对象基于LocalThread绑定到请求提供服务的线程上。现在一般使用DispatcherServlet这个*分发器。现在RequestContextFilter过滤器主要用于第三方的Servlet,如JSF的FacesServlet。在Spring2.5之前都是使用该过滤器配置。配置如下:
- <filter>
- <filter-name>RequestContextFilter</filter-name>
- <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>RequestContextFilter</filter-name>
- <servlet-name>Faces Servlet</servlet-name>
- </filter-mapping>
http://liuluo129.iteye.com/blog/1965268
图来自上面连接
http://blog.****.net/geloin/article/details/7441330
整理于上面
Spring的web包中中有很多过滤器,这些过滤器位于org.springframework.web.filter并且理所当然地实现了javax.servlet.Filter,不过实现的方式有以下几类:
(1) 直接实现Filter,这一类过滤器只有CompositeFilter;
(2) 继承抽象类GenericFilterBean,该类实现了javax.servlet.Filter,这一类的过滤器只有一个,即DelegatingFilterProxy;
(3) 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;
(4) 继承抽象类AbstractRequestLoggingFilter,该类为OncePerRequestFilter的直接子类,这一类过滤器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。
本文要讲述的,即是GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter。
GenericFilterBean
抽象类GenericFilterBean实现了javax.servlet.Filter、org.springframework.beans.factory.BeanNameAware、org.springframework.context.EnvironmentAware、org.springframework.web.context.ServletContextAware、org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean五个接口,作用如下:
(1) Filter,实现过滤器;
(2) BeanNameAware,实现该接口的setBeanName方法,便于Bean管理器生成Bean;
(3) EnvironmentAware,实现该接口的setEnvironment方法,指明该Bean运行的环境;
(4) ServletContextAware,实现该接口的setServletContextAware方法,指明上下文;
(5) InitializingBean,实现该接口的afterPropertiesSet方法,指明设置属性生的操作;
(6) DisposableBean,实现该接口的destroy方法,用于回收资源。
GenericFilterBean的工作流程是:init-doFilter-destory,其中的init和destory在该类中实现,doFilter在具体实现类中实现。init的代码如下:
- /**
- * Standard way of initializing this filter.
- * Map config parameters onto bean properties of this filter, and
- * invoke subclass initialization.
- * @param filterConfig the configuration for this filter
- * @throws ServletException if bean properties are invalid (or required
- * properties are missing), or if subclass initialization fails.
- * @see #initFilterBean
- */
- public final void init(FilterConfig filterConfig) throws ServletException {
- Assert.notNull(filterConfig, "FilterConfig must not be null");
- if (logger.isDebugEnabled()) {
- logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
- }
- this.filterConfig = filterConfig;
- // Set bean properties from init parameters.
- try {
- PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- initBeanWrapper(bw);
- bw.setPropertyValues(pvs, true);
- }
- catch (BeansException ex) {
- String msg = "Failed to set bean properties on filter '" +
- filterConfig.getFilterName() + "': " + ex.getMessage();
- logger.error(msg, ex);
- throw new NestedServletException(msg, ex);
- }
- // Let subclasses do whatever initialization they like.
- initFilterBean();
- if (logger.isDebugEnabled()) {
- logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
- }
- }
该方法来自于javax.servlet.Filter,即过滤器的初始化,它的主要工作集中于以下几行代码:
- // 从properties文件中获取值,这里是web.xml
- PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
- // 设置bean适配器
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- // 设置上下文,这里的servletContext的设定继承自ServletContextAware的setter
- ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
- // 将上下文信息和环境信息设置到bean适配器中,这里的environment来自于EnvironmentAware的setter
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- // 初始化bean适配器
- initBeanWrapper(bw);
- // 将从properties中获取的资源放置到bean适配器
- bw.setPropertyValues(pvs, true);
- // 初始化bean
- initFilterBean();
其中initFilterBean方法在两个位置起作用,一处是上文所述的init方法,另一处是afterPropertiesSet方法,在调用该方法前,需要保证用于Filter的所有的bean都已被设置,该方法由子类实现。
GenericFilterBean中包含一个内部私有类FilterConfigPropertyValues,主要用于将web.xml中定义的init-param的值取出。
OncePerRequestFilter
抽象类oncePerRequestFilter继承自GenericFilterBean,它保留了GenericFilterBean中的所有方法并对之进行了扩展,在oncePerRequestFilter中的主要方法是doFilter,代码如下:
- /**
- * This <code>doFilter</code> implementation stores a request attribute for
- * "already filtered", proceeding without filtering again if the
- * attribute is already there.
- * @see #getAlreadyFilteredAttributeName
- * @see #shouldNotFilter
- * @see #doFilterInternal
- */
- public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
- throw new ServletException("OncePerRequestFilter just supports HTTP requests");
- }
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
- // 调用GenericFilterBean的getFilterName方法返回已过滤的属性名
- String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
- if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {
- // 未调用该过滤器或已过滤
- filterChain.doFilter(request, response);
- }
- else {
- // 进行过滤
- request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
- try {
- doFilterInternal(httpRequest, httpResponse, filterChain);
- }
- finally {
- // Remove the "already filtered" request attribute for this request.
- request.removeAttribute(alreadyFilteredAttributeName);
- }
- }
- }
在doFilter方法中,doFilterInternal方法由子类实现,主要作用是规定过滤的具体方法。
AbstractRequestLoggingFilter
AbstractRequestLoggingFilter继承了OncePerRequestFilter并实现了其doFilterInternal方法,该方法代码如下:
- /**
- * Forwards the request to the next filter in the chain and delegates down to the subclasses to perform the actual
- * request logging both before and after the request is processed.
- *
- * @see #beforeRequest
- * @see #afterRequest
- */
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- if (isIncludePayload()) {
- // 若日志中包含负载,则重置request
- request = new RequestCachingRequestWrapper(request);
- }
- // 过滤前执行的方法
- beforeRequest(request, getBeforeMessage(request));
- try {
- // 执行过滤
- filterChain.doFilter(request, response);
- }
- finally {
- // 过滤后执行的方法
- afterRequest(request, getAfterMessage(request));
- }
- }
doFilter方法中的beforeRequest和afterRequest方法由子类实现,RequestCachingRequestWrapper为AbstractRequestLoggingFilter的内部内,主要作用是重置request。
区别
我们在使用过滤器时,通常没必要知道GenericFilterBean、OncePerRequestFilter和AbstractRequestLoggingFilter,但不防碍我们了解这几个类,就上文所述,
AbstractRequestLoggingFilter继承自OncePerRequestFilter,
OncePerRequestFilter继承自GenericFilterBean,
所以我们知道,genericFilterBean是任何类型的过滤器的一个比较方便的超类,
这个类主要实现的就是从web.xml文件中取得init-param中设定的值,然后对Filter进行初始化(当然,其子类可以覆盖init方法)。
OncePerRequestFilter继承自GenericFilterBean,那么它自然知道怎么去获取配置文件中的属性及其值,所以其重点不在于取值,而在于确保在接收到一个request后,每个filter只执行一次,它的子类只需要关注Filter的具体实现即doFilterInternal。
AbstractRequestLoggingFilter是对OncePerRequestFilter的扩展,它除了遗传了其父类及祖先类的所有功能外,还在doFilterInternal中决定了在过滤之前和之后执行的事件,它的子类关注的是beforeRequest和afterRequest。
总体来说,这三个类分别执行了Filter的某部分功能,当然,具体如何执行由它们的子类规定,若你需要实现自己的过滤器,也可以根据上文所述继承你所需要的类
==============================================================================================================
Spring MVC过滤器-上下文信息过滤器(AbstractRequestLoggingFilter及其子类)
http://blog.****.net/geloin/article/details/7443329
AbstractRequestLoggingFilter类定义了两个方法beforeRequest和afterRequest分别用于设定过滤前后执行的操作,
它有三个子类,
分别是CommonsRequestLoggingFilter、
ServletContextRequestLoggingFilter
和Log4jNestedDiagnosticContextFilter,
这三个子类分别实现了各自的beforeRequest和afterRequest。其中,
CommonsRequestLoggingFilter在过滤前后分别打印出一段debug的信息;
ServletContextRequestLoggingFilter在过滤前后分别向日志文件中写入一段日志信息,日志文件可由log4j.properties等指定;
Log4jNestedDiagnosticContextFilter则将日志信息存储到NDC中,NDC采用了一个类似栈的机制来push和pot上下文信息,每一个线程都独立地储存上下文信息,比如说一个servlet就可以针对 每一个request创建对应的NDC,储存客户端地址等信息。
当然,在AbstractRequestLoggingFilter中,还包含很多其他方法:
setIncludeQueryString:查询语句是否包含在日志文件中,true或false;
setIncludeClientInfo:客户地址和session id是否包含在日志中,true或false;
setIncludePayload:负载信息是否包含在日志信息中,true或false;
setMaxPayloadLength:最大负载量,int值;
setBeforeMessagePrefix:日志信息前的信息的前缀;
setBeforeMessageSuffix:日志信息前的信息的后缀;
setAfterMessagePrefix:日志信息后的信息的前缀;
setAfterMessageSuffix:日志信息后的信息的后缀。
以上这些信息均可以在init-param中进行设置。
日志信息在getBeforeMessage和getAfterMessage方法中通过createMessage创建,然后被doFilterInternal调用,
日志信息中至少包含uri信息,其他信息则根据init-param中所设定的值获取,
例如,当includeQueryString的值为true时,日志信息中除uri信息外,还包含query string信息。
由此可以,在spring中已定义的AbstractRequestLoggingFiter的子类过滤器均是用于输入上下文信息,只不过承载的方式不同,
其中CommonsRequestLoggingFilter将上下文信息直接打印;
ServletContextRequestLoggingFilter将上下文信息写入日志文件;
Log4jNestedDiagnosticContextFilter将上下文信息写入NDC中
==============================================================================================================
Spring MVC过滤器-HiddenHttpMethodFilter
http://blog.****.net/geloin/article/details/7444321
因为很少有人用到GET和POST以外的method, 而且只有部分浏览器不支持部分method put,deletede等
但是为了做到程序兼容性更好,spring3.0添加了一个过滤器HiddenHttpMethodFilter,可以将这些请求转换为标准的http方法,使得支持PUT与DELETE,就像可以使用GET、POST、一样,
HiddenHttpMethodFilter的父类是OncePerRequestFilter,它继承了父类的doFilterInternal方法,工作原理是
将jsp页面的form表单的method属性值在doFilterInternal方法中转化为标准的Http方法,即GET,、POST、 HEAD、OPTIONS、PUT、DELETE、
TRACE,然后到Controller中找到对应的方法。
例如,在使用注解时我们可能会在Controller中用于@RequestMapping(value = "list", method = RequestMethod.PUT),
所以如果你的表单中使用的是<form ...,那么这个表单会被提交到标了Controller的Method="PUT"的方法中
需要注意的是,由于doFilterInternal方法只对method为post的表单进行过滤,所以在页面中必须如下设置:
- <form action="..." method="<strong>post</strong>">
- <input type="hidden" name="_method" value="<strong>put</strong>" />
- ......
- </form>
而不是使用:
- <form action="..." method="put">
- ......
- </form>
- <filter>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <servlet-name>spring</servlet-name>
- </filter-mapping>
- <servlet>
- <servlet-name>spring</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:dispatcher.xml</param-value>
- </init-param>
- lt;/servlet>
- <servlet-mapping>
- <servlet-name>spring</servlet-name>
- <url-pattern>*.html</url-pattern>
- lt;/servlet-mapping>
同样的,作为Filter,可以在web.xml中配置HiddenHttpMethodFilter的参数,可配置的参数为methodParam,
如果不在web.xml里重新配置的话,default的值为 “_method”,而他在hidden里对应的value为http method的任意一个即可
- <input type="hidden" name="_method" value="<strong>put</strong>" />
如果不想用default的“_method”,可以在web.xml里重新配置,比如abcd_abc,提供这个修改可能是为了防止你的form表单里如果已经有“_method”的定义了,不能用于HiddenHttpMethodFilter实现的目的,通过它可以重写
- <input type="hidden" name="abcd_abc" value="<strong>put</strong>" />
==============================================================================================================
Spring MVC过滤器-HttpPutFormContentFilter
http://blog.****.net/geloin/article/details/7444590
在Spring MVC过滤器-HiddenHttpMethodFilter中我们提到,jsp或者说html中的form的method值只能为post或get,我们可以通过HiddenHttpMethodFilter获取put表单中的参数-值
而在Spring3.0中获取put表单的参数-值还有另一种方法,即使用HttpPutFormContentFilter过滤器
HttpPutFormContentFilter过滤器的作为就是获取put表单的值,并将之传递到Controller中标注了method为RequestMethod.put的方法中。
在web.xml中配置HttpPutFormContentFilter的代码类似如下:
- <filter>
- <filter-name>httpPutFormcontentFilter</filter-name>
- <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>httpPutFormContentFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
需要注意的是,该过滤器只能接受enctype值为application/x-www-form-urlencoded的表单,也就是说,在使用该过滤器时,form表单的代码必须如下
- <form action="" method="put" enctype="<strong>application/x-www-form-urlencoded</strong>">
- ......
- </form>
==============================================================================================================
ShallowEtagHeaderFilter
ShallowEtagHeaderFilter是spring提供的支持ETag的一个过滤器,所谓ETag是指被请求变量的实体值,是一个可以与Web资源关联的记号,而Web资源可以是一个Web页,也可以是JSON或XML文档,服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:
ETag:"50b1c1d4f775c61:df3"
客户端的查询更新格式是这样的:
If-None-Match : W / "50b1c1d4f775c61:df3"
如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。
ShallowEtagHeaderFilter会将JSP等的内容缓存,生成MD5的key,然后在response中作为Etage的header返回给客户端。下次客户端对相同的资源(或者说相同的url)发出请求时,客户端会将之前生成的key作为If-None-Match的值发送到server端。 Filter会客户端传来的值和服务器上的做比较,如果相同,则返回304;否则,将发送新的内容到客户端。
查看ShallowEtagHeaderFilter的源码如下:
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{
- ShallowEtagResponseWrapper responseWrapper = new ShallowEtagResponseWrapper(response, null);
- filterChain.doFilter(request, responseWrapper);
- // 由此可知,服务器仍会处理请求
- byte[] body = responseWrapper.toByteArray();
- int statusCode = responseWrapper.getStatusCode();
- if (isEligibleForEtag(request, responseWrapper, statusCode, body)) {
- String responseETag = generateETagHeaderValue(body);
- response.setHeader(HEADER_ETAG, responseETag);
- String requestETag = request.getHeader(HEADER_IF_NONE_MATCH);
- if (responseETag.equals(requestETag)) {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("ETag [" + responseETag + "] equal to If-None-Match, sending 304");
- }
- response.setStatus(304);
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag + "], sending normal response");
- }
- copyBodyToResponse(body, response);
- }
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("Response with status code [" + statusCode + "] not eligible for ETag");
- }
- copyBodyToResponse(body, response);
- }
- }
由源码可知,ShallowEtagHeaderFilter只能根据结果判断是否重新向客户端发送数据,并不会不处理请求,因此节省带宽,而不能提高服务器性能。
配置ShallowEtagHeaderFilter的代码如下:
- <filter>
- <filter-name>shallowEtagHeaderFilter</filter-name>
- <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</fliter-class>
- </filter>
- <filter-mapping>
- <filter-name>shallowEtagHeaderFilter</filter-name>
- <servlet-name>spring</servlet-name>
- </filter-mapping>
RequestContextFilter
这是在Spring2.0时添加的类,通过LocaleContextHolder和RequestContextHolder把Http request对象基于LocalThread绑定到请求提供服务的线程上。现在一般使用DispatcherServlet这个*分发器。现在RequestContextFilter过滤器主要用于第三方的Servlet,如JSF的FacesServlet。在Spring2.5之前都是使用该过滤器配置。配置如下:
- <filter>
- <filter-name>RequestContextFilter</filter-name>
- <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>RequestContextFilter</filter-name>
- <servlet-name>Faces Servlet</servlet-name>
- </filter-mapping>