JAVA过滤器以及拦截器的使用介绍
一 过滤器
1.1 过滤器简单介绍
JAVA过滤器能够对目标资源的请求和响应进行截取,对目标资源的请求和响应进行预处理,然后交给下一个过滤器或servlet处理。
通过过滤器进行预处理操作的主要逻辑都是在doFilter()
方法中进行实现,这也是需要重点关注的方法,至于init()
和destroy()
分别是初始化以及销毁方法。
1.2 自定义过滤器
那么,应该如何自定义过滤器呢?
- 实现
Filter
接口; - 通过
@WebFilter
注解完成过滤路径设置
其实就是这么简单,如下:
/**
* 通过实现 Filter 接口来设置一个过滤器
* 通过 @Component 注解使之成为spring的一个Bean
* 通过 @WebFilter(urlPatterns = {"/*"}) 设置会进入该过滤器的请求路径
*/
@Component
@WebFilter(urlPatterns = {"/common/test", "/common/test01"}) //设置会被过滤处理的路请求径
@Order(0) //如果存在多个过滤器 设置过滤器的优先级,值越小,优先级越高,doFilter() 越早执行
public class FirstFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("我是第一个过滤器 init~");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("我是第一个过滤器 doFilter~");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("我是第一个过滤器 destroy~");
}
}
这里我还设置了第二个过滤器,代码和第一个过滤器几乎一样,除了优先级以及简单的日志输出,所以具体代码我就不重复贴出来了。
1.3 过滤器测试
这个待会儿和下面大的拦截器一起进行测试,便于观察(我就是懒图个省事)。
二 拦截器
2.1 拦截器简单介绍
JAVA里的拦截器是动态拦截Action调用的对象。它提供了一种机制,使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取action中可重用部分的方式。
拦截器大部分是基于动态代理实现的,拦截是AOP的一种实现策略。
2.2 自定义拦截器
那么,如何自定义一个拦截器呢?
- 实现
HandlerInterceptor
接口; - 实现
WebMvcConfigurer
配置接口,进行自定义过滤i的注册;
具体代码实现如下(基于SpringBoot2.x):
首先自然是实现HandlerInterceptor
接口,然后实现相应的方法,具体作用以及介绍注释有说明:
/**
* 通过实现 HandlerInterceptor 接口实现一个拦截器
* 要想使得拦截器在SpringBoot生效:
* 第一需要在实现类上添加注解 @Component
* 第二需要在配置类注册拦截器
*/
@Component
public class FirstInterceptor implements HandlerInterceptor {
/**
* 该方法在处理拦截之前执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o)
throws Exception {
System.out.println("我是第一个拦截器 preHandle");
//TODO 根据实际业务需求 符合要求的才返回true 不符合的返回false
//注意: 如果请求参数是json格式,那么不能通过 request.getParameter()的方式获取
String first = request.getParameter("first");
if (Objects.equals(first, "111")) {
return true;
}
response.setContentType("application/json,charset=utf-8");
response.getOutputStream().write("不符合要求,不放行".getBytes(StandardCharsets.UTF_8));
return false;
}
/**
* 该方法仅在方法没有抛出异常且没有被拦截且执行成功的时候,才会进入执行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
System.out.println("我是第一个拦截器 postHandle");
}
/**
* 该方法无论方法是否抛出异常,只要没被拦截,最后都会进入执行
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
System.out.println("我是第一个拦截器 afterCompletion");
}
}
既然有了第一个拦截器,很显然,还会有第二个拦截器。为什么要定义多个拦截器呢?其实我的目的是为了展示多个拦截器
之间的执行顺序
。
OK,接下来是第二个拦截器,具体代码和第一个拦截器几乎是一样的,区别就在于没有做任何拦截处理,直接放行。所以我就不重复贴出来了。
好了,拦截器
定义好了还不够,还需要将其进行注册
,接下来便是注册部分:
/**
* 该配置类用于注册拦截器
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private FirstInterceptor firstInterceptor;
@Autowired
private SecondInterceptor secondInterceptor;
/**
* 拦截器注册 可注册多个 执行顺序和注册顺序一样
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//拦截器的执行顺序取决与注册顺序
registry.addInterceptor(firstInterceptor)
//设置会被该拦截器拦截的请求路径 /** 表示所有
.addPathPatterns("/**");
registry.addInterceptor(secondInterceptor)
.addPathPatterns("/**")
//设置该拦截器不会拦截的请求路径
.excludePathPatterns("/common/test01");
}
}
2.3 拦截器+过滤器测试
具体控制器层测代码如下:
@RestController
public class TestController {
@GetMapping("/common/test")
public void test() {
System.out.println("我是test");
}
@GetMapping("/common/test01")
public void test01() {
System.out.println("我是test01");
}
}
OK,让我们请求 /common/test
接口,看看会有什么效果
执行顺简单总结一下:
- 可以看到,因为设置了第一个过滤器的优先级高于第二个过滤器,所以第一个过滤器优先执行。
- 过滤器是优先于拦截器执行的。
- 拦截器的执行顺序取决于注册顺序,也就是代码里的注册顺序。
- 第一个拦截器并不是所有方法都是优先执行的,仅有
preHandle()
方法是优先执行的。
三 二者对比
- 过滤器是基于函数回调的,而拦截器则是基于Java反射的。
- 过滤器依赖于servlet容器,拦截器不依赖于servlet容器。
- 过滤器几乎对所有的请求起作用,l拦截器只能对action请求起作用。
- 拦截器可以访问Action的上下文,值栈里的对象,而过滤器不能。
- 在Action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
一般拦截器用来做一些接口签名或者参数校验之类的,当然也可以做日志处理。功能比过滤器更加强大,也更加灵活。