Java Servlet 过滤器与 springmvc 拦截器的区别?

前言:在工做中,遇到须要记录日志的状况,不知道该选择过滤器仍是拦截器,故总结了一下。css

servlet 过滤器

定义

  java过滤器可以对目标资源的请求和响应进行截取。过滤器的工做方式分为四种html

应用场景

  能够经过 doFilter 方法的 request、response 提早过滤一些不想要的信息,统一设置一些参数、统一设置字符集、控制权限是否登陆等。java

配置  

  <!-- 定义Filter -->
  <filter>
    <!-- Filter的名字 -->
    <filter-name>loginFilter</filter-name>
    <!-- Filter的实现类 -->
    <filter-class>com.yule.common.filters.LoginFilter</filter-class>
  </filter>
  <!-- 定义Filter拦截的URL地址 -->
  <filter-mapping>
    <!-- Filter的名字 -->
    <filter-name>loginFilter</filter-name>
    <!-- Filter负责拦截的URL 所有以/的请求,若是/*,将会全部的请求-->
    <url-pattern>/*</url-pattern>
  </filter-mapping>

过滤器的4种工做方式

<filter-mapping>
    <filter-name>myFilter</filter-name>
    <servlet-name>目标资源</servlet-name>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

  四中工做方式经过配置 <dispatcher> 标签来决定web

  1. request 过滤器:不配 <dispatcher> 标签,或者配置为 <dispatcher>REQUEST</dispatcher> 。说明只有直接访问该目标资源时该过滤器才会起做用,对转发到该目标资源的请求将忽略不处理。
  2. forward 过滤器:配置为 <dispatcher>FORWARD</dispatcher> 。表示对转发到目标资源的请求过滤,若是直接访问目标资源,过滤器则不起做用。
  3. include 过滤器:配置为 <dispatcher>INCLUDE</dispatcher> 。表示对包含了目标资源的请求过滤,若是直接访问目标资源,则此过滤器将不起做用
    include 包含如下语句:
    在 JSP 页面中的动做:<jsp:include page=.......
    在 Java 代码中的 request.getRequestDispatcher("....").include
    注意:若是目标资源一经过 <%@ include file="目标资源二"%> 指令包含,这时此过滤器不工做,由于这个是指令,在JSP 编译时插入一个包含文本或代码的文件,这个包含的过程是静态的。
  4. error 过滤器:配置为
    <filter-mapping>
        <filter-name>myFilter</filter-name>
        <url-pattern>/error.jsp</url-pattern>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>
    
    <error-page>
        <error-code>404</error-code>
        <location>/error.jsp</location>
    </error-page>
    当咱们访问一个web目标资源时,若是服务器没有找到该目标资源,那么服务器就会给出一个404错误代码。若是咱们给404错误代码定义一个页面,那么当404发生时就会调用该页面。
    当咱们访问一个不存在的文件时,就会访问error.jsp,可是配置了过滤器对错误页面进行过滤,因此过滤器先接受到请求,而后再转发给error.jsp。
    若是咱们访问一个已经存在的页面,会不会调用error.jsp呢?若是这个页面中有response.sendError(404,"出错了!");那么该错误页面仍然会被调用,过滤器也会工做。

执行顺序

  根据 web.xml 的代码顺序来决定过滤器的执行顺序。Filter 链: 一个Web应用中,能够编写多个Filter,这些 Filter 组合起来称之为一个Filter链。spring

  当第一个 Filter 的 doFilter 方法被调用时,web 服务器会建立一个表明 Filter 链的 FilterChain 对象传递给该方法。在 doFilter 方法中,若是调用了 FilterChain 对象的 doFilter 方法,则 web 服务器会检 FilterChain 对象中是否还有 filter ,若是有,则调用第下一个 filter,若是没有,则调用目标资源。数据库

  init() 方法和 destroy() 方法随着项目的启动和关闭才会被调用,且仅一次。编程

举个栗子

  web.xml 中spring-mvc

<!-- 定义Filter -->
  <filter>
    <!-- Filter的名字 -->
    <filter-name>demoFilter</filter-name>
    <!-- Filter的实现类 -->
    <filter-class>com.yule.common.filters.DemoFilter</filter-class>
  </filter>
  <!-- 定义Filter拦截的URL地址 -->
  <filter-mapping>
    <!-- Filter的名字 -->
    <filter-name>demoFilter</filter-name>
    <!-- Filter负责拦截的URL 所有以/的请求,若是/*,将会全部的请求-->
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  Java 代码服务器

package com.yule.common.filters;

import javax.servlet.*;
import java.io.IOException;

/**
 * 过滤器
 * @author yule
 * @date 2018/7/2 21:52
 */
