Spring MVC----spring MVC配置+拦截器使用  

 概述:

Spring MVC 也叫 Spring Web MVC ,属于展示层框架。SpringMVC 是 Spring 为展示层提供的基于MVC设计理念的优秀web框架。

spring MVC 开发模式:

  • 模型 (Model):封装了应用程序数据,通常它们将由 POJO 类组成。
  • 视图 (View):负责渲染模型数据,一般来说它生成客户端浏览器可以解释 HTML 输出。
  • 控制器 (Controller):负责处理用户请求并构建适当的模型,并将其传递给视图进行渲染。

Spring MVC 通过一套mvc注解,让POJO 成为处理请求的控制器,而无需实现任何借口。  

1、核心:DispatcherServlet 组件类

参考:https://blog.csdn.net/floating_dreaming/article/details/89089214

前言:在学java web 中,使用每创建一个请求,都需要在web.xml中配置 Servlet,不灵活

Spring Web MVC 框架是围绕 DispatcherServlet 设计的,它处理所有的 HTTP 请求和响应。

Spring Web MVC DispatcherServlet 的请求处理工作流如下图所示:

Spring MVC----spring MVC配置+拦截器使用
 

以下是对应于到 DispatcherServlet 的传入 HTTP 请求的事件顺序:

  • 在接收到 HTTP 请求后,DispatcherServlet 会查询 HandlerMapping 以调用相应的 Controller。
  • Controller 接受请求并根据使用的 GET 或 POST 方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给 DispatcherServlet。
  • DispatcherServlet 将从 ViewResolver 获取请求的定义视图。
  • 当视图完成,DispatcherServlet 将模型数据传递到最终的视图,并在浏览器上呈现。

所有上述组件,即: HandlerMapping,Controller 和 ViewResolver 是 WebApplicationContext 的一部分,它是普通 ApplicationContext 的扩展,带有 Web 应用程序所需的一些额外功能。

根据上图我们需要配置的有

spring MVC运行流程

请求(url)--->springDispatcherServlet (查看是否存在对应的映射,如果不存在,查看是否配置了<mvc:default-servlet-handle />,如果有,寻找目标资源,返回目标资源(不需要经过RequestMapping,一般是静态资源文件),没有的话,返回404)--->HandleMapping(如果映射存在,有HandleMapping获得handleExecutionChain对象)--->获得HandleAdapter对象--->调用拦截器preHandle方法---->调用目标方法(获得model and view name)----->调用拦截器postHandle方法(如果存在异常,会HandleExecutionResolver,形成一个新的model and view name)---->有VIewResovler组件根据model and view name得到实际的view--->返回给浏览器;

2、视图解析分析

2.1视图和视图解析器

  请求处理方法执行完成后,最终返回一个ModelAndView对象。对于那些返回String,View或ModeMap 等类型的处理方法,Spring MVC也会在内部将它们装配成一个ModelAndView对象,它包含了逻辑名和模型对象的视图
  Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是JSP,也可能是Excel、JFreeChart 等各种表现形式的视图。

  对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现MVC的充分解耦。

2.2 视图

  视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。

  为了实现视图模型和具体实现技术的解耦,Spring在org.springframework.web.servlet包中定义了一个高度抽象的View接口

  视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题。

2.2 视图解析器

  SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在Spring WEB 上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。

  视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象。

  所有的视图解析器都必须实现ViewResolver接口:

  

 使用:

在 pom.xml 配置文件中增加 org.springframework:spring-webmvc 依赖

spring-webmvc 依赖包中包含spring-web,所以之前的spring-web可以删除;

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.3.17.RELEASE</version>
</dependency>

web.xml中配置

CharacterEncodingFilter

配置字符集过滤器,用于解决中文编码问题

<filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

  但是在需要同时支持跳转jsp和html的时候,这部分需要去掉,强制编码会导致html在显示的时候出现中文乱码(注意缓存)

