架构---Spring-Mvc中的监听器-拦截器-过滤器

Spring-Mvc框架中的拦截器和过滤器

一.知识背景介绍 

首先我们要知道该博客的监听器和拦截器和过滤器概念以及实例是建立在SSM框架之上的,对于SSM框架内部运行原理不懂的大家可以去参考我的另一篇博客:

   spring架构---spring-Mvc运行原理解读 https://blog.csdn.net/weixin_42504145/article/details/84074628

1.监听器

Listener是实现了javax.servlet.ServletContextListener接口的服务器端程序,它也是随web应用的启动而启动,只初始化了一次,随web应用的停止而销毁.

2.拦截器

Interceptor是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个Action执行的前后执行的代码,也可以在一个Action执行前阻止其执行 。同时也提供了一种可以提取Action中可重用的部分的方式。

3.过滤器

Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码,做一些业务逻辑判断,过滤器随web应用启动而启动,只初始化一次,只有当web应用停止或重新部署才销毁

 二.三种方式代码示例

 再看代码实例之前我们先看看Spring-Mvc负责分发请求的Dispatcher

   DispatcherServlet是ssm框架前置控制器,所有的请求都通过DispatcherServlet来进行分发,配置在web.xml文间中.DispatcherServlet拦截匹配的请求也就是Servlet拦截匹配规则可以自己定义,像下面一行代码<url-pattern>/</url-pattern>就是缺省匹配,定义规则可以参考下面这篇博客

servlet的url-pattern匹配规则   http://www.cnblogs.com/canger/p/6084846.html

首先进入方法doDispatch(HttpServletRequest request, HttpServletResponse response)经过一系列步骤,找到你要请求的目标Controller,最后交给他处理(没有其他的拦截器)来处理。如果没有handle处理器就会返回去,不再执行后面,所以说只能拦截severlet请求,但是不能对这个请求进行处理(缺陷解决在下面).

web.xml配置:

 <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>Dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring/spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>			<!-- 代表servlet启动就初始化 -->
  </servlet>
  <servlet-mapping>
    <servlet-name>Dispatcher</servlet-name>
    <url-pattern>/</url-pattern>                    <!-- 匹配规则拦截 -->
  </servlet-mapping>

 1.监听器(Listener)

Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener接口的服务器端程序,它也是随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。主要作用是:做一些初始化的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。

在javax.servlet.ServletContextListener接口中定义了2种方法:

  void contextInitialized(ServletContextEvent sce) 监听器的初始化

  void contextDestroyed(ServletContextEvent sce) 监听器销毁
使用样例如下:

package com.cn.util;
 
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
 
public class ServletContextListenerUtil implements ServletContextListener{
 
	//监听器的初始化
	@Override
	public void contextInitialized(ServletContextEvent sce) {
		System.out.println("监听器ServletContextListenerUtil初始化");
	}
 
	//监听器销毁
	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		System.out.println("监听器ServletContextListenerUtil销毁");
	}
}

需要注意的是在Spring-Mvc项目启动时,先启动监听器,再启动过滤器。 

2.过滤器(filter) 

Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码、做一些业务逻辑判断等.其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,此时你就可以对请求或响应(Request、Response)统一设置编码,简化操作;同时还可以进行逻辑判断,如用户是否已经登录、有没有权限访问该页面等等工作,它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关的请求,只有当你的web应用停止或重新部署的时候才能销毁。

在javax.servlet.Filter接口中定义了3个方法:

 void init(FilterConfig filterConfig) 用于完成过滤器的初始化

 void destroy() 用于过滤器销毁前,完成某些资源的回收

 void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) 实现过滤功能,该方法对每个请求增加额外的处理
 

package com.cn.util;
 
import java.io.IOException;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class FilterUtil implements Filter{
 
	@SuppressWarnings("unused")
	private FilterConfig filterConfig;
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
		System.out.println("过滤器Filter初始化");
	}
 
	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
			throw new ServletException("FilterUtil just supports HTTP requests");
		}
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		httpRequest.setCharacterEncoding(this.filterConfig.getInitParameter("encoding"));
		httpResponse.setCharacterEncoding(this.filterConfig.getInitParameter("encoding"));
		chain.doFilter(httpRequest, httpResponse);
	}
 
	@Override
	public void destroy() {
		System.out.println("过滤器Filter销毁");
	}
 
}

 web.xml配置如下:

