springmvc的GenericFilterBean和DelegatingFilterProxy和OncePerRequestFilter过滤器

DelegatingFilterProxy

这是一个filter的代理类,什么意思呢? 我们知道:当我们实现Filter接口并配置到web.xml中这个filter就会生效,但是在这个filter中我们是无法使用自动注入功能的,因为这个filter并没有被spring管理,那么如果我们想实现一个自定义的filter并且可以进行自动注入spring管理的bean那该多好,spring给我们提供了DelegatingFilterProxy类,注意这是一个过滤器,实现了Filter接口,怎么使用呢?

  1. 我们需要在web.xml文件中配置delegatingFilterProxy过滤器
<filter>
	<filter-name>myDelegatingFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	<!--这个参数的意思是:是否执行被代理的filter的声明周期函数(init,destory)-->
	<init-param>
		<param-name>targetFilterLifecycle</param-name>
		<param-value>true</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>myDelegatingFilter</filter-name>
	<servlet-name>springmvc</servlet-name>
</filter-mapping>

注意上面的filter-name需要设置为我们接下来自定义filter的bean的名字,或者也可以配置targetBeanName init-param参数指定被代理filter的名字
2. 自定义我们的filter,也就是需要被代理的filter

package com.fuyi.learn.springmvc.filter;


import com.fuyi.learn.springmvc.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.*;
import java.io.IOException;
@Component
public class MyDelegatingFilter extends GenericFilterBean {
    @Autowired
    IUserService userService;
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("MyDelegatingFilter:");
        userService.sayHello();
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

这个filter不需要配置到web.xml

实现原理分析

在DelegatingFilterProxy中,

  1. 当第一次执行到该代理filter的doFilter方法时,会先调用findWebApplicationContext()方法查找WebApplicationContext, 该方法内部是调用WebApplicationContextUtils.findWebApplicationContext(getServletContext())查找的
    doFilter代码:
@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// Lazily initialize the delegate if necessary.
		Filter delegateToUse = this.delegate;
		if (delegateToUse == null) {
			synchronized (this.delegateMonitor) {
				delegateToUse = this.delegate;
				if (delegateToUse == null) {
					WebApplicationContext wac = findWebApplicationContext();
					if (wac == null) {
						throw new IllegalStateException("No WebApplicationContext found: " +
								"no ContextLoaderListener or DispatcherServlet registered?");
					}
					delegateToUse = initDelegate(wac);
				}
				this.delegate = delegateToUse;
			}
		}

		// Let the delegate perform the actual doFilter operation.
		invokeDelegate(delegateToUse, request, response, filterChain);
	}
  1. 查找到WebApplicationContext之后,调用initDelegate()方法,该方法内部时从WebApplicationContext容器中查找我们自定义的filter(我们已经交给spring管理),拿到我们自定义的filter之后,赋值给该代理filter的delegateToUse属性中,以便下次执行不需要再从spring容器中获取,
  2. 从spring容器中拿到我们定义的filter bean之后就直接调用invokeDelegate()方法,内部就调用了我们filter的doFilter方法,
    invokeDelegate方法:
protected void invokeDelegate(
			Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		delegate.doFilter(request, response, filterChain);
	}

这样就实现了我们自定义filter中实现spring的自动注入功能,总结就是
通过spring提供的代理filter来代理 我们自己的filter,然后在代理filter(DelegatingFilterProxy)中执行我们自定义的filter(已经交给spring容器管理)

GenericFilterBean

springmvc提供的过滤器的简单基本实现,它将其过滤器的配置参数(web.xml中过滤器的init-param指定的参数)作为bean属性。
实现原理分析:

  1. GenericFilterBean实现了Filter接口的init方法,在init方法中读取我们配置的init-param参数并设置到我们自定义bean的setter方法
  2. 之后会调用initFilterBean(),这个方法可以自类去实现,写一些自己的想做的事情
    init()方法:
@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");
		}
	}

OncePerRequestFilter

该过滤器能够使request在单次请求中只过滤一次,什么意思呢?我们知道过滤器可以配置拦截多个请求路径的,假如我们的一次请求经过了该过滤器然后到达servlet,但是servlet又进行了转发,这时转发的路径又要被该过滤器过滤,那么我们的过滤器就会在用户的一次请求中执行两次,有时这并不是我们想要的。这时我们就可以继承OncePerRequestFilter过滤器并实现doFilterInternal方法,在这个方法里写我们自己的连接器的处理逻辑。
我们以一个请求为例,来进行原理分析:

  1. 首先我们的请求第一次进入OncePerRequestFilter的doFilter方法,在doFilter方法中会调用request.setAttribute()方法向请求中设置一个标识属性,下面代码
    :request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
    标识我们的请求已经执行过一次,然后执行我们的doFilterInternal方法,第一次请求就继续往后执行
  2. 当请求被后续的servlet转发过来,那么又会经过我们的filter,这时doFilter方法调用下面代码判断请求中有没有设置alreadyFilteredAttributeName标识,其实每次请求都会调用下面的代码判断,只是第一次请求的时候request中没有该字段罢了,相关代码:
    String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
   	boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;

这时从request中获取到了该标识,那就表明我们这次request已经执行了一次了,就会直接调用filterChain.doFilter(request, response);方法继续往后执行,不会调用我们的doFilterInternal方法

其实这里逻辑很简单看下源码就知道了,附上doFilter方法

@Override
	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;

		String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
		boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;

		if (skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {

			// Proceed without invoking this filter...
			filterChain.doFilter(request, response);
		}
		else if (hasAlreadyFilteredAttribute) {

			if (DispatcherType.ERROR.equals(request.getDispatcherType())) {
				doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain);
				return;
			}

			// 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);
			}
		}
	}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring MVC 中的过滤器和拦截器都可以在请求处理之前或之后进行一些特定的操作,但它们的实现方式和作用范围不同。 过滤器是基于 Servlet 规范实现的,在 Servlet 容器中运行,可以拦截所有的请求(包括静态资源请求),并对请求进行处理。过滤器通常用于请求的预处理和后处理,比如编码转换、日志记录、权限校验、资源缓存等。 拦截器是基于 Spring MVC 框架实现的,在 DispatcherServlet 中运行,只能拦截 Spring MVC 的请求(即只能拦截到经过 DispatcherServlet 的请求),并对请求进行处理。拦截器通常用于请求的预处理和后处理,比如权限校验、日志记录、性能监控等。 下面是一个简单的 Spring MVC 过滤器和拦截器的示例: ```java // 过滤器实现类 public class EncodingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化方法 } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 处理请求的方法 request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); chain.doFilter(request, response); } @Override public void destroy() { // 销毁方法 } } // 拦截器实现类 public class LogInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 处理请求前的方法 System.out.println("请求 URL:" + request.getRequestURI()); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 处理请求后的方法 } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 请求处理完成后的方法 } } ``` 在 Spring MVC 中配置过滤器和拦截器: ```xml <!-- 配置过滤器 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>com.example.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置拦截器 --> <mvc:interceptors> <bean class="com.example.LogInterceptor" /> </mvc:interceptors> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值