<init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
</init-param>

  

配置DispatcherServlet

在web.xml中配置 Spring 的 Servlet 分发器处理所有 HTTP 的请求和响应

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--    配置DispatcherServlet初始化参数
            作用:配置 spring MVC 配置文件(xml)的位置和名称
-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:/spring-mvc*.xml</param-value>
        </init-param>
<!--    服务器加载就被创建,任何一个数字都可以,数字越小,表示优先级越高(对于配置了多个需要在服务器启动时,Servlet就要加载有用)-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>  // /:拦截所有的请求,但不拦截jsp, /* 拦截请求包括jsp
    </servlet-mapping>
</web-app>

补充:可以不用配置DispatcherServlet初始化参数,默认去/WEB-INF/springDispatcherServle-servlet.xml,所以我们在/WEB-INF目录下生成这样的文件就好了(SpringDispatherServlet是我们配置

<servlet-name>中的名字)

在resources配置spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <description>Spring MVC Configuration</description>

    <!-- 加载配置属性文件 -->
    <context:property-placeholder ignore-unresolvable="true" location="classpath:myshop.properties"/>

    <!-- 使用 Annotation 自动注册 Bean,只扫描 @Controller -->
    <context:component-scan base-package="com.lusifer.myshop" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 默认的注解映射(@RequestMapping)的支持 -->
    <mvc:annotation-driven />

    <!-- 定义视图文件解析 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="${web.view.prefix}"/>
        <property name="suffix" value="${web.view.suffix}"/>
    </bean>

    <!-- 静态资源映射,指定静态资源的路径 -->
    <mvc:resources mapping="/static/**" location="/static/" cache-period="31536000"/>
</beans>

相关配置说明:

  • context:property-placeholder:动态加载属性配置文件以变量的方式引用需要的值

  • context:component-scan:当前配置文件为 MVC 相关,故只需要扫描包含 @Controller 的注解即可,由于 spring-context.xml 配置文件中也配置了包扫描,所以还需要排除spring MVC.xml中的 @Controller 的注解扫描,否则就会创建多个实例。

  • InternalResourceViewResolver:视图文件解析器的一种,用于配置视图资源的路径和需要解释的视图资源文件类型,这里有两个需要配置的属性 prefix(前缀)以及 suffix(后缀)。

    • prefix:配置视图资源路径,如:/WEB-INF/views/
    • suffix:配置视图资源类型,如:.jsp
  • mvc:resources:静态资源映射,主要用于配置静态资源文件存放路径,如:JS、CSS、Image 等

处理静态资源的另一种方法

default-servlet-handler 将在SpringMVC 上下文中定义一个DefaultServletHttpRequestHandler,它会对进入DispatcherServlet的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由WEB应用服务器默认的Servlet 处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。

一般WEB应用服务器默认的Servlet的名称都是default,若所使用的WEB服务器的默认Servlet 名称不是default,则需要通过default-servlet-name 属性显式指定

<mvc:default-servlet-handler></mvc:default-servlet-handler> //spring-MVC.xml中将处理静态资源的配置,可以更换为这个

  

系统相关配置

在 spring-mvc.xnl 中,我们配置了 <context:property-placeholder ignore-unresolvable="true" location="classpath:myshop.properties"/> 用于动态加载属性配置文件,实际开发中我们会将系统所需的一些配置信息封装到 .properties 配置文件中便于统一的管理。

创建一个名为 myshop.properties (resources下)的配置文件,内容如下:

#============================#
#==== Framework settings ====#
#============================#

# u89c6u56feu6587u4ef6u5b58u653eu8defu5f84
web.view.prefix=/WEB-INF/views/
web.view.suffix=.jsp

去掉 Spring 配置的重复扫描

修改 spring-context.xml 配置:

<!-- 使用 Annotation 自动注册 Bean,在主容器中不扫描 @Controller 注解,在 SpringMVC 中只扫描 @Controller 注解。-->
<context:component-scan base-package="com.funtl.my.shop">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

  