public class DemoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("demo过滤器init。。。");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("demo过滤器doFilter。。。此处省略业务处理逻辑");

        //经过判断是否继续往下走
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("demo过滤器destroy。。。");
    }
}

 

springmvc 拦截器

定义

  springMVC 拦截器源码解析cookie

  Spring Web MVC的处理器拦截器。相似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。拦截器是面向切面编程的,依赖的技术就是Java的动态代理。

应用场景

  1. 日志记录:记录请求日志等
  2. 权限检查:白名单等
  3. 性能监控:能够经过拦截器在进入处理器以前记录开始时间,在处理完后记录结束时间,从而获得该请求的处理时间;
  4. 通用行为:读取cookie获得用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都须要的便可使用拦截器实现。
  5. OpenSessionInView:如Hibernate,在进入处理器打开Session,在完成后关闭Session。

  本质是AOP(面向切面编程),符合 AOP 的全部功能均可以使用拦截器实现。

配置

  在 spring-mvc.xml 中

<mvc:interceptors>
        <!--  使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截全部的请求   -->
        <!-- <bean class="com.bybo.aca.web.interceptor.Login"/> -->

        <mvc:interceptor>
            <!--进行拦截的地址-->
            <mvc:mapping path="/**"/>
            <bean class="com.yule.common.interceptors.DemoInterceptor"/>
        </mvc:interceptor>

    </mvc:interceptors>

执行顺序

  根据 xml 中的配置顺序来执行。拦截器的执行顺序在过滤器之间。

方法说明

  • preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)方法,该法在请求处理以前进行调用。SpringMVC 中的 Interceptor 是链式调用的,在一个应用中或者说是在一个请求中能够同时存在多个 Interceptor 。每一个 Interceptor 的调用会依据它的声明顺序依次执行,并且最早执行的都是 Interceptor 中的 preHandle 方法,因此能够在这个方法中进行一些前置初始化操做或者是对当前请求作一个预处理,也能够在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值 Boolean 类型的,当它返回为 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当返回值为 true 时,就会继续调用下一个 Interceptor 的 preHandle 方法,若是已是最后一个 Interceptor 的时候,就会是调用当前请求的 Controller 中的方法。
  • postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)方法,经过 preHandle 方法的解释我们知道这个方法包括后面要说到的 afterCompletion 方法都只能在当前所属的 Interceptor 的 preHandle 方法的返回值为 true 的时候,才能被调用。postHandle 方法在当前请求进行处理以后,也就是在 Controller 中的方法调用以后执行,可是它会在 DispatcherServlet 进行视图返回渲染以前被调用,因此我们能够在这个方法中对 Controller 处理以后的 ModelAndView 对象进行操做。postHandle 方法被调用的方向跟 preHandle 是相反的,也就是说,先声明的 Interceptor 的 postHandle 方法反而会后执行。
  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)方法,也是须要当前对应的 Interceptor 的 preHandle 方法的返回值为 true 时才会执行。所以,该方法将在整个请求结束以后,也就是在 DispatcherServlet 渲染了对应的视图以后执行,这个方法的主要做用是用于进行资源清理的工做。afterCompletion 方法被调用的方向也跟 preHandle 是相反的,也就是说,先声明的 Interceptor 的 afterCompletion 方法反而会后执行

举个栗子

  spring-mvc 中

<mvc:interceptors>
        <!--  使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截全部的请求   -->
        <!-- <bean class="com.bybo.aca.web.interceptor.Login"/> -->

        <mvc:interceptor>
            <!--进行拦截的地址-->
            <mvc:mapping path="/**"/>
            <bean class="com.yule.common.interceptors.DemoInterceptor"/>
        </mvc:interceptor>

    </mvc:interceptors>

  Java 代码

package com.yule.common.interceptors;

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

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

/**
 * 自定义拦截器方式一
 * Created by yule on 2018/7/2 22:37.
 */
public class DemoInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        //return true 表示继续下一个拦截器或者 control 层
        //return false 表示被拦截下来
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}
package com.yule.common.interceptors;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

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

/**
 * 自定义拦截器方式二
 * 通常都是经过实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter抽象类,复写preHandle()、postHandle()和afterCompletion()这 3 个方法来对用户的请求进行拦截处理
 * Created by yule on 2018/7/2 22:43.
 */
