一、http请求后台执行的顺序
启动顺序:监听器 > 过滤器 > 拦截器(context-param-->listener-->filter-->servlet-->interceptor)
记忆技巧:接到命令,监听电报,过滤敌情,拦截行动。
二、区别和理解
如果我们现在大海就是我们启动的项目,那么监听器就能够听到整个大海的声音,过滤器就是能够过滤出其中的鱼,而拦截器则是拦截其中的部分鱼,并且作标记。所以当需要监听到项目中的一些信息,并且不需要对流程做更改时,用监听器;当需要过滤掉其中的部分信息,只留一部分时,就用过滤器;当需要对其流程进行更改,做相关的记录时用拦截器。
三、详细概念
context-param:就是一些需要初始化的配置,放入context-param中,从而被监听器(这里特指org.springframework.web.context.ContextLoaderListener)监听,然后加载;
监听器(listener):就是对项目起到监听的作用,它能感知到包括request(请求域),session(会话域)和applicaiton(应用程序)的初始化和属性的变化;
过滤器(filter):就是对请求起到过滤的作用,它在监听器之后,作用在servlet之前,对请求进行过滤;
servlet:就是对request和response进行处理的容器,它在filter之后执行,servlet其中的一部分就是controller层(标记为servlet_2),还包括渲染视图层(标记为servlet_3)和进入controller之前系统的一些处理部分(servlet_1),另外我们把servlet开始的时刻标记为servlet_0,servlet结束的时刻标记为servlet_4。
拦截器(interceptor):就是对请求和返回进行拦截,它作用在servlet的内部,具体来说有三个地方:
1)servlet_1和servlet_2之间,即请求还没有到controller层
2)servlet_2和servlet_3之间,即请求走出controller层次,还没有到渲染时图层
3)servlet_3和servlet_4之间,即结束视图渲染,但是还没有到servlet的结束
它们之间的关系,可以用一张图来表示:
四、用途
监听器的用途:
1.通常使用Web监听器做以下的内容: 2.统计在线人数,利用HttpSessionLisener 3.加载初始化信息:利用ServletContextListener 4.统计网站访问量 5.实现访问监控
servlet的监听接口, 1)ServletContextListener监听web应用的启动和关闭 2)ServletContextAttributeListener监听ServletContext范围内属性的改变 3)ServletRequestListener监听用户的请求 4)ServletRequestAttributeListener监听ServletRequest范围内(request)内属性的变化 5)HttpSessionListener监听用户session的开始和结束 6)HttpSessionAttributeListener监听HttpSession范围内session内属性的改变。 springmvc的监听器ContextLoaderListener(实现ServletContextListener)用于加载spring容器的配置文件。
过滤器的用途:
1、对请求参数request,response等设置字符编码或者其他响应头
2、对用户登录信息获取,没有重定向到登录页,实现用户权限访问
3、对一些非法请求过滤处理,如敏感参数等
拦截器:
对通过过滤的指定拦截的请求,做一些特殊处理,如aop的模式(性能分析, 权限检查, 日志记录)
五、java代码实现
过滤器:
定义一个TimeFilter类,实现javax.servlet.Filter: public class TimeFilter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("过滤器初始化"); }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("开始执行过滤器"); Long start = new Date().getTime(); filterChain.doFilter(servletRequest, servletResponse); System.out.println("【过滤器】耗时 " + (new Date().getTime() - start)); System.out.println("结束执行过滤器"); }
@Override public void destroy() { System.out.println("过滤器销毁"); } } TimeFilter重写了Filter的三个方法,方法名称已经很直白的描述了其作用,这里不再赘述。要使该过滤器在Spring Boot中生效,还需要一些配置。这里主要有两种配置方式。
配置方式一 可通过在TimeFilter上加上如下注解: @Component @WebFilter(urlPatterns = {"/*"}) public class TimeFilter implements Filter { ... }
@Component注解让TimeFilter成为Spring上下文中的一个Bean,@WebFilter注解的urlPatterns属性配置了哪些请求可以进入该过滤器,/*表示所有请求。
配置方式二 除了在过滤器类上加注解外,我们也可以通过FilterRegistrationBean来注册过滤器。
定义一个WebConfig类,加上@Configuration注解表明其为配置类,然后通过FilterRegistrationBean来注册过滤器: @Configuration public class WebConfig { @Bean public FilterRegistrationBean timeFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); TimeFilter timeFilter = new TimeFilter(); filterRegistrationBean.setFilter(timeFilter);
List<String> urlList = new ArrayList<>(); urlList.add("/*");
filterRegistrationBean.setUrlPatterns(urlList); return filterRegistrationBean; } }
FilterRegistrationBean除了注册过滤器TimeFilter外还通过setUrlPatterns方法配置了URL匹配规则。重启项目访问
通过过滤器我们只可以获取到servletRequest对象,所以并不能获取到方法的名称,所属类,参数等额外的信息。
拦截器:
定义一个TimeInterceptor类,实现org.springframework.web.servlet.HandlerInterceptor接口: public class TimeInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { System.out.println("处理拦截之前"); httpServletRequest.setAttribute("startTime", new Date().getTime()); System.out.println(((HandlerMethod) o).getBean().getClass().getName()); System.out.println(((HandlerMethod) o).getMethod().getName()); return true; }
@Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { System.out.println("开始处理拦截"); Long start = (Long) httpServletRequest.getAttribute("startTime"); System.out.println("【拦截器】耗时 " + (new Date().getTime() - start)); }
@Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { System.out.println("处理拦截之后"); Long start = (Long) httpServletRequest.getAttribute("startTime"); System.out.println("【拦截器】耗时 " + (new Date().getTime() - start)); System.out.println("异常信息 " + e); } }
TimeInterceptor实现了HandlerInterceptor接口的三个方法。preHandle方法在处理拦截之前执行,postHandle只有当被拦截的方法没有抛出异常成功时才会处理,afterCompletion方法无论被拦截的方法抛出异常与否都会执行。
通过这三个方法的参数可以看到,相较于过滤器,拦截器多了Object和Exception对象,所以可以获取的信息比过滤器要多的多。但过滤器仍无法获取到方法的参数等信息,我们可以通过切面编程来实现这个目的,具体可参考https://mrbird.cc/Spring-Boot-AOP log.html。
要使拦截器在Spring Boot中生效,还需要如下两步配置:
1.在拦截器类上加入@Component注解;
2.在WebConfig中通过InterceptorRegistry注册过滤器: @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Autowired private TimeInterceptor timeInterceptor;
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(timeInterceptor); } } 监听器:
第一种:在初始化的时候添加监听器 app.addListeners(new MyApplicationListener());
第二种:
第三种:在application.propertiies 添加
context.listener.classes=com.boot.enable.addlistener.MyApplicationListener
第四种:
@Component public class HandlerEvent {
@EventListener public void evendListener(MyApplicationEvent event){ System.out.println("监听了-----"+event.getSource()); System.out.println("监听了-----"+event.getClass()); }
}