在WEB-INF下创建views目录,用于存放jsp文件;

在webapp下创建一个static,用于存放静态资源,css,js文件等;

 

基本使用

@Controller
public class DemoController{
    //使用注解映射请求url
    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public String test(HttpServletResponse httpServletResponse) throws IOException {
        //通过"prefix"+"login"+"suffix",得到物理视图(jsp),做请求转发;
        return "login";
    }
}

 详细扩展:

1、@RequestMapping()

  不仅仅可以在方法上定义,还可以在类上定义

  1.1 参数:params,headers

@RequestMapping(value = "/loginxx",method = RequestMethod.GET,params = {"username","password!=10"},headers = {"Accept-Language=zh-CN,zh;q=0.9"})
    params:请求中必须带上username和password,并且password的值不能是10(int)
    headers:请求头中的Accept-Language必须是zh-CN,zh;q=0.9 

  1.2 @RequestParam(获取url中的参数)

    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public String test(@RequestParam("username") String username,@RequestParam(value = "password",required = false) String password,@RequestParam(value = "test",required = false,defaultValue = "test") String test) throws IOException {
        System.out.println(username);
        System.out.println(password);
        System.out.println(test);
        return "login";
    }
    
    //可以直接使用Map接受所有的参数
    @RequestMapping(value = "/test2",method = RequestMethod.GET)
    public String test2(@RequestParam Map map){
      return "test2";
    };

    required:是否必须,默认为true,如果请求中没有带参数,就报错,如果为false,默认值为null  

    defaultValue:设置默认参数值

  1.3 @RequestHeader(获取请求头)

    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public String test(@RequestHeader("Accept-Language") String al) throws IOException {
        System.out.println(al);
        return "login";
    }

    用法:和@RequestParam一样

  1.4 @CookieValue(获取cookie)

    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public String test(@CookieValue("JSESSIONID") String sessionid) throws IOException {
        System.out.println(sessionid);
        return "login";
    }

    用法:和@RequestParam一样

  1.5 使用POJO对象绑定请求参数(如果需要获取session,request等信息,直接注入到方法(只能是Controller)形参中)

    @RequestMapping(value = "user",method = RequestMethod.GET)
    public String user(HttpServletRequest httpServletRequest,User user){ //请求参数名会和pojo属性名字自动匹配,自动为该对象填充属性,支持级联属性
        String username = httpServletRequest.getParameter("username");
        System.out.println(username);
        System.out.println(user.getUsername());
        return "login";
    }

  1.6 使用Servlet原生API作为参数

HttpServletRequest
HttpServletResponse
HttpSession
java.security.Principal
Locale
InputStream
Outputstream
Reader
Writer

  1.7 Ant风格的URL

@RequestMapping(value = "/login/*/test",method = RequestMethod.GET)

    ?:匹配文件名中的一个字符
     * :匹配文件名中的任意字符
     **:匹配多层路径

  1.8 @PathVariable(url绑定的占位符)

@RequestMapping(value = "/login/{id}",method = RequestMethod.GET)
  public String test(@PathVariable(value = "id") int id) throws IOException {}

     spring3.0新增的功能,该功能让spring MVC想REST目标挺进过程中具有里程碑意义

  1.9 接受一个列表

<form>
    <input  name="1"/>
    <input  name="1"/>
</form>
public JsonResult userFileSelectType(Long[] name)

  2.0@Valid

@RestController
@RequestMapping("/user")
public class UserController {
    @PostMapping
    public User create (@Valid @RequestBody User user) {
        System.out.println(user.getId());
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        user.setId("1");
        return user;
    }
}


public class User {
    private String id;  
 
    @NotBlank(message = "密码不能为空")
    private String password;
}

  