public class Demo2Interceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
}

 区别

  其实二者仍是有类似之处,就是均可以用做权限检查、日志记录等状况,可是在这些状况下如何选择就要知道不一样之处。

不一样之处

  使用范围不一样:Filter 只能用于 Web 程序中。而拦截器能够用于 Web 程序,Application、Swing 程序中。

  规范不一样:Filter 是 servlet 规范规定的,是 servlet 支持的。而拦截器是在 spring 容器内,是 spring 框架支持的。

  使用资源不一样:Filter 不能直接使用 spring 的资源、对象等。而拦截器是一个 spring 组件,归 spring 管理,配置在 spring 文件中,所以能使用 spring 的任何资源、对象,例如 Service 对象、数据源、事务管理等,经过 IoC 注入到拦截器便可。也就是说在拦截器中能够注入一个 service ,用于业务逻辑或者访问数据库。

  深度不一样:Filter 只在 Servlet 先后起做用。而拦截器可以深刻到方法先后、异常抛出先后等,所以拦截器的使用具备更大的弹性。

  做用范围不一样:拦截器只能对 Controller 层请求起做用,而过滤器则能够对几乎全部的请求起做用(如 .js、.css等)。

  因此,在 Spring 构架的程序中,要优先使用拦截器。

注意

  拦截器是在过滤器之间运行的。

执行顺序举例

拦截器,spring-mvc.xml 中:

<mvc:interceptors>
        <!--  使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截全部的请求   -->
        <!-- <bean class="com.bybo.aca.web.interceptor.Login"/> -->

        <mvc:interceptor>
            <!--进行拦截的地址-->
            <mvc:mapping path="/**"/>
            <bean class="com.yule.common.interceptors.DemoInterceptor"/>
        </mvc:interceptor>

        <mvc:interceptor>
            <!--进行拦截的地址-->
            <mvc:mapping path="/**"/>
            <bean class="com.yule.common.interceptors.Demo2Interceptor"/>
        </mvc:interceptor>

    </mvc:interceptors>

java 代码:

package com.yule.common.interceptors;

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

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

/**
 * 自定义拦截器方式一
 * Created by yule on 2018/7/2 22:37.
 */
public class DemoInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        //return true 表示继续下一个拦截器或者 control 层
        //return false 表示被拦截下来
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("afterCompletion");
    }
}
View Code
package com.yule.common.interceptors;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

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

/**
 * 自定义拦截器方式二
 * 通常都是经过实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter抽象类,复写preHandle()、postHandle()和afterCompletion()这 3 个方法来对用户的请求进行拦截处理
 * Created by yule on 2018/7/2 22:43.
 */
public class Demo2Interceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle 2222222...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle 22222222");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion 2222222");
    }
}
View Code

过滤器, web.xml 中:

<!-- 定义Filter -->
  <filter>
    <!-- Filter的名字 -->
    <filter-name>demoFilter</filter-name>
    <!-- Filter的实现类 -->
    <filter-class>com.yule.common.filters.DemoFilter</filter-class>
  </filter>
  <!-- 定义Filter拦截的URL地址 -->
  <filter-mapping>
    <!-- Filter的名字 -->
    <filter-name>demoFilter</filter-name>
    <!-- Filter负责拦截的URL 所有以/的请求,若是/*,将会全部的请求-->
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <filter>
    <filter-name>demo2Filter</filter-name>
    <filter-class>com.yule.common.filters.Demo2Filter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>demo2Filter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

java 代码:

package com.yule.common.filters;

import javax.servlet.*;
import java.io.IOException;

/**
 * 过滤器
 * @author yule
 * @date 2018/7/2 21:52
 */
public class DemoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("demo过滤器init。。。");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("demo过滤器doFilter。。。此处省略业务处理逻辑");

        //经过判断是否继续往下走
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("demo过滤器destroy。。。");
    }
}
View Code
package com.yule.common.filters;

import javax.servlet.*;
import java.io.IOException;

/**
 * Created by yule on 2018/7/2 22:18.
 */
public class Demo2Filter implements Filter{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("demo2过滤器init  2222222");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("demo过滤器doFilter  222222");

        //经过判断是否继续往下走
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("demo2过滤器destroy  22222 ");
    }
}
View Code

调用 controller 打印结果:

相关文章
相关标签/搜索