spring 过滤器
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 过滤器的更多相关文章
- spring 过滤器简介
spring 过滤器简介 过滤器放在容器结构的什么位置 过滤器放在web资源之前,可以在请求抵达它所应用的web资源(可以是一个Servlet.一个Jsp页面,甚至是一个HTML页面)之前截获进入的请 ...
- spring过滤器
什么是过滤器 Spring 中不能处理用户请求,但可以用来提供过滤作用的一种Servlet规范.在请求进入容器之后,还未进入Servlet之前进行预处理,并且在请求结束返回给前端这之间进行后期处理.具 ...
- Spring过滤器组件自动扫描
在这个Spring自动组件扫描的教程,您已经了解如何使Spring自动扫描您的组件.在这篇文章中,我们将展示如何使用组件过滤器自动扫描过程. 1.过滤组件 - 包含 参见下面的例子中使用Spring ...
- spring过滤器和拦截器的区别和联系
一 简介 (1)过滤器: 依赖于servlet容器,是JavaEE标准,是在请求进入容器之后,还未进入Servlet之前进行预处理,并且在请求结束返回给前端这之间进行后期处理.在实现上基于函数回调,可 ...
- spring过滤器篇
CharacterEncodingFilter spring的web包下的一个过滤器,用于设置程序的字符编码,它有两个参数encoding和forceEncoding. encoding:字符集,将过 ...
- spring:过滤器和拦截器
过滤器:网络通信模型的会话层控制: 拦截器:事务处理的aop注入(生命周期监控). 对于Servlet Filter,官方文档中说的很好, 并且给出了常见的应用场景. A filter is an o ...
- spring 过滤器- 过滤登陆请求路径(过滤静态资源跳转到登陆页面)
public class LoginedFilter implements Filter { /** * 排除的地址 */ private Map<String, Boolean> ign ...
- Spring字符集过滤器CharacterEncodingFilter
Spring中的字符集过滤器可以很方便的为我们解决项目中出现的中文乱码问题,而且使用方法也很简单,只需要在web.xml文件中配置一下该过滤器,设置两个重要的参数(encoding和forceEnco ...
- Spring 配置请求过滤器,编码格式设为UTF-8,避免中文乱码
<!-- 配置请求过滤器,编码格式设为UTF-8,避免中文乱码--> <filter> <filter-name>springUtf8Encoding</fi ...
随机推荐
- ant执行jar包中的main方法
<project name= "myproject" basedir= "." default="main"> <p ...
- C语言学习笔记 (007) - 数组指针和通过指针引用数组元素的方法总结
1.数组指针:即指向数组的指针 那么, 如何声明一个数组指针呢? ]; /*括号是必须写的,不然就是指针数组:10是数组的大小*/ 拓展:有指针类型元素的数组称为指针数组. 2.通过指针引用数组元素的 ...
- mac 终端添加颜色
1.打开终端,然后找到终端偏好设置,选择自己喜欢的颜色 2.然后切换到当前用户的家目录: cd ~ 3.打开文件,开始编辑".bash_profile", 添加下面两句 expor ...
- golang学习笔记 ---数组与切片
数组: golang数组包含的每个数据称为数组元素(element),数组包含的元素个数被称为数组长度(length). golang数组的长度在定义后不可更改,并且在声明时可以是一个常量或常量表达式 ...
- Openlayers2中统计图的实现
概述: 在前文中.介绍了Arcgis for js和Openlayers3中统计图的实现.在本文,书接上文.介绍在Openlayers2中,统计图的实现. 实现: 在Openlayers2中,popu ...
- asp.net中Winform开发框架之数据即时更新的实现
在开篇之前,首先介绍一下Winform开发框架中的界面层的继承关系,首先我把所有窗体区分为三类,一类是普通的窗体,一类编辑窗体,还有一类是列表显示窗体.窗体的集成可以封装处理很多东西,良好的封装和继承 ...
- Python 文件 next() 方法
描述 Python 3 中的 文件 对象不支持 next() 方法. Python 3 的内置函数 next() 通过迭代器调用 __next__() 方法返回下一项. 在循环中,next()方法会在 ...
- 【转载】Eclipse中.setting目录下文件介绍
原文:http://blog.csdn.net/huaweitman/article/details/52351394 Eclipse在新建项目的时候会自动生成一些文件.这些文件比如.project. ...
- [转]如何在 Git 里撤销(几乎)任何操作
任何版本控制系统的一个最有的用特性就是“撤销 (undo)”你的错误操作的能力.在 Git 里,“撤销” 蕴含了不少略有差别的功能. 当你进行一次新的提交的时候,Git 会保存你代码库在那个特定时间点 ...
- appium 重新启动apk
在旧版本的appium,重新启动apk,调用startActivity方法可以随意启动一个app,并传入一个package name 和启动activity name的名称.语句如下: driver. ...