限制 说明
@Null 限制只能为null
@NotNull 限制必须不为null
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future 限制必须是一个将来的日期
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Past 限制必须是一个过去的日期
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Past 验证注解的元素值(日期类型)比当前时间早
@NotEmpty 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

2、处理模型数据

  2.1 ModelAndView(数据放到request中)

    public ModelAndView user(){
        //将view放在ModelAndView()
        ModelAndView modelAndView = new ModelAndView("login");
        //给modelAndView添加属性,spring MVC会把model中的数据放到request域中
        modelAndView.addObject("data",new Date());
        return modelAndView;
    }

    html中取值 ${date}

  2.2 Map(或者Model,ModelMap类型参数,用法和Map一样,数据放到request中)

    public String  user(Map<String,String> map){
        map.put("user","小明");
        return "login";
    }

    html取值 ${user}

  2.3 @SessionAttribute()

//value=user:表示map中put的user同时添加到session中,types,表示只要put中的key是字符串,都添加到session中
@SessionAttributes(value = {"user"},types = {String.class})
@Controller
public class DemoController{
    @RequestMapping(value = "user",method = RequestMethod.GET)
    public String  user(Map<String,String> map){
        map.put("user","小明");
        return "login";
    }
}

    注意:该注解只能注解类,不能注解方法

3、@ModelAttribute(类似拦截器)

  3.1使用 @ModelAttribute 来修饰方法

    @ModelAttribute//如果没有此方法,当访问/user的时候,获取的user是一个空对象,而我们可以通过@ModelAttribute注解拦截请求,通过该Model给request请求域中额外添加属性
    public void userinit(@RequestParam(value = "id",required = false) String  id, Model model){
        if (id==null){
            User user = new User("小明");
            model.addAttribute("user",user);//注意key(user),要和user方法中的参数值的类型名字一样(第一个字符小写),注意和参数名无关,和参数类型有关
        }
    }
    @RequestMapping(value = "/user",method = RequestMethod.GET)
    public String user(User user){
        System.out.println(user);
        return "login";
    }

    注意:首先请求进入有@ModelAttribute注解的方法中,如果请求有id,此时user就是数据库中的数据查询的数据,再到/user方法中,此时的参数user就是之前的传入的user对象,如果是post请求,表单的有数据,此时会将表单中的属性一个一个替换之前user对象中属性。如果没有的属性,user对象就不会被替换。

  3.2 使用 @ModelAttribute 来修饰参数 (了解)

    默认情况下User xxx,会将 user 添加到Model中

    使用@ModelAttribute("myUser") User xxx,会将myUser添加到Model中

    @ModelAttribute
    public void userinit(@RequestParam(value = "id",required = false) String  id, Model model){
        if (id==null){
            User user = new User("小明");
            model.addAttribute("abc",user);
        }
    }
    @RequestMapping(value = "user",method = RequestMethod.GET)
    public String user(@ModelAttribute("abc") User xxx){ //html取值 ${requestScope.abc}
        System.out.println(xxx+"===");
        return "login";
    }

SpringMVC 确定目标方法POJ0类型入参的过程

    1.确定一个key:
    1)若目标方法的POJO类型的参数木有使用@ModelAttribute作为修饰,则key为POJO类名第一个字母的小写2).若使用了@ModelAttribute来修饰,则key为@ModelAttribute 注解的value属性值.
    2.在implicitModel中查找key对应的对象,若存在,则作为入参传入1).若在@Mode1Attribute 标记的方法中在Map 中保存过,且key 和1确定的key一致,则会获取到。
    3.若implicitModel中不存在key 对应的对象,则检查当前的 Handler是否使用@SessionAttributes注解修饰,若使用了该注解,且@SessionAttributes 注解的 value属性值中包含了key,则会从  HttpSession中来获取key所对应的value值,若存在则直接传入到目标方法的入参中。若不存在则将抛出异常。
    4.若Handler 没有标识@SessionAttributes 注解或@SessionAttributes 注解的value值中不包含key,则会通过反射来创建POJ0类型的参数,传入为目标方法的参数
    5.SpringMVC 会把key 和POJO类型对象保存到implicitModel中,进而会保存到request中。

