概述
我们先用一张图来了解一下filter、servlet、Interceptor之间的关系:
Springboot实现Filter
Springboot中有两种常用的实现Filter的方式:@WebFilter + @ServletComponentScan和JavaConfig 配置。
@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";
}
测试结果如下:
我们可以看出代码执行的流程,首先请求被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的区别
- 过滤器和拦截器触发时机不一样,过滤器是在请求进入容器后,但请求进入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
再来看一个图片:
过滤器可以拿到原始的http请求,但是拿不到你请求的控制器和请求控制器中的方法的信息;拦截器可以拿到请求的控制器和方法,却拿不到请求方法的参数;切片可以拿到方法的参数,但是却拿不到http请求和响应的对象。