Filter 介绍
Filter 过滤器主要用来过滤用户请求,它允许对用户请求进行前置处理和后置处理,比如实现 URL 级别的权限控制、过滤非法请求等。Filter 过滤器是面向切面编程——AOP 的具体实现。
另外,Filter 是依赖于 Servlet 容器,Filter
接口就在 Servlet 包下面,属于 Servlet 规范的一部分。所以,很多时候也称其为“增强版 Servlet”。
Filter 过滤原理
Filter
接口中有一个叫做 doFilter
的方法,这个方法实现了对用户请求的过滤。具体流程大体是这样的:
- 用户发送请求到 web 服务器,请求会先到过滤器;
- 过滤器会对请求进行一些处理,比如过滤请求的参数、判断是否允许用户访问该接口、修改返回给客户端的 response 的内容等等。
- 用户请求响应完毕。
- 进行一些自己想要的其他操作。
自定义 Filter
手动注册配置实现
自定义的 Filter 需要实现 javax.Servlet.Filter
接口,并重写接口中定义的 3 个方法。
public interface Filter {
// 初始化过滤器后执行的操作
public default void init(FilterConfig filterConfig) throws ServletException {}
// 对请求进行过滤
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
// 销毁过滤器后执行的操作,主要用于对某些资源的回收
public default void destroy() {}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
public class MyFilter implements Filter {
// 使用指定的类初始化日志对象,在日志输出的时候可以打印出日志信息所属的类
private static final Logger logger = LoggerFactory.getLogger(MyFilter.class);
@Override
public void init(FilterConfig filterConfig) {
logger.info("初始化过滤器:", filterConfig.getFilterName());
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
logger.info("过滤器开始对请求进行预处理:");
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestUri = request.getRequestURI();
System.out.println("请求的接口为:" + requestUri);
long startTime = System.currentTimeMillis();
// 通过 doFilter 方法实现过滤功能
filterChain.doFilter(servletRequest, servletResponse);
// 上面的 doFilter 方法执行结束后用户的请求已经返回
long endTime = System.currentTimeMillis();
System.out.println("该用户的请求已经处理完毕,请求花费的时间为:" + (endTime - startTime));
}
@Override
public void destroy() {
logger.info("销毁过滤器");
}
}
在配置中注册自定义的过滤器。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.Arrays;
@Configuration
public class FilterConfig {
@Autowired
MyFilter myFilter;
@Bean
public FilterRegistrationBean<MyFilter> thirdFilter() {
FilterRegistrationBean<MyFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(myFilter);
filterRegistrationBean.setUrlPatterns(new ArrayList<>(Arrays.asList("/api/*")));
return filterRegistrationBean;
}
}
通过注解实现
在自定义过滤器的类上加上 @WebFilter
然后在这个注解中通过它提供好的一些参数进行配置。
@WebFilter(filterName = "MyFilterWithAnnotation", urlPatterns = "/api/*")
public class MyFilterWithAnnotation implements Filter {
......
}
另外,为了能让 Spring 找到它,需要在启动类上加上 @ServletComponentScan
注解。
过滤器执行顺序
假如现在又加入了一个过滤器:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
public class MyFilter2 implements Filter {
// 使用指定的类初始化日志对象,在日志输出的时候可以打印出日志信息所属的类
private static final Logger logger = LoggerFactory.getLogger(MyFilter.class);
@Override
public void init(FilterConfig filterConfig) {
logger.info("2初始化过滤器:", filterConfig.getFilterName());
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
logger.info("2过滤器开始对请求进行预处理:");
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestUri = request.getRequestURI();
System.out.println("2请求的接口为:" + requestUri);
long startTime = System.currentTimeMillis();
// 通过 doFilter 方法实现过滤功能
filterChain.doFilter(servletRequest, servletResponse);
// 上面的 doFilter 方法执行结束后用户的请求已经返回
long endTime = System.currentTimeMillis();
System.out.println("2该用户的请求已经处理完毕,请求花费的时间为:" + (endTime - startTime));
}
@Override
public void destroy() {
logger.info("2销毁过滤器");
}
}
在配置中注册自定义的过滤器,通过 FilterRegistrationBean
的 setOrder
方法可以决定 Filter 的执行顺序。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.Arrays;
@Configuration
public class FilterConfig {
@Autowired
MyFilter myFilter;
@Autowired
MyFilter2 myFilter2;
@Bean
public FilterRegistrationBean<MyFilter> setUpMyFilter() {
FilterRegistrationBean<MyFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setOrder(2);
filterRegistrationBean.setFilter(myFilter);
filterRegistrationBean.setUrlPatterns(new ArrayList<>(Arrays.asList("/api/*")));
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean<MyFilter2> setUpMyFilter2() {
FilterRegistrationBean<MyFilter2> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setOrder(1);
filterRegistrationBean.setFilter(myFilter2);
filterRegistrationBean.setUrlPatterns(new ArrayList<>(Arrays.asList("/api/*")));
return filterRegistrationBean;
}
}
自定义 Controller 验证过滤器。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class FilterController {
@GetMapping("/hello")
public String getHello() throws InterruptedException {
Thread.sleep(1000);
return "Hello";
}
}
测试结果:
2019-10-22 22:32:15.569 INFO 1771 --- [ main] g.j.springbootfilter.filter.MyFilter2 : 2初始化过滤器
2019-10-22 22:32:15.569 INFO 1771 --- [ main] g.j.springbootfilter.filter.MyFilter : 初始化过滤器
2019-10-22 22:32:55.199 INFO 1771 --- [nio-8080-exec-1] g.j.springbootfilter.filter.MyFilter2 : 2过滤器开始对请求进行预处理:
请求的接口为2:/api/hello
2019-10-22 22:32:55.199 INFO 1771 --- [nio-8080-exec-1] g.j.springbootfilter.filter.MyFilter : 过滤器开始对请求进行预处理:
请求的接口为:/api/hello
该用户的请求已经处理完毕,请求花费的时间为:1026
2该用户的请求已经处理完毕,请求花费的时间为:1027