4、mvc:view-controller标签

  在spring-MVC.xml配置中添加,表示,我需要经过Controller,就和直接访问jsp页面一样。

<mvc:view-controller path="/test/test" view-name="login"></mvc:view-controller>

5、重定向和请求转发

return "forward:/delete";   //请求转发
return "redirect:/delete"; //请求重定向

6、自定义视图(了解)

   创建视图

@Component //将MyView 放到ioc容器当中
public class MyView implements View {
    public String getContentType() {
        return "text/html";
    }
    public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        httpServletResponse.getWriter().write("Myview");
    }
}

  配置RequestMapping

    @RequestMapping(value = "viewtest",method = RequestMethod.GET)
    public String myviewtest(){
        return "myView"; //会从配置文件中的视图解析器创建视图实例(返回类的名字(首字母小写))
    }

  修改spring-MVC.xml

<!--将Component注解也添加到扫描中-->
    <context:component-scan base-package="com.zy.Controller" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>  <!--添加-->
    </context:component-scan>
    <!--配置视图解析器(BeanNameViewResolver):作用是使用视图名字来解析视图-->
    <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"> <!--再添加一个视图解析器,order越小,优先级越高,优先使用该解析器,在使用InternalResourceViewResolver(order:2147483647)--> 
    <property name="order" value="100"></property>
  </bean>

7、自定义类型转换器(了解)

  ConversionService 是Spring 类型转换体系的核心接口。

  可以利用 ConversionServiceFactoryBean在Spring的IOC容器中定义一个ConversionService,Spring 将自动识别出IOC容器中的 ConversionService,并在Bean 属性配置及Spring MVC处理方法入参绑定等场合使用它进行数据的转换
可通过ConversionServiceFactoryBean的 converters属性注册自走义的类型转换器。

配置自定义转换器

@Component
public class MyConversion implements Converter<String,User> {
    public User convert(String s) {
        System.out.println(s);
        User user=null;
        String[] split = s.split("-");
        if (split!=null){
            String username=split[0];
            String password=split[1];
            user = new User();
            user.setUsername(username);
            user.setPassword(password);
        }
        return user;
    }
}

spring-MVC.xml配置 

    <!-- 默认的注解映射的支持 -->
    <mvc:annotation-driven conversion-service="conversionServiceFactoryBean" />

    <bean >
        <property name="converters">
            <set>
                <ref bean="myConversion"></ref>
            </set>
        </property>
    </bean>

html

<form action="/useradd" method="post">
    <input type="text" name="user" value="zy-123456">  //user名字是Converter<String,User>,User小写
    <input type="submit" value="提交">
</form>

8、<MVC:annocation-driven>

<mvc:annotation-driven/>会自动注册 RequestMappingHandlerMapping、RequestMappingHandlerAdapter与ExceptionHandlerExceptionResolver 三个bean。
还将提供以下支持:
-支持使用ConversionService实例对表单参数进行类型转换
-支持使用@NumberFormat annotation、
@DateTimeFormat 注解完成数据类型的格式化
-支持使用@Valid 注解对JavaBean实例进行JSR303验证
-支持使用@RequestBody和@ResponseBody注解

9、@InitBinder

    @InitBinder
    public void initBinder(WebDataBinder webDataBinder){
        webDataBinder.setDisallowedFields("password","username");
    }

    作用:会忽略表单提交上来的某些属性,比如在@ModelAttribute中new User("zz",1234),此时表单中的数据就不会覆盖里面的值了

10、数据格式化  

spring-MVC.xml

<mvc:annotation-driven></mvc:annotation-driven>

