一、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%20log.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());
}
}