SpringMVC——过滤器实现和理解

一、先提出几个问题

?过滤器是什么?
?过滤器是如何实现的?
?过滤器和拦截器的区别?
?过滤器的核心原理是什么(阅读源码)?

之前我学东西总是不够深入,现在决定换个思路来学习。
每次学一个东西,都先提出问题,让自己带着兴趣去实践和学习。
1、先问是什么?
2、然后怎么做?实践的过程能更加熟练并提出更深入的问题
3、最后为什么?这时候再看看源码,刚才实践过程中遇到了哪些问题,这时候能找出答案。

二、过滤器是什么

参考博客SpringMVC的拦截器(Interceptor)和过滤器(Filter)的区别与联系

依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等

我的理解,就是对所有请求进行过滤操作,获取请求携带的数据或者修改请求的某些参数。

三、过滤器的实现

1、Spring自带的字符编码过滤器

过滤器常用的场景:解决中文乱码问题。

我搭建好了一个Spring+SpringMVC的简单项目框架,项目运行之后你能从浏览器请求到页面。

字符编码过滤器主要是过滤reponse响应,我用了一个添加用户信息和页面显示,来直观地看到响应被处理的过程。

Controller控制器

    @RequestMapping(value="/add",method=RequestMethod.GET)public String add(){return "add";}@RequestMapping(value="/add",method=RequestMethod.POST)public void addPost(HttpServletRequest request,HttpServletResponse response,Model model) throws IOException{//姓名String name=request.getParameter("name");  System.out.println("name>>>"+name);//国家String country=request.getParameter("country"); System.out.println("country>>>"+country);//性别String sex = request.getParameter("sex"); System.out.println("sex>>>"+sex);//获取PrintWriter  PrintWriter out = response.getWriter(); out.print("name : "+name+"|");  out.print("country : "+country+"|");  out.print("sex : "+sex);  out.flush();//刷新流  out.close();//关闭流 }

页面add.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body><form action="./add" method="post">  <table align="center" border="1" width="350">  <tr>  <td class="2" align="center" colspan="2">  <h2>添加用户信息</h2>  </td>  </tr>  <tr>  <td align="right">姓名:</td>  <td>  <input type="text" name="name">  </td>  </tr>  <tr>  <td align="right">国家:</td>  <td>  <input type="text" name="country">  </td>  </tr>  <tr>  <td align="right">性别:</td>  <td>  <input type="text" name="sex">  </td>  </tr>  <tr>  <td class="2" align="center" colspan="2">  <input type="submit" value="添 加">  </td>  </tr>  </table>
</form>  </body>
</html>

从浏览器进入到添加用户的界面

点击”添加”打印到页面上都是乱码了。

控制台打印输出是正常正文显示

现在添加字符编码过滤器
web.xml

    <!-- 字符集过滤器 --><filter><description>字符集过滤器</description>  <filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><init-param>  <param-name>forceEncoding</param-name>  <param-value>true</param-value>  </init-param> </filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>

注意,这里少了forceEncoding的参数也不行。

forceEncoding是强制字符集
true处理请求和响应,相当于:
request.setCharacterEncoding(“”);
response.setCharacterEncoding(“”);
false只处理请求,相当于
request.setCharacterEncoding(“”);

然后再次请求,就能正常显示中文了。

进行到这一步,对这个过滤器的实现有了一定的了解。这是Spring自带的字符编码过滤器,用起来太简单了,只需要在web.xml中配置。

如果我们自定义过滤器的话,又该怎么配置呢?

2、自定义过滤器(权限过滤器,实现uri过滤)

这里写一个uri过滤器,同时因为兴趣原因,我也用cookie实现了自动登录的功能。

主要还是对过滤器进行说明,cookie自动登录只是写了点皮毛,也不值得拿出来说了。

uri过滤器主要实现的功能是:除了/login的uri,都要进行过滤处理,获取session值,看session里是否有用户信息,没有用户信息,就跳到登录页面。

参考了博客springmvc 登录鉴权过滤器

(1)写个过滤器的类SessionFilter.class


import java.io.IOException;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;import com.mocha.model.User;public class SessionFilter extends OncePerRequestFilter{@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {//不过滤的uriString[] notFilter = new String[]{"/login"};//请求的uriString uri = request.getRequestURI();System.out.println("filter>>>uri>>>"+uri);//是否过滤boolean doFilter = true;for(String s : notFilter){if(uri.indexOf(s) != -1){//uri中包含不过滤uri,则不进行过滤doFilter = false;break;}}if(doFilter){System.out.println("doFilter>>>");//过滤操作//从session中获取登陆者实体Object obj = request.getSession().getAttribute("user");if(obj==null){System.out.println("doFilter>>>obj is null");boolean isAjaxRequest = isAjaxRequest(request);if(isAjaxRequest){response.setCharacterEncoding("UTF-8");response.sendError(HttpStatus.UNAUTHORIZED.value(),"您已经太长时间没有操作,请刷新页面");System.out.println("doFilter>>>ajax request");return ;}else{System.out.println("doFilter>>>http request");response.sendRedirect("./login");//跳转到登录页面return ;}}else{User user = (User) obj;System.out.println("doFilter>>>username>>"+user.getUsername());// 如果session中存在登录者实体,则继续  filterChain.doFilter(request, response);}}else{System.out.println("no Filter>>>");//不执行过滤操作filterChain.doFilter(request, response);}}/*** is Ajax request* @param request* @return*/private boolean isAjaxRequest(HttpServletRequest request) {String header = request.getHeader("X-Requested-With");if(header != null && "XMLHttpRequest".equals(header)){//ajax requestreturn true;}else{//traditional sync http requestreturn false;}}}

(2)web.xml

<filter><description>session过滤器</description><filter-name>sessionFilter</filter-name><filter-class>com.mocha.filter.SessionFilter</filter-class></filter><filter-mapping><filter-name>sessionFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>

(3)控制器

@RequestMapping(value="/login",method=RequestMethod.GET)public String login(HttpServletRequest request,HttpServletResponse response){//获取CookieCookie[] cookies = request.getCookies();for(Cookie cookie : cookies){System.out.println("cookie>>"+cookie.getValue());//从数据库获取保存的cookieSession session = iSessionDAO.getSession(cookie.getValue());if(session!=null){//如果存在,就跳转到首页return "index";}}return "login";}@RequestMapping(value="/login",method=RequestMethod.POST)public String loginPOST(HttpServletRequest request, HttpServletResponse response,Model model){//用户名String username=request.getParameter("username");  System.out.println("username>>>"+username);//密码String password=request.getParameter("password"); System.out.println("password>>>"+password);//先从数据库查找该账号信息User user = null;try {user = iUserDAO.queryForUser(username);} catch (NullPointerException e) {e.printStackTrace();model.addAttribute("message", "No account");}if(user==null){model.addAttribute("message", "No account");}else{// 匹配密码if (user.getPassword().equals(password)) {//登录成功,保存sessionrequest.getSession().setAttribute("user", user);// 保存cookieCookie[] cookies = request.getCookies();Cookie cookie = cookies[0];//获得最新的那个cookieSession isSession = iSessionDAO.getSessionByUserId(user.getId());//没有session,就添加if(isSession==null){Session session = new Session();session.setId(UUID.randomUUID().toString());session.setSession(cookie.getValue());session.setUser_id(user.getId());System.out.println("cookie>>" + cookie.getValue());iSessionDAO.save(session);System.out.println("==添加session==");}else{//如果已经有session,就更新isSession.setSession(cookie.getValue());iSessionDAO.update(isSession);System.out.println("==更新session==");}model.addAttribute("message", user.getUsername());return "index";}else{model.addAttribute("message", "Wrong password");}}return "login";}@RequestMapping(value="/sessionTest",method=RequestMethod.GET)public String sessionTest(HttpServletRequest request,HttpServletResponse response,Model model){System.out.println(">>>sessionTest");model.addAttribute("message", "sessionTest");return "index";}

这时候请求控制器,进入login.jsp

此时控制台打印显示经过了过滤器,但是没有进行过滤操作。

输入账号信息登录

跳转到首页index.jsp

这次跳转其实进行了一个操作,控制器里有个添加请求的session的操作
request.getSession().setAttribute(“user”, user);
将这个账号信息保存到了session里,这个session 能够保持到浏览器关闭之前。
比如我这时候请求一个进行过滤操作的uri

此时控制台打印消息:
filter>>>uri>>>/spring-basic/sessionTest
doFilter>>>
doFilter>>>username>>kim
也就是说,经过了过滤处理,并且拿到了session里user的值。

当我打开一个新的浏览器进行请求的时候,由于没有session,就会跳转到登录页。

到这里,有了实际操作,我对过滤器有了一定的了解。

四、过滤器和拦截器的区别

这里参考了博客SpringMVC的拦截器(Interceptor)和过滤器(Filter)的区别与联系

什么是拦截器

依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理

多个过滤器与拦截器的代码执行顺序

  • 执行顺序跟在SpringMVC的配置文件中定义的先后顺序有关

五、过滤器原理

终于到了琢磨源码的时刻。
有了初步的实践,现在再来看源码,就不会太过陌生了。

session过滤器里继承了OncePerRequestFilter,可以阅读一下这个过滤器的源码

看到一头雾水,我还是看了别人的讲解,参考博客Spring-web源码解析之Filter-OncePerRequestFilter:

package org.springframework.web.filter;import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.util.WebUtils;/*** Filter base class that aims to guarantee a single execution per request* dispatch, on any servlet container. * It provides a {@link #doFilterInternal}* method with HttpServletRequest and HttpServletResponse arguments.** <p>As of Servlet 3.0, a filter may be invoked as part of a* {@link javax.servlet.DispatcherType#REQUEST REQUEST} or* {@link javax.servlet.DispatcherType#ASYNC ASYNC} dispatches that occur in* separate threads. A filter can be configured in {@code web.xml} whether it* should be involved in async dispatches.* However, in some cases servlet containers assume different default configuration.*  (但是,在某些情况下,servlet容器会采用不同的默认配置。)* Therefore sub-classes can override the method {@link #shouldNotFilterAsyncDispatch()} * to declare statically if they should indeed be invoked, once, during both types* of dispatches in order to provide thread initialization, logging, security,* and so on. This mechanism complements and does not replace the need to* configure a filter in {@code web.xml} with dispatcher types.* 为了提供线程初始化、日志记录、安全性等等,在这两种类型的调度期间,子类可以重写该方法以静态声明它们是否应该被调用。* 这个机制补充不能取代web.xml中对过滤器的配置。* <p>Subclasses may use {@link #isAsyncDispatch(HttpServletRequest)} to* determine when a filter is invoked as part of an async dispatch, and use* {@link #isAsyncStarted(HttpServletRequest)} to determine when the request* has been placed in async mode and therefore the current dispatch won't be* the last one for the given request.* 子类可以使用isAsyncDispatch方法来确定何时将过滤器作为异步调度的一部分进行调用,并使用此方法来确定请求何时处于异步模式,因此当前调度不会是给定请求的最后一个。* <p>Yet another dispatch type that also occurs in its own thread is* {@link javax.servlet.DispatcherType#ERROR ERROR}. Subclasses can override* {@link #shouldNotFilterErrorDispatch()} if they wish to declare statically* if they should be invoked <em>once</em> during error dispatches.** <p>The {@link #getAlreadyFilteredAttributeName} method determines how to* identify that a request is already filtered. The default implementation is* based on the configured name of the concrete filter instance.** @author Juergen Hoeller* @author Rossen Stoyanchev* @since 06.12.2003*/
public abstract class OncePerRequestFilter extends GenericFilterBean {/*** Suffix that gets appended to the filter name for the* "already filtered" request attribute.* @see #getAlreadyFilteredAttributeName*/public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";/*** This {@code doFilter} implementation stores a request attribute for* "already filtered", proceeding without filtering again if the* attribute is already there.* @see #getAlreadyFilteredAttributeName* @see #shouldNotFilter* @see #doFilterInternal*/@Overridepublic 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;String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {// Proceed without invoking this filter...filterChain.doFilter(request, response);}else {// Do invoke this filter...request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);try {doFilterInternal(httpRequest, httpResponse, filterChain);}finally {// Remove the "already filtered" request attribute for this request.request.removeAttribute(alreadyFilteredAttributeName);}}}private boolean skipDispatch(HttpServletRequest request) {if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) {return true;}if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) {return true;}return false;}/*** The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced* in Servlet 3.0 means a filter can be invoked in more than one thread over* the course of a single request. This method returns {@code true} if the* filter is currently executing within an asynchronous dispatch.* @param request the current request* @since 3.2* @see WebAsyncManager#hasConcurrentResult()*/protected boolean isAsyncDispatch(HttpServletRequest request) {return WebAsyncUtils.getAsyncManager(request).hasConcurrentResult();}/*** Whether request processing is in asynchronous mode meaning that the* response will not be committed after the current thread is exited.* @param request the current request* @since 3.2* @see WebAsyncManager#isConcurrentHandlingStarted()*/protected boolean isAsyncStarted(HttpServletRequest request) {return WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted();}/*** Return the name of the request attribute that identifies that a request* is already filtered.* <p>The default implementation takes the configured name of the concrete filter* instance and appends ".FILTERED". If the filter is not fully initialized,* it falls back to its class name.* @see #getFilterName* @see #ALREADY_FILTERED_SUFFIX*/protected String getAlreadyFilteredAttributeName() {String name = getFilterName();if (name == null) {name = getClass().getName();}return name + ALREADY_FILTERED_SUFFIX;}/*** Can be overridden in subclasses for custom filtering control,* returning {@code true} to avoid filtering of the given request.* <p>The default implementation always returns {@code false}.* @param request current HTTP request* @return whether the given request should <i>not</i> be filtered* @throws ServletException in case of errors*/protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {return false;}/*** The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced* in Servlet 3.0 means a filter can be invoked in more than one thread* over the course of a single request. * ASYNC类型在Servlet 3.0中意味着一个过滤器可以在一个请求的情况下中被多个线程调用* Some filters only need to filter the initial thread (e.g. request wrapping)*  while others may need* to be invoked at least once in each additional thread for example for* setting up thread locals or to perform final processing at the very end.* 一些过滤器只需要过滤初始线程(例如request wrapping),而其他过滤器可能需要在每个附加线程中至少调用一次,* 例如用于设置线程本地或在最后执行最终处理。* <p>Note that although a filter can be mapped to handle specific dispatcher* types via {@code web.xml} or in Java through the {@code ServletContext},* servlet containers may enforce different defaults with regards to* dispatcher types. * 请注意,虽然过滤器可以通过{code web.xml}或Java中的通过{@code ServletContext}映射* 来处理特定的调度类型,但servlet容器可能会针对调度类型强制执行不同的默认值。* This flag enforces the design intent of the filter.* 该标志强制执行过滤器的设计意图。* <p>The default return value is "true", which means the filter will not be* invoked during subsequent async dispatches. * 默认返回值是“true”,这意味着在随后的异步调度期间不会调用过滤器。* If "false", the filter will* be invoked during async dispatches with the same guarantees of being* invoked only once during a request within a single thread.* 如果为“false”,则将在异步调度期间调用过滤器,并保证在单个线程的请求期间仅调用一次该过滤器。* @since 3.2*/protected boolean shouldNotFilterAsyncDispatch() {return true;}/*** Whether to filter error dispatches such as when the servlet container* processes and error mapped in {@code web.xml}. * 是否过滤错误调度,例如,当servlet容器处理和错误映射到{web.xml}时。* The default return value is "true", which means the filter will not be * invoked in case of an error dispatch.* 默认返回值是true,意味着如果发生错误,过滤器将不被调用。* @since 3.2*/protected boolean shouldNotFilterErrorDispatch() {return true;}/*** Same contract as for {@code doFilter}, but guaranteed to be* just invoked once per request within a single request thread.* 与doFilter相同的协定,但是确保了在单个请求线程里每次请求只调用了一次。* See {@link #shouldNotFilterAsyncDispatch()} for details.* <p>Provides HttpServletRequest and HttpServletResponse arguments instead of the* default ServletRequest and ServletResponse ones.* */protected abstract void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException;}

另外还有父类GenericFilterBean
这里参考翻译:GenericFilterBean-api
这个父类主要是获取web.xml的配置参数对过滤器进行配置的。

package org.springframework.web.filter;import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceEditor;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.support.ServletContextResourceLoader;
import org.springframework.web.context.support.StandardServletEnvironment;
import org.springframework.web.util.NestedServletException;/*** Simple base implementation of {@link javax.servlet.Filter} which treats* its config parameters (<code>init-param</code> entries within the* <code>filter</code> tag in <code>web.xml</code>) as bean properties.* * 接口GenericFilterBean是javax.servlet.Filter接口的一个基本的实现类,特别的是,* 接口GenericFilterBean将web.xml中filter标签中的配置参数-init-param项作为bean的属性。* * <p>A handy superclass for any type of filter. Type conversion of config* parameters is automatic, with the corresponding setter method getting* invoked with the converted value. It is also possible for subclasses to* specify required properties. Parameters without matching bean property* setter will simply be ignored.* * 接口GenericFilterBean可以简单地成为任何类型的filter的父类。配置参数的值的类型转换通过调用* 相应的setter 方法来自动进行的。接口GenericFilterBean的子类可以自定义一些自己需要的属性。但是,* 配置文件中没有对应bean属性setter方法的参数会被忽略。* * <p>This filter leaves actual filtering to subclasses, which have to* implement the {@link javax.servlet.Filter#doFilter} method.* 这个filter,接口GenericFilterBean,将实际的过滤工作留给他的子类来完成,这* 就导致了他的子类不得不实现doFilter方法。* * <p>This generic filter base class has no dependency on the Spring* {@link org.springframework.context.ApplicationContext} concept.* Filters usually don't load their own context but rather access service* beans from the Spring root application context, accessible via the* filter's {@link #getServletContext() ServletContext} (see* {@link org.springframework.web.context.support.WebApplicationContextUtils}).* 接口GenericFilterBean不依赖于Spring的ApplicationContext。Filters通常不会直接读取他们* 的容器信息(ApplicationContext concept)而是通过访问spring容器(Spring root application context)* 中的service beans来获取,通常是通过调用filter里面的getServletContext() 方法来获取* * @author Juergen Hoeller* @since 06.12.2003* @see #addRequiredProperty* @see #initFilterBean* @see #doFilter*/
public abstract class GenericFilterBean implementsFilter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean {/** Logger available to subclasses */protected final Log logger = LogFactory.getLog(getClass());/*** Set of required properties (Strings) that must be supplied as* config parameters to this filter.*/private final Set<String> requiredProperties = new HashSet<String>();private FilterConfig filterConfig;private String beanName;private Environment environment = new StandardServletEnvironment();private ServletContext servletContext;/*** Stores the bean name as defined in the Spring bean factory.* <p>Only relevant in case of initialization as bean, to have a name as* fallback to the filter name usually provided by a FilterConfig instance.* 存储Spring bean工厂中定义的bean名称。* 只有在初始化为bean的情况下,才有一个名称作为回退到通常由FilterConfig实例提供的过滤器名称。* @see org.springframework.beans.factory.BeanNameAware* @see #getFilterName()*/@Overridepublic final void setBeanName(String beanName) {this.beanName = beanName;}/*** {@inheritDoc}* <p>Any environment set here overrides the {@link StandardServletEnvironment}* provided by default.* <p>This {@code Environment} object is used only for resolving placeholders in* resource paths passed into init-parameters for this filter. If no init-params are* used, this {@code Environment} can be essentially ignored.* 此处设置的任何环境都会覆盖默认提供的{@link StandardServletEnvironment}。* 此Environment对象仅用于解析传递给此过滤器的init参数的资源路径中的占位符。 如果没有使用init-params,* 则可以基本忽略该代码。* 也就说这个方法是解析web.xml中<init-param>的配置的。* @see #init(FilterConfig)*/@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}/*** Stores the ServletContext that the bean factory runs in.* 存储bean factory运行的ServletContext* <p>Only relevant in case of initialization as bean, to have a ServletContext* as fallback to the context usually provided by a FilterConfig instance.* @see org.springframework.web.context.ServletContextAware* @see #getServletContext()*/@Overridepublic final void setServletContext(ServletContext servletContext) {this.servletContext = servletContext;}/*** Calls the {@code initFilterBean()} method that might* contain custom initialization of a subclass.* 调用可能包含子类自定义初始化的initFilterBean方法* <p>Only relevant in case of initialization as bean, where the* standard {@code init(FilterConfig)} method won't be called.* 只有在初始化为bean的情况下才调用,标准初始化init方法将不会被调用。* @see #initFilterBean()* @see #init(javax.servlet.FilterConfig)*/@Overridepublic void afterPropertiesSet() throws ServletException {initFilterBean();}/*** Subclasses can invoke this method to specify that this property* (which must match a JavaBean property they expose) is mandatory,* and must be supplied as a config parameter. This should be called* from the constructor of a subclass.* 子类可以调用这个方法来指定这个属性是强制性的,并且必须提供配置参数。这个必须从子类的构造器调用。* <p>This method is only relevant in case of traditional initialization* driven by a FilterConfig instance.* 此方法仅与由FilterConfig实例驱动的传统初始化相关。* @param property name of the required property*/protected final void addRequiredProperty(String property) {this.requiredProperties.add(property);}/*** Standard way of initializing this filter.初始化该过滤器的标准方法* Map config parameters onto bean properties of this filter, and* invoke subclass initialization.* 映射配置参数到这个过滤器bean的属性里,并且调用子类的初始化方法。* @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*/@Overridepublic 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");}}/*** Initialize the BeanWrapper for this GenericFilterBean,* possibly with custom editors.* <p>This default implementation is empty.* @param bw the BeanWrapper to initialize* @throws BeansException if thrown by BeanWrapper methods* @see org.springframework.beans.BeanWrapper#registerCustomEditor*/protected void initBeanWrapper(BeanWrapper bw) throws BeansException {}/*** Make the FilterConfig of this filter available, if any.* Analogous to GenericServlet's {@code getServletConfig()}.* <p>Public to resemble the {@code getFilterConfig()} method* of the Servlet Filter version that shipped with WebLogic 6.1.* @return the FilterConfig instance, or {@code null} if none available* @see javax.servlet.GenericServlet#getServletConfig()*/public final FilterConfig getFilterConfig() {return this.filterConfig;}/*** Make the name of this filter available to subclasses.* Analogous to GenericServlet's {@code getServletName()}.* <p>Takes the FilterConfig's filter name by default.* If initialized as bean in a Spring application context,* it falls back to the bean name as defined in the bean factory.* @return the filter name, or {@code null} if none available* @see javax.servlet.GenericServlet#getServletName()* @see javax.servlet.FilterConfig#getFilterName()* @see #setBeanName*/protected final String getFilterName() {return (this.filterConfig != null ? this.filterConfig.getFilterName() : this.beanName);}/*** Make the ServletContext of this filter available to subclasses.* Analogous to GenericServlet's {@code getServletContext()}.* <p>Takes the FilterConfig's ServletContext by default.* If initialized as bean in a Spring application context,* it falls back to the ServletContext that the bean factory runs in.* @return the ServletContext instance, or {@code null} if none available* @see javax.servlet.GenericServlet#getServletContext()* @see javax.servlet.FilterConfig#getServletContext()* @see #setServletContext*/protected final ServletContext getServletContext() {return (this.filterConfig != null ? this.filterConfig.getServletContext() : this.servletContext);}/*** Subclasses may override this to perform custom initialization.* All bean properties of this filter will have been set before this* method is invoked.* <p>Note: This method will be called from standard filter initialization* as well as filter bean initialization in a Spring application context.* Filter name and ServletContext will be available in both cases.* <p>This default implementation is empty.* @throws ServletException if subclass initialization fails* @see #getFilterName()* @see #getServletContext()*/protected void initFilterBean() throws ServletException {}/*** Subclasses may override this to perform custom filter shutdown.* <p>Note: This method will be called from standard filter destruction* as well as filter bean destruction in a Spring application context.* <p>This default implementation is empty.*/@Overridepublic void destroy() {}/*** PropertyValues implementation created from FilterConfig init parameters.* 从FilterConfig 初始化参数创建的PropertyValues实现。*/@SuppressWarnings("serial")private static class FilterConfigPropertyValues extends MutablePropertyValues {/*** Create new FilterConfigPropertyValues.* @param config FilterConfig we'll use to take PropertyValues from* @param requiredProperties set of property names we need, where* we can't accept default values* @throws ServletException if any required properties are missing*/public FilterConfigPropertyValues(FilterConfig config, Set<String> requiredProperties)throws ServletException {Set<String> missingProps = (requiredProperties != null && !requiredProperties.isEmpty()) ?new HashSet<String>(requiredProperties) : null;Enumeration<?> en = config.getInitParameterNames();while (en.hasMoreElements()) {String property = (String) en.nextElement();Object value = config.getInitParameter(property);addPropertyValue(new PropertyValue(property, value));if (missingProps != null) {missingProps.remove(property);}}// Fail if we are still missing properties.if (missingProps != null && missingProps.size() > 0) {throw new ServletException("Initialization from FilterConfig for filter '" + config.getFilterName() +"' failed; the following required properties were missing: " +StringUtils.collectionToDelimitedString(missingProps, ", "));}}}}

SpringMVC——过滤器相关推荐

  1. 关键springmvc过滤器配置失败的解决办法。

    在学习springmvc的过程中遇到过滤器不起作用的问题,经过解决,提供两种方式解决. 1.对获取的参数封装之前进行转码. String monsterName = new String(monste ...

  2. springMVC 过滤器与拦截器的执行顺序问题。springboot一样参考

    最近项目要搞国际化,发现做国际化的时候是需要添加拦截器的,但是我们项目是通过filter过滤器做登录拦截,此时的报错信息总是国际化失败.折腾半天发现原因是国际化的拦截器没有用到导致.所以在此研究了下过 ...

  3. SpringMVC 过滤器Filter使用解析

    转自 https://www.cnblogs.com/mfc-itblog/p/6854698.html SpringMVC框架是一个成熟的优秀Java web开发框架,学习研究框架设计有助于我们更好 ...

  4. springmvc过滤器简单实例

    自定义过滤器 package com.ssm.student.filter;import java.io.IOException; import java.io.PrintWriter;import ...

  5. SpringMVC源码阅读:过滤器

    SpringMVC源码阅读:过滤器 目录 1.前言 2.源码分析 3.自定义过滤器 3.1 自定义过滤器继承OncePerRequestFilter 3.2 自定义过滤器实现Filter接口 4.过滤 ...

  6. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  7. 捷易拍与springMVC系统结合

    1. 捷易拍高拍仪在jsp页面的调用 使用ActiveX插件的方式处理解决此问题,捷易拍公司提供了支持IE8以上的32位浏览器的插件,安装插件后,我们可以使用Object标签,使用高拍仪 注意: 1. ...

  8. SpringMVC (注解、拦截器、json、Ajax)

    SpringMVC 1.回顾MVC 1.1 什么是mvc MVC是模型(Model).视图(View).控制器(Controller)的简写,是一种软件设计规范. 是将业务逻辑.数据.显示分离的方法来 ...

  9. SpringMVC3----@Controller注解、RestFul风格的讲解和应用、SpringMVC的接受请求参数、网页跳转方式和数据回显、乱码问题

    目录 7 Controller类的写法 7.1 继承Controller接口 7.2 一个简单通过@Controller注解实现的程序. 7.3 @RequestMapping 8 RestFul风格 ...

最新文章

  1. iOS 线程之GCD的高级使用方法
  2. android7.1默认背光亮度及编译调试
  3. 在 wxWidgets 中播放声音的示例
  4. html中擦窗效果,纯CSS写的小雨打在窗户上效果
  5. python提取视频中的音频 代码_如何使用python从视频文件中提取音频?
  6. 各种字符串Hash函数
  7. linux的文本,Linux文本处理
  8. python相关函数_Python 函数相关概念
  9. android蓝牙5.0扫描失败,bluetooth-lowenergy – BLE扫描的解决方案SCAN_FAILED_APPLICATION_REGISTRATION_FAILED?...
  10. 全链路压测原理篇(方案 概念 架构 实现)
  11. 【操作系统真象还原】Mac安装配置bochs
  12. 计算机考研作息时间表,2020考研的最佳作息时间表 如何安排学习时间
  13. 大时代势不可挡_会使您势不可挡的程序员的行为
  14. vs2021下载步骤
  15. sketch八款设计插件,画图效率翻倍
  16. 大数据工具和数据库区别和关联
  17. 计算机主机箱进行总结,工业级主机用机箱分类总结
  18. 信息隐藏 !!!!!!!!!!!!!
  19. 虚拟机 硬盘空间不足 磁盘最大大小调整的相对方法
  20. golang中匿名组合

热门文章

  1. Matlab实现平面几何图形的平移、旋转和缩放
  2. JAVA反编译工具精选
  3. WebGL开源框架列举概述
  4. Android基础-MMKV基本使用
  5. 全国计算机等级考试三级数据库技术(十四)
  6. java实现微信小程序客服功能开发
  7. C语言100题练习计划 27——字符串替换
  8. Excel/WPS 按条件合并多行
  9. Python cv2(Opencv) Canny边缘检测 和 傅里叶变换
  10. Python官网下载