概述

我们先用一张图来了解一下filter、servlet、Interceptor之间的关系:

spring boot filter 获取参数 springboot的filter_ide

Springboot实现Filter

Springboot中有两种常用的实现Filter的方式:@WebFilter + @ServletComponentScanJavaConfig 配置

@WebFilter + @ServletComponentScan方式实现Filter

第一步:在Springboot的主启动类上加上@ServletComponentScan注解

@SpringBootApplication
@ServletComponentScan
public class FlowPlatformWebApplication {
    public static void main(String[] args) {
        SpringApplication.run(FlowPlatformWebApplication.class, args);
    }
}

第二步:定义自己Filter
通过实现Filter接口来自定义Filter,Filter接口中的三个方法:init()、doFilter()、destroy()。

  • init():项目启动初始化的时候会被加载。
  • doFilter():过滤请求,预处理。
  • destroy():项目停止前,会执行该方法。

自定义第一个filter:

@Order(1)
@WebFilter(filterName="firstFilter", urlPatterns="/*")
public class FirstFilter implements Filter {
 
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		
	}
 
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("first filter 1");
		chain.doFilter(request, response);
		System.out.println("first filter 2");
	}
 
	@Override
	public void destroy() {
	}

@WebFilter(filterName=“firstFilter”, urlPatterns=“/*”)中的filterName属性用来定义过滤器的名字,urlPatterns属性用来定义需要过滤的请求url,此例中表示所有的请求都会经过吃过滤器。
@Order(1)用来定义过滤器的执行顺序,数值越小的优先执行。

为了测试filter的执行顺序,我们再定义一个filter:

@Order(2)
@WebFilter(filterName="secondFilter", urlPatterns="/*")
public class SecondFilter implements Filter {
 
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
 
	}
 
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("second filter 1");
		System.out.println("before:" + response);
		chain.doFilter(request, response);
		System.out.println("after:" + response);
		System.out.println("second filter 2");
		
	}
 
	@Override
	public void destroy() {
		
	}
}

然后定义一个Controller:

@RestController
public class TestController {
	
	@GetMapping("/test1")
	public String test1() {
		System.out.println("method in controller");
		return "test1";
	}

测试结果如下:

spring boot filter 获取参数 springboot的filter_ide_02


我们可以看出代码执行的流程,首先请求被firstfilter截获,打印出first filter 1,然后去执行chain.doFilter(request, response),这句话代表着请求会转发给过滤器链上下一个对象,也就是secondfilter,所以打印出secondfilter里的second filter 1,接下来再执行secondfilter里的chain.dofilter()方法,请求再转发给下一个对象,由于没有其他的filter了,所以会转发给controller,打印出了controller类中的method in controller,接下来再去内存栈里调用secondfilter的print(“second filter 2”),然后再去内存栈里调用firstfilter的print(“first filter 1”)。如果在自己实现的Filter类的doFilter方法里不加chain.doFilter(req, rep)是万万不行的,那样会导致请求到了这个filter里就不再往下走了,永远进不了controller中。

这里需要注意一点,@WebFilter这个注解是Servlet3.0的规范,并不是Spring boot提供的。

JavaConfig 配置方式实现Filter

@Configuration
public class FilterConfiguration {
    @Bean
    public Filter testFilter() {
        return new TestFilter();
    }

    @Bean
    public FilterRegistrationBean registrationTestFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new DelegatingFilterProxy("testFilter"));
        registration.setName("testFilter");
        registration.addUrlPatterns("/v1/*");
        registration.addUrlPatterns("/v2/*");
        registration.setOrder(1);
        return registration;
    }
}

通过 JavaConfig 显式配置 Filter ,功能强大,配置灵活。只需要把每个自定义的 Filter 声明成 Bean 交给 Spring 管理即可,还可以设置匹配的URL 、指定 Filter 的先后顺序。

Filter使用场景

  • 过滤敏感词汇(防止sql注入)
  • 设置字符编码
  • URL级别的权限访问控制
  • 压缩响应信息

Springboot实现Interceptor

实现拦截器可以通过继承 HandlerInterceptorAdapter类,也可以通过实现HandlerInterceptor这个接口,区别在于HandlerInterceptorAdapter实现了AsyncHandlerInterceptor接口,而AsyncHandlerInterceptor继承的是HandlerInterceptor接口,在AsyncHandlerInterceptor接口中多了一个afterConcurrentHandlingStarted方法,但是此方法用的并不多,所以选用哪种方式实现拦截器都可以。事实上,不仅Springboot项目这样处理,在Spring MVC项目中也是同样的实现方式。举例如下:

第一步:自定义拦截器:

public class LogCostInterceptor implements HandlerInterceptor { 
long start = System.currentTimeMillis();

@Override 
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        start = System.currentTimeMillis(); return true;
    }
 
@Override 
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor cost="+(System.currentTimeMillis()-start));
    }
 
@Override 
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    }
}

这里有三个方法:

  • preHandle:此方法是请求执行前执
  • postHandler:此方法是请求结束执行的,且需要preHandle方法返回true的时候才会执行
  • afterCompletion:此方法是视图渲染完成后才执行,且需要preHandle方法返回true的时候才会执行

第二步

@Configuration 
public class InterceptorConfig extends WebMvcConfigurerAdapter {
    @Override 
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogCostInterceptor()).addPathPatterns("/**"); super.addInterceptors(registry);
    }
}

拦截器的使用场景

登录验证,判断用户是否登录。
权限验证,判断用户是否有权限访问资源,如校验token
日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量。
处理cookie、本地化、国际化、主题等。
性能监控,监控请求处理时长等。
通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现)

Springboot实现servlet

Servlet是一个Java编写的程序,此程序是基于http协议的,在服务器端(如Tomcat)运行的,是按照servlet规范编写的一个Java类。客户端发送请求至服务器端,服务器端将请求发送至servlet,servlet生成响应内容并将其传给服务器。
Servlet 的作用:处理客户端的请求并将其结果发送到客户端。

自定义一个Servlet的步骤
第一步:在Springboot的主启动类上加上@ServletComponentScan注解

@SpringBootApplication
@ServletComponentScan
public class FlowPlatformWebApplication {
    public static void main(String[] args) {
        SpringApplication.run(FlowPlatformWebApplication.class, args);
    }
}

第二步:在类上添加 @WebServlet 注解。

@WebServlet(name = "TestServlet", urlPatterns = {"/v1/*", "/v2/*"})
public class TestServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.service(req, resp);
    }
}

这样就完成了一个自定义的servlet。
HttpServlet 中有很多方法,常用的还是重写 service(HttpServletRequest req, HttpServletResponse resp) 方法,进行请求处理返回。

Filter和Interceptor的区别

spring boot filter 获取参数 springboot的filter_后端_03

  • 过滤器和拦截器触发时机不一样,过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的,请求结束返回也是在servlet处理完后,返回给前端之前,在进行后置处理;拦截器是在进入servlet之后,出Servlet之前,在控制器方法前后进行操作。
  • 过滤器是JavaEE标准,只需依赖servlet api ,不需要依赖spring,所以过滤器无法获取IOC容器中的各个bean;而拦截器是spring提供并管理的,spring的功能可以被拦截器使用,因此拦截器可以获取IOC容器中的各个bean,例如在拦截器里注入一个service,可以调用业务逻辑,而这些在过滤器中都是做不到的。
  • Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。
  • Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理(反射)的方式来执行。
  • Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。

拦截器是在DispatcherServlet这个servlet中执行的,因此所有的请求最先进入Filter,最后离开Filter。其顺序如下:
Filter->Interceptor.preHandle->Handler->Interceptor.postHandle->Interceptor.afterCompletion->Filter

再来看一个图片:

spring boot filter 获取参数 springboot的filter_java_04


过滤器可以拿到原始的http请求,但是拿不到你请求的控制器和请求控制器中的方法的信息;拦截器可以拿到请求的控制器和方法,却拿不到请求方法的参数;切片可以拿到方法的参数,但是却拿不到http请求和响应的对象。