优质博文:IT-BLOG-CN
拦截器Interceptor
和过滤器Filter
都是基于AOP(Aspect Oriented Programming,面向切面编程)
思想实现的,用来解决项目中某一类问题的两种“工具”,两者在使用上有时候可能会分不清,希望能够通过本章解决这个痛点。
一、拦截器
拦截器Interceptor
是SpringMVC
中实现的一种基于Java
反射(动态代理)机制的方法增强工具,是面向切面AOP
编程中应用的一种统一处理方案。拦截器的实现是继承HandlerInterceptor
接口,并实现接口的preHandle
、postHandle
和afterCompletion
方法,对目标方法进行增强。可以访问action
上下文、堆栈里的对象,可以多次被调用。
【1】preHandle
:请求方法前置拦截,该方法会在Controller
处理之前进行调用,Spring
中可以有多个Interceptor
,这些拦截器会按照设定的Order
顺序调用,当有一个拦截器在preHandle
中返回false
的时候,请求就会终止。
【2】postHandle
:preHandle
返回结果为true
时,在Controller
方法执行之后,视图渲染之前被调用。
【3】afterCompletion
:在preHandle
返回ture
,并且整个请求结束之后,也就是在DispatcherServlet
渲染了对应的视图之后执行。此方法主要用来进行资源清理。
@Component
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取用户信息
EmpsInformationEntity ee = CommonTool.getEmpsInfo();
if (ee == null || StringUtils.isEmpty(ee.getEid())) {
// 跳转登录
redirectToLoginPage(request, response);
return false;
}
// 模块权限
PermissionConfig permission = getPermissionConfig(handler);
boolean hasPermission = checkPermission(request, response, permission);
if (!hasPermission) {
if (isAjax(request, permission)) {
response.getWriter().write("permission error");
} else {
List<String> codes = getModuleCode(permission);
if (CollectionUtils.isNotEmpty(codes)) {
response.sendRedirect(String.format(screenPopupConfig.getIamErrorPage(), codes.get(0), ee.getEid()));
return false;
}
response.sendRedirect(request.getContextPath() + "/noPermisson");
}
return false;
}
request.setAttribute("Eid", ee.getEid());
request.setAttribute("Ename", ee.getEmpName());
/**
* 多语言环境
*/
if (request.getRequestURI().indexOf("/lastOrderId") == -1) {
// 取值
CultureHelper.initPreRequest(request, response, ee.getDept());
}
accessMetric(ee, request);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
编写完拦截器之后,通过一个配置类设置拦截器,并且可以通过addPathPatterns
和excludePathPatterns
执行哪些请求需要被拦截,哪些不需要被拦截。
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired
private UserInterceptor userInterceptor;
// 或者通过@Bean注入
/** @Bean
* public UserInterceptor getUserInterceptor() {
* return new UserInterceptor();
**/ }
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/error");
}
}
应用场景: 主要是用在插件上,扩展件上比如hibernate spring struts2
等有点类似面向切片的技术,在用之前先要在配置文件即xml
文件里声明一段的那个东西。
【1】日志记录:记录请求操作日志(用户ip,访问时间等)
【2】权限检查:判断用户是否有权限访问资源,如校验token
日志记录
【3】性能监控:记录请求->响应时间,preHandle
:记录开始时间,afterCompletion
:记录结束时间,开始时间相减去结束时间
【4】登录验证:判断用户是否登录
【5】sign
校验,封禁校验等
【6】处理cookie
,主题,国际化,本地化等
【7】filter
可以实现的功能intercepter
基本上都能实现
二、过滤器
过滤器Filter
基于Servlet
实现,过滤器的主要应用场景是对字符编码、跨域等问题进行过滤。Servlet
的工作原理是拦截配置好的客户端请求,然后对Request
和Response
进行处理。Filter
过滤器随着web
应用的启动而启动,只初始化一次。Filter
的使用比较简单,继承Filter
接口,实现对应的init
、doFilter
以及destroy
方法即可。
【1】init
:在容器启动时调用初始化方法,只会初始化一次。
【2】doFilter
:每次请求都会调用doFilter
方法,通过FilterChain
调用后续的方法。
【3】destroy
:当容器销毁时,执行destory
方法,只会被调用一次。
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化拦截器");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//做一些处理
chain.doFilter(request,response);
}
@Override
public void destroy() {
System.out.println("销毁拦截器");
}
}
Filter
配置类
@Slf4j
@Configuration
public class FilterConfiguration {//不拦截路径
private static List<String> exclusionUrlList=new ArrayList<>();
//拦截路径
private static List<String> inclusionUrlList=new ArrayList<>();
static {
exclusionUrlList.add("/favicon.ico");
exclusionUrlList.add("/**/*.css");
exclusionUrlList.add("/**/*.js");
exclusionUrlList.add("/ok");
inclusionUrlList.add("/api/**");
}
@Bean
public FilterRegistrationBean filterRegistration() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(new MyFilter());
registration.addInitParameter(FILTER_INIT_PARAM_EXCLUSION_URLS,String.join(",", exclusionUrlList));
registration.addInitParameter(AJAX_URL_PATTERNS,String.join(",", inclusionUrlList));
registration.addUrlPatterns("/*");
registration.setName("testFilter");
return registration;
}
}
过滤器Filter
的三种配置方式:
【1】通过@WebFilter
注解配置
1.初始化Filter
@WebFilter(urlPatterns = "/test001")
@Order(1) //order值越小,过滤器越靠前,此处配置无效
public class MyFilter implements Filter {
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
System.out.println("##############TestFilter init##############");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//在DispatcherServlet之前执行
System.out.println("##############doFilter before##############");
filterChain.doFilter(servletRequest, servletResponse);
// 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
System.out.println("##############doFilter after##############");
}
@Override
public void destroy() {
System.out.println("##############TestFilter destroy##############");
}
}
//2.在启动类添加 @ServletComponentScan
@SpringBootApplication
@ServletComponentScan
public class TestbootApplication {
public static void main(String[] args) {
SpringApplication.run(TestbootApplication.class, args);
}
}
【2】通过@Bean
来配置:初始化Filter
@Component
public class MyFilter implements Filter {
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
System.out.println("##############Filter3 init##############");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//在DispatcherServlet之前执行
System.out.println("##############doFilter3 before##############");
filterChain.doFilter(servletRequest, servletResponse);
// 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
System.out.println("##############doFilter3 after##############");
}
@Override
public void destroy() {
System.out.println("##############Filter3 destroy##############");
}
}
注册到config
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean testFilter3RegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean(new MyFilter());
registration.addUrlPatterns("/hello");
registration.setOrder(1); // 值越小越靠前,此处配置有效
return registration;
}
@Bean
public FilterRegistrationBean testFilter4RegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean(new MyFilter1());
registration.addUrlPatterns("/hello");
registration.setOrder(2);
return registration;
}
}
【3】Spring MVC
在web.xml
进行配置:1、初始化Filter
;2、web.xml
文件中配置Filter
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
应用场景: 传入的request
,response
提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet
或者struts
的action
进行业务逻辑处理。例如在过滤器中修改字符编码CharacterEncodingFilter
、在过滤器中修改HttpServletRequest
的一些参数XSSFilter
(自定义过滤器),如:过滤低俗文字、危险字符、敏感词过滤、响应信息压缩、控制权限、控制转向、做一些业务逻辑判断等
三、拦截器与过滤器的区别
拦截器与过滤器执行时机不同: 请求进入Servlet
之前,过滤器的doFilter
方法进行过滤,进入Servlet
容器之后,执行Controller
方法之前,拦截器的preHandle
方法进行拦截,执行Controller
之后,视图渲染之前postHandle
方法进行拦截,请求结束之后,拦截器的postHandle
方法执行。
拦截器基于
Java
发射机制实现,过滤器基于函数回调方式实现。在实际应用中拦截器主要用于权限控制、日志打印、参数校验,过滤器主要用于跨域问题解决、编码转换。
区别 | 过滤器 | 拦截器 | 备注 |
---|---|---|---|
配置位置 | 配置在web.xml 中 | 配置在springmvc.xml 中 | |
定义位置 | Filter 定义在java.servlet 包下 | 接口HandlerInterceptor 定义在org.springframework.web.servlet 包下 | |
使用范围 | Filter 是Servlet 规范规定的 | 而拦截器既可以用于Web 程序,也可以用于Application 、Swing 程序中。 | |
遵循规范 | Filter 是遵循Servlet 规范 | 而拦截器是在Spring 容器内的,是Spring 框架支持的。 | |
实现方式 | Filter 基于函数回掉 | Interceptor 基于java 反射 | |
作用 | Filter 在只在Servlet 前后起作用,Filter 通常不考虑servlet 的实现 | 拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。允许用户介入hook into 请求的生命周期 | 在Spring 构架的程序中,要优先使用拦截器。几乎所有Filter 能够做的事情,interceptor 都能够轻松的实现 |
调用方 | Filter 是被Server(like Tomcat) 调用 | Interceptor 是被Spring 调用 | 因此Filter 总是优先于Interceptor 执行 |
与Spring 关系 | Filter 不能够使用Spring 容器资源 | Interceptor 是被Spring 调用 | Spring 中使用Interceptor 更容易 |