1、过滤器 (Filter)
过滤器的配置比较简单,直接实现Filter
接口即可,也可以通过@WebFilter
注解实现对特定URL
拦截,看到Filter
接口中定义了三个方法。
-
init()
:该方法在容器启动初始化过滤器时被调用,它在Filter
的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。 -
doFilter()
:容器中的每一次请求都会调用该方法,FilterChain
用来调用下一个过滤器Filter
。 -
destroy()
: 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器Filter
的整个生命周期也只会被调用一次
@Component
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter 前置");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter 处理中");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
System.out.println("Filter 后置");
}
}
2、拦截器 (Interceptor)
拦截器它是链式调用,一个应用中可以同时存在多个拦截器Interceptor
, 一个请求也可以触发多个拦截器 ,而每个拦截器的调用会依据它的声明顺序依次执行。
首先编写一个简单的拦截器处理类,请求的拦截是通过HandlerInterceptor
来实现,看到HandlerInterceptor
接口中也定义了三个方法。
-
preHandle()
:这个方法将在请求处理之前进行调用。注意:如果该方法的返回值为false
,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。 -
postHandle()
:只有在preHandle()
方法返回值为true
时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。 有意思的是:postHandle()
方法被调用的顺序跟preHandle()
是相反的,先声明的拦截器preHandle()
方法先执行,而postHandle()
方法反而会后执行。 -
afterCompletion()
:只有在preHandle()
方法返回值为true
时才会执行。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。
/**
* WebMvcConfigurer配置类其实是Spring内部的一种配置方式,。基于java-based方式
* 采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制
* spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口
* 在Spring Boot 1.5版本都是靠重写WebMvcConfigurerAdapter的方法来添加自定义拦截器,消息转换器等。
* SpringBoot 2.0 后,该类被标记为@Deprecated(弃用)。官方推荐直接实现WebMvcConfigurer
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
/* 拦截器配置 */
@Override
public void addInterceptors(InterceptorRegistry registry) {
super.addInterceptors(registry);
// 添加自定义(实现HandlerInterceptor接口)的拦截器
registry.addInterceptor(new MyHandlerInterceptor())
// addPathPatterns("/**")对所有请求都拦截
.addPathPatterns("/**")
// 用于设置不需要拦截的过滤规则
.excludePathPatterns("/emp/toLogin","/emp/login","/js/**","/css/**","/images/**");
/**
* 了解什么是跨域?
* 所有支持 JavaScript 的浏览器都会使用同源策略。所谓同源是指:协议,域名,端口 全部相同。
* 浏览器从一个域名的网页去请求另一个域名的资源时,协议,域名,端口任意不同,都会出现跨域问题。
* 现实工作开发中经常会有跨域的情况。因为公司会有很多项目,也会有很多子域名,各个项目或者网站之间需要相互调用对方的资源,所以跨域请求是避免不了的
* 解决方法?
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
super.addCorsMappings(registry);
registry.addMapping("/cors/**")
.allowedHeaders("*")
.allowedMethods("POST","GET")
.allowedOrigins("*");
}
}
public class MyHandlerInterceptor implements HandlerInterceptor {
/**
* 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
* 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("==========================================1================================================");
return true;
}
/**
* 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("==========================================post1================================================");
}
/**
* 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("==========================================after1================================================");
}
}
我们不一样
过滤器 和 拦截器 均体现了AOP
的编程思想,都可以实现诸如日志记录、登录鉴权等功能,但二者的不同点也是比较多的,接下来一一说明。
1、实现原理不同
过滤器和拦截器 底层实现方式大不相同,过滤器
是基于函数回调的,拦截器
则是基于Java的反射机制(动态代理)实现的。
这里重点说下过滤器!
在我们自定义的过滤器中都会实现一个 doFilter()
方法,这个方法有一个FilterChain
参数,而实际上它是一个回调接口。ApplicationFilterChain
是它的实现类, 这个实现类内部也有一个 doFilter()
方法就是回调方法。
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
ApplicationFilterChain
里面能拿到我们自定义的xxxFilter
类,在其内部回调方法doFilter()
里调用各个自定义xxxFilter
过滤器,并执行 doFilter()
方法。
2、使用范围不同
我们看到过滤器 实现的是 javax.servlet.Filter
接口,而这个接口是在Servlet
规范中定义的,也就是说过滤器Filter
的使用要依赖于Tomcat
等容器,导致它只能在web
程序中使用。 而拦截器(Interceptor
) 它是一个Spring
组件,并由Spring
容器管理,并不依赖Tomcat
等容器,是可以单独使用的。不仅能应用在web
程序中,也可以用于Application
、Swing
等程序中。
3、触发时机不同
过滤器
和 拦截器
的触发时机也不同,我们看下边这张图。
过滤器Filter
是在请求进入容器后,但在进入servlet
之前进行预处理,请求结束是在servlet
处理完以后。
拦截器 Interceptor
是在请求进入servlet
后,在进入Controller
之前进行预处理的,Controller
中渲染了对应的视图之后请求结束。
执行顺序 :Filter 处理中
-> Interceptor 前置
-> 我是controller
-> Interceptor 处理中
-> Interceptor 处理后
4、应用场景不同
拦截器
- 登录验证,判断用户是否登录。
- 权限验证,判断用户是否有权限访问资源,如校验token
- 日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量。
- 处理cookie、本地化、国际化、主题等。
- 性能监控,监控请求处理时长等。
- 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现)
过滤器
1)过滤敏感词汇(防止sql注入)
2)设置字符编码
3)URL级别的权限访问控制
4)压缩响应信息