如果还需要添加之前的自定义的类型转换器

    <mvc:annotation-driven conversion-service="conversionServiceFactoryBean" />
    <bean > //修改Bean
        <property name="converters">
            <set>
                <ref bean="myConversion"></ref>
            </set>
        </property>
    </bean>

实体类

@DateTimeFormat(pattern = "yyyy-MM-dd")  //格式化时间,会将 yyyy-MM-dd时间转化为Date
private Date date;

html(里面输入值必须是yyyy-MM-dd)

<form:input path="date"></form:input>

实体类

@NumberFormat(pattern = "###,###")   //格式化数字,同理
private Float salary;

  说明  

html页面将日期发送给controller,controller通过 IplogQueryObject对象来接收,需要指定IplogQueryObject中日期的获取格式,否则请求会失败(400)

Spring MVC----spring MVC配置+拦截器使用
 

Spring MVC----spring MVC配置+拦截器使用
 

Spring MVC----spring MVC配置+拦截器使用
 

 自定义拦截器使用:

拦截器

  Spring web MVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器 进行预处理和后处理。

  拦截器和过滤器

  使用范围不同:Filter是Servlet规范规定的,只能用于Web程序中。而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。  

  规范不同:Filter是在Servlet规范中定义的,是Servlet容器支持的。而拦截器是在Spring容器内的,是Spring框架支持的。  

  使用的资源不同:同其他的代码块一样,拦截器也是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过

loC注入到拦截器即可;而Filter则不能。

  深度不同:Filter在只在Servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在Spring构架的程序中,要优先使用拦截器。

                                                           Spring MVC----spring MVC配置+拦截器使用
 

  使用

  Spring MVC 拦截器需要实现 HandlerInterceptor 接口,该接口定义了 3 个方法,分别为 preHandle()postHandle() 和 afterCompletion(),咱们就是通过重写这 3 个方法来对用户的请求进行拦截处理的。

  • preHandle(HttpServletRequest request, HttpServletResponse response, Object handle):该方法在请求处理之前进行调用。Spring MVC 中的 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 渲染了对应的视图之后执行,这个方法的主要作用是用于进行资源清理的工作。

示例:

在web(和controller同级)目录下创建目录(interceptor)

在interceptor中创建拦截器(类需要继承 HandlerInterceptor 接口,实现接口的三个方法)

public class LoginInterceptor implements HandlerInterceptor {
    //调用方法之前:可以做权限管理,日志,事务等
   public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        User user = ((User) httpServletRequest.getSession().getAttribute("user"));
        //表示未登录
        if(user==null){
            System.out.println("未登录");
            httpServletResponse.sendRedirect("/login");
       return false;
        }
        //通过(放行)
        return true;
    }
  //调用方法之后,渲染视图之前:可以对请求域的属性或者视图做出修改
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }
  //渲染视图之后:释放资源
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

配置拦截器

在spring-context.xml 添加

<mvc:interceptors>
        <mvc:interceptor>
            <!-- /**:表示全路径拦截(子子孙孙)-->
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/static/**"/>
            <mvc:exclude-mapping path="/login"/> //以实际url请求为主,如果url是/login.do,那么path='/login.do'
            <bean class="com.zy.myshop.web.Interceptor.LoginInterceptor"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
           <mvc:mapping path="/login"/>     //LoginOrHomeInterceptor之拦截/login
           <bean class="com.zy.myshop.web.Interceptor.LoginOrHomeInterceptor"></bean>//注意这个是拦截器,不是controller
        </mvc:interceptor>
</mvc:interceptors>

拦截的顺序:对于preHandle,按照拦截器的配置顺序(上到下)执行,对于postHandle和afterCompletion按照反序(下到上)执行

正常拦截器的拦截顺序,如图:

Spring MVC----spring MVC配置+拦截器使用
 

如果第一个拦截器的preHandle通过,第二个拦截器的preHandle返回了false,那么会执行直接执行第一个拦截器的afterCompletion;

Spring MVC----spring MVC配置+拦截器使用