本文部分内容来自 “过滤器 和 拦截器 6个区别,别再傻傻分不清了”
一 两者的区别
这两者在功能上有些类似,在具体技术实现方面还是有较大的差距的,这两者都是属于面向切面编程的具体实现,区别如下:
过滤器是依赖Servlet
容器的,属于Servlet规范的一部分(也就是只能在web程序中使用),而拦截器则是独立存在的,在任何情况下都能使用
过滤器的执行由Servlet容器回调完成,而拦截器则通过动态代理的方式来执行
- 在我们自定义的过滤器中都会实现一个 doFilter()方法,这个方法有一个FilterChain 参数,而实际上它是一个回调接口。ApplicationFilterChain是它的实现类, 这个实现类内部也有一个 doFilter() 方法就是回调方法。
- ApplicationFilterChain里面能拿到我们自定义的xxxFilter类,在其内部回调方法doFilter()里调用各个自定义xxxFilter过滤器,并执行 doFilter() 方法。
- 而每个xxxFilter 会先执行自身的 doFilter() 过滤逻辑,最后在执行结束前会执行filterChain.doFilter(servletRequest, servletResponse),也就是回调ApplicationFilterChain的doFilter() 方法,以此循环执行实现函数回调。
过滤器的生命周期由Servlet容器管理,而拦截器则可以通过Ioc容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用更加方便
过滤器 和 拦截器的触发时机也不同,我们看下边这张图。![在这里插入图片描述](https://img-blog.csdnimg.cn/20200608170716741.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hjejEzMTQ=,size_16,color_FFFFFF,t_70)
-
过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
-
拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
二 注册过滤器
创建过滤器需要实现javax.servlet.Filter
接口, 写接口中的方法.如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import java.io.IOException;
public class TestFilter implements Filter {
private Logger log = LoggerFactory.getLogger(TestFilter.class);
// 类的初始化方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("初始化过滤器");
}
// 类的销毁方法
@Override
public void destroy() {
log.info("销毁过滤器");
}
// 过滤器具体功能,如这里是对接口的执行时间进行计算
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
log.info("进入接口时间:{}", startTime);
filterChain.doFilter(servletRequest,servletResponse);
long endTime = System.currentTimeMillis();
log.info("接口完成时间:{},总用时:{}", endTime, endTime-startTime);
}
}
SpringBoot中需要通过FilterRegistrationBean配置过滤器,如果是以前的web项目,则在web.xml中进行配置
import cn.xczh.basic_test.uitls.aop.TestFilter;
import cn.xczh.basic_test.uitls.aop.TestLogFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AopConfig {
@Bean
public FilterRegistrationBean registRunTimeFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
// 指定注入的过滤器
registrationBean.setFilter(new TestFilter());
// 过滤器所拦截的uri
registrationBean.addUrlPatterns("/*");
// 设置名字
registrationBean.setName("runTimeFilter");
// 设置优先级别,数字越小优先级越高
registrationBean.setOrder(2);
return registrationBean;
}
}
或者是通过@WebFilter(urlPatterns = "/*", servletNames = "runtimeFilter")
注解进行标记,为filter指定过滤的uri和servletName,如果使用这种方式的话无法控制过滤器的优先级别,其执行顺序依赖于Filter的名称,是根据Filter类名(注意不是配置的filter的名字)的字母顺序排列,并且@WebFilter指定的过滤器优先级order默认为int最大值
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author :xcz 2020/6/8 14:43
* 天靈靈地靈靈,代碼不要有問題
* 测试过滤器,用于过滤接口运行时间
*/
@WebFilter(urlPatterns = "/*", filterName= "runtimeFilter")
public class TestRunTimeFilter implements Filter {
private Logger log = LoggerFactory.getLogger(TestRunTimeFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("初始化过滤器");
}
@Override
public void destroy() {
log.info("销毁过滤器");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
log.info("进入接口时间:{}", startTime);
filterChain.doFilter(servletRequest,servletResponse);
long endTime = System.currentTimeMillis();
log.info("接口完成时间:{},总用时:{}", endTime, endTime-startTime);
}
}
(ps:如果是在springboot中使用该注解的话,需要添加@Component或则是@Configuration注解将该类添加进入ioc容器,否则将无法生效,或则在application启动类上添加@ServletComponentScan该注解可以将@WebServlet、@WebFilter、@WebListener自动注入容器
)
三 过滤器配置
创建拦截器类,实现 import org.springframework.web.servlet.HandlerInterceptor
接口
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LogInterceptor implements HandlerInterceptor {
Logger log = LoggerFactory.getLogger(LogInterceptor.class);
// 这个方法将在请求处理之前进行调用。如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("拦截器拦截,请求方法:{},请求方式:{}", request.getRequestURL(), request.getMethod());
return true;
}
// 只有在 preHandle() 方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet
返回渲染视图之前被调用。postHandle() 方法被调用的顺序跟 preHandle() 是相反的,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("拦截器postHandle");
}
// 只有在 preHandle() 方法返回值为true 时才会执行。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("拦截器afterCompletion");
}
}
对拦截器进行配置
import cn.xczh.basic_test.uitls.aop.LogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器,为其指定拦截的url
registry.addInterceptor(new LogInterceptor()).addPathPatterns("/*");
}
}