spring项目--过滤器,拦截器,监听器

    技术2022-07-21  62

    一、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());     }

    }

    Processed: 0.008, SQL: 9