Filter过滤器工作原理

Filter过滤器工作原理

Filter简介

Filter也称之为过滤器,它是Servlet技术中最激动人心的技术之一,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp,
Servlet, 静态图片文件或静态html文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等
一些高级功能。
  Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter
技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截,Filter接口源代码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.servlet;

import java.io.IOException;

public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {
    }

    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    default void destroy() {
    }
}

Filter工作原理

Filter接口中有一个doFilter方法,当我们编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,
都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
​ 调用目标资源之前,让一段代码执行。
​ 是否调用目标资源(即是否让用户访问web资源)。
​ 调用目标资源之后,让一段代码执行。
  web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个
doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,
否则web资源不会被访问。

Spring中的filter过滤器

在这里插入图片描述

Spring的web包中中有很多过滤器,这些过滤器无一例外的继承了Filter接口,实现的方式大致可以分为以下几类:

  • 直接实现Filter,这一类过滤器只有CompositeFilter;
  • 继承抽象类GenericFilterBean,该类实现了javax.servlet.Filter,这一类的过滤器只有一个,即DelegatingFilterProxy;
  • 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;
  • 继承抽象类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
	 */
	@Override
	public final void init(FilterConfig filterConfig) throws ServletException {
		Assert.notNull(filterConfig, "FilterConfig must not be null");

		this.filterConfig = filterConfig;

		// Set bean properties from init parameters.
		PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
				Environment env = this.environment;
				if (env == null) {
					env = new StandardServletEnvironment();
				}
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, env));
				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 for use");
		}
	}

该方法来自于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的某部分功能,当然,具体如何执行由它们的子类规定,若你需要实现自己的过滤器,也可以根据上文所述继承你所需要的类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值