<filter>  
 	<filter-name>LoginFilter</filter-name>  
 	<filter-class>com.qcbylearn.utils.LoginFilter</filter-class>    
  <filter-mapping>  
  	<filter-name>LoginFilter</filter-name>
    <!-- 这里表示对所有的以jsp后缀的文件有效,其他的无效 -->  
 	<url-pattern>*.jsp</url-pattern>  
  </filter-mapping>
  <filter-mapping>
  	<filter-name>LoginFilter</filter-name>
    <!-- 这里表示对所有的以html后缀的文件有效,其他的无效 -->  
  	<url-pattern>*.html</url-pattern>
  </filter-mapping>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
	   <filter-name>requestFilter</filter-name>
	   <url-pattern>/*</url-pattern>  <!-- 这里写Filter的匹配规则 -->  
   </filter-mapping>

3.自定义拦截器(Interceptor)

    拦截器是在面向切面编程中应用的,就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。拦截器不是在web.xml配置的,比如struts在struts.xml配置,在springMVC在spring与springMVC整合的配置文件中配置。

      首先请求通过统一入口进入DispatcherServlet,再DispatcherServlet里面有个方法寻找这个这个请求的处理器和Interceptor,这时候就会寻找到HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor 接口的类,就会找到你自定义的拦截器,继承HandlerInterceptor 有是三个方法,preHandle , postHandle 和 afterCompletion,preHandle 在业务处理器处理请求之前被调用,然后处理完请求就会调用postHandle,或者在视图渲染之前调用它,最后请求执行完,视图渲染完调用,DispatcherServlet完全处理完请求后被调用afterCompletion,可用于清理资源,如果定义多个拦截器,先定义的拦截器先执行preHandle,但是没有另外的两个方法,而是等待其他拦截器执行prehandle方法,知道最后一个拦截器执行完毕,也是从最后一个拦截器逆序执行这两个方法。

在springmvc中,定义拦截器要实现HandlerInterceptor接口,并实现该接口中提供的三个方法:

    1.preHandle方法:进入Handler方法之前执行。可以用于身份认证、身份授权。比如如果认证没有通过表示用户没有登陆,需要此方法拦截不再往下执行(return false),否则就放行(return true)。
    2.postHandle方法:进入Handler方法之后,返回ModelAndView之前执行。可以看到该方法中有个modelAndView的形参。应用场景:从modelAndView出发:将公用的模型数据(比如菜单导航之类的)在这里传到视图,也可以在这里同一指定视图。
    3.afterCompletion方法:执行Handler完成之后执行。应用场景:统一异常处理,统一日志处理等。
在springmvc中,拦截器是针对具体的HandlerMapping进行配置的,也就是说如果在某个HandlerMapping中配置拦截,经过该 HandlerMapping映射成功的handler最终使用该拦截器。
 

给出样例代码: 

package com.qcbylearn.utils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class HandleInterceptor implements HandlerInterceptor{

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		// 所有请求第一个进入的方法

        RequestWrapper myRequestWrapper = new RequestWrapper((HttpServletRequest) request);
		String reqURL = request.getRequestURL().toString();
	    String ip = request.getRemoteHost ();
	    
		InputStream  is = request.getInputStream ();
	    StringBuilder responseStrBuilder = new StringBuilder ();
	    BufferedReader streamReader = new BufferedReader (new InputStreamReader (is,"UTF-8"));
	    String inputStr;
	     while ((inputStr = streamReader.readLine ()) != null)
         responseStrBuilder.append (inputStr);
//	     System.out.println("请求参数: " + responseStrBuilder.toString ());
	     String parmeter = responseStrBuilder.toString();
	     
	   long startTime = System.currentTimeMillis();
       request.setAttribute("startTime", startTime);
       if (handler instanceof HandlerMethod) {
           StringBuilder sb = new StringBuilder(1000);
           SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
           sb.append("-----------------------").append(df.format(new Date())).append("-------------------------------------\n");
           HandlerMethod h = (HandlerMethod) handler;
		   //Controller 的包名
           sb.append("Controller: ").append(h.getBean().getClass().getName()).append("\n");
		   //方法名称
           sb.append("Method    : ").append(h.getMethod().getName()).append("\n");
		   //请求方式  post\put\get 等等
           sb.append("RequestMethod    : ").append(request.getMethod()).append("\n");
		   //所有的请求参数
           sb.append("Params    : ").append(parmeter).append("\n");
		   //部分请求链接
           sb.append("URI       : ").append(request.getRequestURI()).append("\n");
		    //完整的请求链接
           sb.append("AllURI    : ").append(reqURL).append("\n");
		   //请求方的 ip地址
           sb.append("request IP: ").append(ip).append("\n");
           System.out.println(sb.toString());
           
       }
//       修改request中的参数并保存到request中 
//       request.setAttribute("parmeter_json", parmeter); 
        
       return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// TODO Auto-generated method stub

        
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// TODO Auto-generated method stub
		
	}
	

}

Spring-mvc.xml配置如下:

<!-- 拦截器配置 -->
    <mvc:interceptors>
        <!--多个拦截器,顺序执行 -->
	    <!-- 登陆认证拦截器 -->
	    <mvc:interceptor>
	        <!-- /** 表示拦截所有url包括子url路径,/*只拦截根下的url -->
	        <mvc:mapping path="/**"/>
	        <bean id="handleInterceptor" class="com.qcbylearn.utils.HandleInterceptor">
	    </mvc:interceptor>
	    <!-- 其他拦截器 -->
    </mvc:interceptors>

三.三者问题对比

问题一:过滤器和拦截器的区别?

拦截器是基于java的反射机制的,而过滤器是基于函数回调.

②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。

③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。

④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。

⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。

⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

问题二:过滤器和拦截器的执行顺序?

过滤器和拦截器的执行顺序:过滤前 - 拦截前 - Action处理 - 拦截后 - 过滤后。拦截器是被包裹在过滤器之中的。

问题三:各自应用场景?

两者的本质区别:从灵活性上说拦截器功能更强大些,Filter能做的事情,Interceptor都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录状态之类),太细的话,还是建议用interceptor。Filter只在Servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在Spring构架的程序中,优先使用拦截器。

参考博客链接:

https://blog.csdn.net/aqsunkai/article/details/51813121

https://blog.csdn.net/baidu_37050701/article/details/56286513

http://www.cnblogs.com/canger/p/6084846.html

猜你喜欢

转载自blog.csdn.net/weixin_42504145/article/details/84963088