Spring MVC

MVC设计模式简介

  • MVC 是 Model、View 和 Controller 的缩写,分别代表 Web 应用程序中的 3 种职责
    • 模型:用于存储数据以及处理用户请求的业务逻辑
    • 视图:向控制器提交数据,显示模型中的数据
    • 控制器:根据视图提出的请求判断将请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示
  • 基于 Servlet 的 MVC 模式的具体实现如下
    • 模型:一个或多个 JavaBean 对象,用于存储数据(实体模型,由 JavaBean 类创建)和处理业务逻辑(业务模型,由一般的 Java 类创建)
    • 视图:一个或多个 JSP 页面,向控制器提交数据和为模型提供数据显示,JSP 页面主要使用 HTML 标记和 JavaBean 标记来显示数据
    • 控制器:一个或多个 Servlet 对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的 JavaBean,并将处理结果存放到实体模型 JavaBean 中,输出给视图显示
  • 基于 Servlet 的 MVC 模式的流程如下图
    在这里插入图片描述

Spring MVC框架

  • Spring MVC 框架主要由 DispatcherServlet、处理器映射、控制器、视图解析器、视图组成,其工作原理如下图所示
    在这里插入图片描述

  • 由上图可总结出 Spring MVC 的工作流程如下:

    1. 客户端请求提交到 DispatcherServlet
    2. 由 DispatcherServlet 控制器寻找一个或多个 HandlerMapping,找到处理请求的 Controller
    3. DispatcherServlet 将请求提交到 Controller
    4. Controller 调用业务逻辑处理后返回 ModelAndView
    5. DispatcherServlet 寻找一个或多个 ViewResolver 视图解析器,找到 ModelAndView 指定的视图
    6. 视图负责将结果显示到客户端
  • Spring MVC包含四个接口:DispatcherServlet、HandlerMapping、Controller 和 ViewResolver

    • Spring MVC 所有的请求都经过 DispatcherServlet 来统一分发,在 DispatcherServlet 将请求分发给 Controller 之前需要借助 Spring MVC 提供的 HandlerMapping 定位到具体的 Controller
    • HandlerMapping 接口负责完成客户请求到 Controller 映射
    • Controller 接口将处理用户请求,这和 Java Servlet 扮演的角色是一致的。一旦 Controller 处理完用户请求,将返回 ModelAndView 对象给 DispatcherServlet 前端控制器,ModelAndView 中包含了模型(Model)和视图(View)
    • 宏观角度考虑,DispatcherServlet 是整个 Web 应用的控制器;从微观考虑,Controller 是单个 Http 请求处理过程中的控制器,而 ModelAndView 是 Http 请求过程中返回的模型(Model)和视图(View)
    • ViewResolver 接口(视图解析器)在 Web 应用中负责查找 View 对象,从而将相应结果渲染给客户

在这里插入图片描述
在这里插入图片描述

Spring集成web环境

ApplicationContext应用上下文获取方式

  • 应用上下文对象是通过new ClasspathXmlapplicationContext(spring配置文件)方式获取的,但是每次从容器中获得Bean都要编写这句代码,这样的弊端是配置文件被加载多次,应用上下文对象创建多次
  • 在web项目中,可以使用ServletContextListener监听web应用的启动,在web应用启动时就加载spring的配置文件,创建应用上下文对象ApplicationContext,再将其存储到最大的域servletContext域中(每个 Web 应用创建一个唯一的 ServletContext 对象代表当前的 Web 应用,该对象封装了当前 Web 应用的所有信息),这样就可以在任意位置从域中获得应用上下文ApplicaitonContext对象

Spring提供获取应用上下文的工具

  • Spring提供了一个监听器ContextLoaderListener,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供一个客户端工具WebApplicationContextUtils供使用者获取应用上下文对象

  • 使用方法

    • 在web.xml中配置ContextLoaderListener监听器(需要导入spring-web坐标)
    • 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext

Spring MVC开发步骤

  • 开发需求:客户端发起请求,服务端接收请求,执行逻辑并进行视图跳转

  • 开发步骤

    • 导入Spring MVC相关坐标(spring-webmvc, spring-web)
    • 在 web.xml 中配置Spring MVC核心控制器DispatcherServlet
    • 创建Controller类和视图页面
    • 使用注解配置Controller类中业务方法的映射地址
    • 创建并配置Spring MVC核心文件spring-mvc.xml,主要用来配置 Controller 映射信息,然后DispatcherServlet使用 servlet 的 init-param 元素加载该配置文件
    • 客户端发起请求进行测试
  • Spring MVC流程图
    在这里插入图片描述

Spring MVC注解

  • 在 Spring MVC 中最重要的两个注解类型是 Controller 和 RequestMapping

@Controller

  • 使用基于注解的控制器具有以下两个优点

    • 在基于注解的控制器类中可以编写多个处理方法,进而可以处理多个请求(动作),这就允许将相关的操作编写在同一个控制器类中,从而减少控制器类的数量,方便以后的维护
    • 基于注解的控制器不需要在配置文件中部署映射,仅需要使用 RequestMapping 注释类型注解一个方法进行请求处理
  • 在 Spring MVC 中使用扫描机制找到应用中所有基于注解的控制器类,所以,为了让控制器类被 Spring MVC 框架扫描到,需要在spring-mvc.xml配置文件中声明 spring-context,并使用 <context:component-scan/> 元素指定控制器类的基本包(请确保所有控制器类都在基本包及其子包下)

@RequestMapping

  • 在基于注解的控制器类中可以为每个请求编写对应的处理方法。RequestMapping注解将请求与处理方法一一对应

  • RequestMapping有三个常用属性,如下所示

    属性作用
    value用于指定请求的URL,和path属性作用一样
    method用于指定请求的方式。如,method=RequestMethod.GET,表示浏览器只能用GET方法访问此方法
    params用于指定限制请求参数的条件。支持简单的表达式,要求请求参数的key和value必须和配置一模一样。如params={“accountName”},表示请求参数必须有accountName,params={“money!100”}表示请求参数中money不能为100
  • RequestMapping分为方法级别注解和类级别注解

  • 方法级别注解如下示例

    package controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    /**
    * “@Controller”表示 IndexController 的实例是一个控制器
    *
    * @Controller相当于@Controller(@Controller) 或@Controller(value="@Controller")
    */
    @Controller
    public class IndexController {
        @RequestMapping(value = "/index/login")
        public String login() {
            /**
             * login代表逻辑视图名称,需要根据Spring MVC配置
             * 文件中internalResourceViewResolver的前缀和后缀找到对应的物理视图
             */
            return "login";
        }
        @RequestMapping(value = "/index/register")
        public String register() {
            return "register";
        }
    }
    

    上述示例中有两个 RequestMapping 注解语句,它们都作用在处理方法上。注解的 value 属性将请求 URI 映射到方法,value 属性是 RequestMapping 注解的默认属性,如果只有一个 value 属性,则可以省略该属性

  • 用户可以使用如下 URL 访问 login 方法(请求处理方法),在访问 login 方法之前需要事先在 /WEB-INF/jsp/ 目录下创建 login.jsp

    http://localhost:8080/dmeo/index/login
    
  • 类级别注解示例

    package controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    @Controller
    @RequestMapping("/index")
    public class IndexController {
        @RequestMapping("/login")
        public String login() {
            return "login";
        }
        @RequestMapping("/register")
        public String register() {
            return "register";
        }
    }
    

    在类级别注解的情况下,控制器类中的所有方法都将映射为类级别的请求。为了方便维护程序,建议开发者采用类级别注解,将相关处理放在同一个控制器类中

  • 方法上标注的目录与类上标注的一级目录一起组成访问虚拟路径

处理器(Handler)

  • 在控制类中每个请求处理方法可以有多个不同类型的参数,以及一个多种类型的返回结果
  • 请求处理方法中常出现的参数类型
    • 如果需要在请求处理方法中使用 Servlet API 类型,如HttpSession、HttpServletRequest和HttpServletResponse等,那么可以将这些类型作为请求处理方法的参数类型
    • 除了 Servlet API 参数类型以外,还有输入输出流、表单实体类、注解类型、与 Spring 框架相关的类型
    • 特别重要的类型是 org.springframework.ui.Model 类型,该类型是一个包含 Map 的 Spring 框架类型在每次调用请求处理方法时 Spring MVC 都将创建 org.springframework.ui.Model 对象
  • 请求处理方法常见返回类型
    • 常见的返回类型就是代表逻辑视图名称的 String 类型,除了 String 类型以外,还有 ModelAndView、Model、View 以及其他任意的 Java 类型

Spring MVC视图解析器(ViewResolver)

  • 用户可以在spring-mvc.xml配置文件中定义 Spring MVC 的一个视图解析器(ViewResolver),示例代码如下
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
    
  • InternalResourceViewResolver 是 URLBasedViewResolver 的子类,所以 URLBasedViewResolver 支持的特性它都支持。在实际应用中 InternalResourceViewResolver 也是使用的最广泛的一个视图解析器
  • 视图解析器可以使Controller请求处理方法返回的视图路径仅需提供视图文件名称,视图解析器将会自动添加前缀和后缀。如上示例,若请求处理方法返回"login"字符串,则 InternalResourceViewResolver 就会把 login 解析为一个 InternalResourceView 对象,先把返回的模型属性都存放到对应的 HttpServletRequest 属性中,然后利用 RequestDispatcher 在服务器端把请求 forword 到 /WEB-INF/login.jsp

Spring MVC转发与重定向

  • 重定向是将用户从当前处理请求定向到另一个视图(例如 JSP)或处理请求,以前的请求(request)中存放的信息全部失效,并进入一个新的 request 作用域

  • 转发是将用户对当前处理的请求转发给另一个视图或处理请求,以前的 request 中存放的信息不会失效

  • 转发是服务器行为,重定向是客户端行为

  • 转发过程

    • 客户浏览器发送 http 请求,Web 服务器接受此请求,调用内部的一个方法在容器内部完成请求处理和转发动作,将目标资源发送给客户;在这里转发的路径必须是同一个 Web 容器下的 URL,其不能转向到其他的 Web 路径上,中间传递的是自己的容器内的 request
    • 在客户浏览器的地址栏中显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求
  • 重定向过程

    • 客户浏览器发送 http 请求,Web 服务器接受后发送 302 状态码响应及对应新的 location 给客户浏览器,客户浏览器发现是 302 响应,则自动再发送一个新的 http 请求,请求 URL 是新的 location 地址,服务器根据此请求寻找资源并发送给客户
    • 在这里 location 可以重定向到任意 URL,既然是浏览器重新发出了请求,那么就没有什么 request 传递的概念了。在客户浏览器的地址栏中显示的是其重定向的路径,客户可以观察到地址的变化。重定向行为是浏览器做了至少两次的访问请求
  • 在 Spring MVC 框架中,不管是重定向或转发,都需要符合视图解析器的配置,如果直接转发到一个不需要 DispatcherServlet 的资源,例如:

    return "forward:/html/index.html";
    

    则需要使用mvc:resources配置

    <mvc:resources location="/html/" mapping="/html/**" />
    

Spring MVC数据响应方式

  • 数据响应方式主要分为如下两类
    • 页面跳转
      • 直接返回字符串
      • 通过ModelAndView对象返回
    • 回写数据
      • 直接返回字符串
      • 返回对象或集合

页面跳转

  • 返回字符串形式
    • 直接返回字符串,此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转
    • 注意:存放在 /WEB-INF/ 下面的内容是不能直接通过 request 请求的方式请求到的,为了安全性考虑,通常把 jsp 文件放在 WEB-INF 目录下,而 InternalResourceView 在服务器端跳转的方式可以很好的解决这个问题
    • 在 Spring MVC 框架中,控制器类中处理方法的 return 语句默认就是转发实现,只不过实现的是转发到视图
    • 若要使用重定向,则需要显式指明,如重定向到index.jsp视图:
      return "redirect:test" 
      
  • 返回ModelAndView
    • Model:模型,作用:封装数据
    • View:视图,作用:展示数据
    • 示例
      @RequestMapping("/test")
      public ModelAndView test(){
          ModelAndView modelAndView = new ModelAndView();
          modelAndView.addObject("username", "xxx");
          modelAndView.setViewName("test");
          return modelAndView;
      }
      
      也可以通过通过参数传入:
      @RequestMapping("/test")
      public ModelAndView test(ModelAndView mv){
      	mv.addObject("username", "xxx"); 
      	mv.setViewName("test");	
      	return mv;
      }
      
      @RequestMapping("/test")
      public String test(Model model){
      	model.addObject("username", "xxx"); 
      	return "test";
      }
      
      // spring mvc框架不常用形式,它已经帮用户封装好Servlet相关接口
      @RequestMapping("/test")
      public String test(HttpServletRequest req){
          req.setAttribute("username", "zzz");
          return "test";
      }
      

回写数据

  • 直接返回字符串

    • web基础阶段,客户端访问服务端,如果想直接回写字符串作为响应体返回的话,只需要使用response.getWriter().print(“hello world”)即可
    • 在Spring MVC框架中,可以通过注入HttpServletResponse对象,使用response.getWriter().print(“hello world”)回写数据,此时不需要视图跳转,业务方法返回值为void
    • 可以将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知Spring MVC框架,方法返回的字符串不是跳转而是直接在http响应体中返回。示例如下
      @RequestMapping("/test")
      @ResponseBody
      public String test(){
          return "test test test";
      }
      
    • 使用json转换工具将对象转化成json格式字符串再回写
      <!--pom.xml导入json转换依赖包-->
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
      </dependency>
      
      @RequestMapping("/test")
      @ResponseBody
      public String test() throws JsonProcessingException {
      	User user = new User();
      	user.setUsername("xxx");
      	user.setPasswd("123");
      	
      	ObjectMapper objectMapper = new ObjectMapper();
      	String s = objectMapper.writeValueAsString(user);
      	return s;
      }
      
  • 返回对象或集合

    • 这种方法需要通过spring mvc提供的映射器将返回的对象和集合映射成json格式字符串并作为消息体回写

    • spring-mvc.xml配置映射器

      <!--配置处理器映射器-->
      <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
          <property name="messageConverters">
              <list>
                  <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
              </list>
          </property>
      </bean>
      
    • 代码可以直接返回对象或集合

      @RequestMapping("/test")
      @ResponseBody
      public User test(){
          User user = new User();
          user.setUsername("xxx");
          user.setPasswd("123");
          return user;
      }
      
    • 上述方法配置比较麻烦,配置代码多,spring mvc提供了注解驱动可以代替上述配置

    • 在spring mvc的各个组件中,处理器映射器、处理器适配器和视图解析器称为spring mvc三大组件

      • 使用<mvc:annotation-driven>自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHadlerAdapter(处理适配器),替代sprng-mvc.xml中的配置
      • 使用<mvc:annotation-driven>底层会集成jackson进行对象或集合的jason格式字符串的转换
    • spring-mvc.xml中注解驱动

      <!--引入mvc命名空间-->
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd
      
      <!--注解驱动-->
      <mvc:annotation-driven/>
      

Spring MVC获得请求数据

  • 客户端请求参数的格式是:name=value&name=value
  • Spring MVC可以接收以下类型的参数
    • 基本类型参数
    • POJO参数
    • 数组类型参数
    • 集合类型参数

获得基本类型请求参数

  • 获得基本类型请求参数,通过http://localhost:8080/test?username=xxx&age=18访问
    @RequestMapping("/test")
    @ResponseBody
    public void test(String username, int age){
        System.out.println("username = " + username + ", age = " + age);
    }
    

获得POJO类型参数

  • 获得POJO类型参数:Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。通过与上例同样的url访问
    // User为一个包含username和age属性的pojo类
    @RequestMapping("/test")
    @ResponseBody
    public void test(User user){
        System.out.println(user);
    }
    

获取数组类型参数

  • 获取数组类型参数:Controller中的业务方法数组名称与请求参数一致,参数值会自动映射匹配。通过http://localhost:8080/test?strs=111&strs=222&strs=333访问
    @RequestMapping("/test")
    @ResponseBody
    public void test(String [] strs){
        for (String str : strs) {
            System.out.println(str);
        }
    }
    

获得集合类型参数

  • 获得集合类型参数
    • 要将集合参数包装到一个POJO中才可以。通过post表单提交数据

      <html>
      <body>
      <form action="${pageContext.request.contextPath}/test10">
          first username: <input type="text" name="userList[0].username"><br/>
          first password: <input type="text" name="userList[0].passwd"><br/>
          second username: <input type="text" name="userList[1].username"><br/>
          second password: <input type="text" name="userList[1].passwd"><br/>
          <input type="submit" value="submit">
      </form>
      </body>
      </html>
      
      package com.ccq.pojo;
      import java.util.List;
      
      public class VO {
          private List<User> userList;
      
          public List<User> getUserList() {
              return userList;
          }
      
          public void setUserList(List<User> userList) {
              this.userList = userList;
          }
      
          @Override
          public String toString() {
              return "VO{" +
                      "userList=" + userList +
                      '}';
          }
      }
      
      @RequestMapping("/test")
      @ResponseBody
      public void test(VO vo){
          System.out.println(vo);
      }
      
    • 使用ajax提交时,可以指定contentType为json形式,在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO封装

      @RequestMapping("/test")
      @ResponseBody
      public void test(@RequestBody List<User>userList){
          System.out.println(userList);
      }
      

参数绑定注解@RequestParam

  • 当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显式绑定

    @RequestMapping("/test")
    @ResponseBody
    // 将请求参数名称name绑定到username
    public void test(@RequestParam(value = "name") String username){
    	  System.out.println(username);
    }
    
  • 注解@RequestParam有如下参数可以使用

    • value:请求参数名称
    • required:指定此请求参数是必须包括,默认为true,提交时如果没有此参数将报错
    • defaultValue:当没有指定请求参数时,使用指定的默认值赋值

获得Restful风格的参数

  • Restful是一种软件架构风格设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等

  • Restful风格的请求是使用“url+请求方式”表示一次请求目的的,HTTP协议里面四个表示操作方式的动词如下:

    • GET:用于获取资源
    • POST:用于新建资源
    • PUT:用于更新资源
    • DELETE:用于删除资源
  • 例如

    • /user/1 GET:得到id=1的user
    • /user/1 DELETE:删除id=1的user
    • /user/1 PUT:更新id=1的user
    • /user POST:新增user
  • 上述url地址/user/1中的1就是要获得的请求参数,在Spring MVC中可以使用占位符进行参数绑定。地址/user/1可以写成/user/{id},占位符{id}对应的就是1的值。在业务方法中可以使用@PathVariable注解进行占位符的匹配获取工作

    @RequestMapping("/test/{username}")
    @ResponseBody
    public void test(@PathVariable(value = "username", required = true) String username){
    	  System.out.println(username);
    }
    

    这个例子可以通过以下url访问。不同的操作方式的处理方法可以通过@RequestMapping的method属性区分

    http://localhost:8080/test/tom
    

获得请求头

  • @RequestHeader

    • 使用@RequestHeader可以获得请求头信息,相当于JavaWeb阶段学习的request.getHeader(String name)
    • @RequestHeader注解的属性如下
      • value:请求头名称

      • required:是否必须携带此请求头

        @RequestMapping("/test")
        @ResponseBody
        public void test(@RequestHeader(value = "User-Agent", required = false) String headerValue){
        	  System.out.println(headerValue);
        }
        
  • CookieValue

    • 使用@CookieValue可以获得指定的Cookie值
    • @CookieValue注解属性如下
      • value:指定cookie的名称

      • required:是否必须携带此cookie

        @RequestMapping("/test")
        @ResponseBody
        public void test(@CookieValue(value = "JSESSIONID", required = false) String sessionId){
        	  System.out.println(sessionId);
        }
        

类型转换器

内置类型转换器

  • Spring MVC 框架的 Converter<S,T>是一个可以将一种数据类型转换成另一种数据类型的接口,这里 S 表示源类型,T 表示目标类型。开发者在实际应用中使用框架内置的类型转换器基本上就够了,但有时需要编写具有特定功能的类型转换器
  • 在 Spring MVC 框架中,对于常用的数据类型,开发者无须创建自己的类型转换器,因为 Spring MVC 框架有许多内置的类型转换器用于完成常用的类型转换。Spring MVC 框架提供的内置类型转换包括以下几种类型
    • 标量转换器
      在这里插入图片描述
    • 集合、数组相关转换器
      在这里插入图片描述
  • 类型转换是在视图与控制器相互传递数据时发生的。Spring MVC 框架对于基本类型(例如 int、long、float、double、boolean 以及 char 等)已经做好了基本类型转换
  • 在使用内置类型转换器时,请求参数输入值与接收参数类型要兼容,否则会报 400 错误

自定义类型转换器

  • 不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器
  • 自定义类型转换器的开发步骤
    • 定义转换器类实现Converter接口
    • 在spring-mvc.xml配置文件中声明转换器
    • 在<annotation-driven>中引用转换器

解决请求数据乱码问题

  • 当post请求时,中文数据可能出现乱码,类似于Servlet中的处理方式,可以设置一个过滤器来进行编码过滤。Spring MVC已经封装好解决乱码的Filter,直接配置使用即可

    <!--过滤器,解决中文编码乱码问题-->
    <filter>
        <filter-name>CharacterEncodingFilter</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>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

Spring MVC开放访问静态资源的方式

  • Spring MVC想要访问静态资源,如css、js、html、images等,需要在spring-mvc.xml文件中配置静态文件路径和映射

    <!-- 允许css目录下的所有文件可见 -->
    <mvc:resources location="/static-resources/css/" mapping="/css/**" />
    <!-- 允许js目录下的所有文件可见 -->
    <mvc:resources location="/static-resources/js/" mapping="/js/**" />
    <!-- 允许fonts目录下的所有文件可见-->
    <mvc:resources location="/static-resources/fonts/" mapping="/fonts/**" />
    <!-- 允许static-resources目录下的所有文件可见-->
    <mvc:resources location="/static-resources/" mapping="/**" />
    <!-- 允许images目录下的所有文件可见 -->
    <mvc:resources location="/static-resources/images/" mapping="/images/**" />
    
  • 或简单配置使用原始Servlet而不是DispatcherServlet处理静态资源

    <!--交给原始Servlet处理其他资源-->
    <mvc:default-servlet-handler/>
    

文件上传

  • 文件上传三要素

    • 表单项type=“file”
    • 表单的提交方式是post
    • 表单的enctype属性是多部分表单形式,及enctype="multipart/form-data"
  • 文件上传原理

    • 当form表单修改为多部分表单multipart/form-data时,request.getParameter()失效
    • enctype="application/x-www-form-urlencoded"时,form表单的正文内容格式为key1=value1&key2=value2
    • 当form表单的enctype取值为multipart/form-data时,请求正文内容就变成多部分形式
      在这里插入图片描述
  • 服务端处理单文件上传步骤

    • 导入fileupload和io坐标

      <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
      </dependency>
      
      <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
      </dependency>
      </dependencies>
      
    • 在spring-mvc.xml配置文件上传解析器

      <!--配置文件上传解析器-->
      <bean name="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
          <!--上传文件总大小-->
          <property name="maxUploadSize" value="5000000"/>
          <!--上传单个文件大小-->
          <property name="maxUploadSizePerFile" value="5000000"/>
          <!--上传文件的编码类型-->
          <property name="defaultEncoding" value="UTF-8"/>
      </bean>
      
    • 编写文件上传代码

      @RequestMapping("/test11")
      @ResponseBody
      public void test11(String filename, MultipartFile uploadedFile, HttpServletRequest req) throws IOException {
          // 原始文件名
          String originalFilename = uploadedFile.getOriginalFilename();
          // 保存到服务器本地
          String path = req.getServletContext().getRealPath("/uploadedFiles");
          uploadedFile.transferTo(new File(path+ File.separator + originalFilename));
      }
      
  • 多文件上传与单文件上传类似,只需将MultipartFile参数改成数组类型MultipartFile [] uploadedFile

Spring MVC拦截器(Interceptor)

  • 拦截器作用:类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理

  • 将拦截器按一定顺序链接成一条链,这条链成为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按链接的顺序被调用。拦截器也是AOP思想的具体体现

  • 拦截器和过滤器的区别

    区别过滤器拦截器
    使用范围Servlet规范的一部分,任何JavaWeb工程都可以使用使用Spring MVC框架的工程才能使用
    拦截范围在url-pattern配置了/*之后,可以对所有要访问的资源拦截只会拦截要访问的控制器方法,如果访问的是jsp,html,css,image或者js是不会进行拦截的
  • 拦截方法说明

    方法名说明
    preHandler()方法将在请求处理之前进行调用,该方法的返回值是布尔类型。当它返回false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回true时就会继续调用下一个Interceptor的preHandle方法
    postHandler()该方法时在当前请求进行处理之后被调用,前提是preHandler方法的返回值为true时才能被调用,且它会在DispacherServlet进行视图返回渲染之前被调用,所有可以用这个方法对Controller处理之后的ModelAndView对象进行操作
    afterCompletion()该方法将在整个请求结束之后,也就是DispatcherServlet渲染了对应的视图之后执行,前提是preHandler方法的返回值为true才能被调用。可以通过此方法实现一些资源清理、记录日志信息等工作
  • 单个拦截器的执行流程

    • 在配置文件中如果只定义了一个拦截器,程序将首先执行拦截器类中的 preHandle 方法,如果该方法返回 true,程序将继续执行控制器中处理请求的方法,否则中断执行。如果 preHandle 方法返回 true,并且控制器中处理请求的方法执行后、返回视图前将执行 postHandle 方法,返回视图后才执行 afterCompletion 方法
  • 多个拦截器的执行流程

    • 在 Web 应用中通常需要有多个拦截器同时工作,这时它们的 preHandle 方法将按照配置文件中拦截器的配置顺序执行,而它们的 postHandle 方法和 afterCompletion 方法则按照配置顺序的反序执行
  • 自定义拦截器步骤

    • 创建拦截器类实现HandlerInterceptor接口
      package com.ccq.interceptor;
      
      import org.springframework.web.servlet.HandlerInterceptor;
      import org.springframework.web.servlet.ModelAndView;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      public class InterceptorDemo implements HandlerInterceptor {
          @Override
          // 在目标方法前执行
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("preHandle done...");
              // 返回值如果为false,则请求会被拦截于此,true则继续传递
              String enter = request.getParameter("enter");
              if(enter.equals("yes"))
                  return true;
              else{
              	response.setContentType("text/html;charset=utf-8");
          		response.getWriter().write("no access!");
          		return false;
              }
          }
      
          @Override
          // 在目标方法执行之后,ModelAndView返回之前执行
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
          	modelAndView.addObject("username", "xxx");
              System.out.println("postHandle done...");
          }
      
          @Override
          // 在目标方法执行完毕且视图已经返回后执行
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("afterCompletion done...");
          }
      }
      
    • 配置拦截器
      <!--配置拦截器-->
      <mvc:interceptors>
          <mvc:interceptor>
              <!--指定对哪些资源的访问会被此拦截器拦截-->
              <mvc:mapping path="/**"/>
              <bean class="com.ccq.interceptor.InterceptorDemo"/>
          </mvc:interceptor>
      </mvc:interceptors>
      
    • 测试拦截器效果
      @RequestMapping("/test")
      public ModelAndView test(ModelAndView mv){
          mv.setViewName("test");
          System.out.println("test done");
          return mv;
      }
      

Spring MVC异常处理机制

  • 在 Spring MVC 应用的开发中,不管是对底层数据库操作,还是业务层或控制层操作,都会不可避免地遇到各种可预知的、不可预知的异常需要处理

  • 如果每个过程都单独处理异常,那么系统的代码耦合度高,工作量大且不好统一,以后维护的工作量也很大

  • 如果能将所有类型的异常处理从各层中解耦出来,这样既保证了相关处理过程的功能单一,又实现了异常信息的统一处理和维护

  • 异常处理思路

    • 系统中异常包含两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生
    • 系统的Dao、Service、Controller出现的异常都通过throws Exception向上抛出,最后由Spring MVC前端控制器交给异常处理器进行统一异常处理
      在这里插入图片描述
  • Spring MVC 统一异常处理有以下 3 种方式

    • 使用 Spring MVC 提供的简单异常处理器SimpleMappingExceptionResolver
    • 实现 Spring 的异常处理接口 HandlerExceptionResolver 自定义自己的异常处理器
    • 使用 @ExceptionHandler 注解实现异常处理
  • 使用SimpleMappingExceptionResolver

  • 自定义异常处理接口

  • 使用@ExceptionHandler 注解

JSON

  • JSON(JavaScript Object Notation,JS对象标记)是一种轻量级的数据交换格式,目前使用特别广泛

  • 采用完全独立于编程语言的文本格式来存储和表示数据

  • 简洁和清晰的层次结构是的JSON成为理想的数据交换语言

  • 已于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率

  • 在JavaScript语言中,一切都是对象。因此,任何JavaScript支持的类型都可以通过JSON来表示,例如字符串、数字、对象、数组等

  • JSON语法格式

    • 对象表示为键值对,数据由逗号分隔
    • 花括号保存对象
    • 方括号保存数组
  • JSON键值对是用来保存JavaScript对象的一种方式,和JavaScript对象的写法大同小异,键/值对组合中的键名卸载前面并用双引号包裹,使用冒号分隔,然后紧接值

    {"name": "xxx"}
    {"age": "18"}
    {"sex": "男"}
    
  • JSON是JavaScript对象的字符串表示法,它使用文本表示一个JS对象的信息,本质是一个字符串

var obj = {a: 'hello', b: 'world'}; // 这是一个JS对象,注意键名也是可以使用引号包裹的
var json = '{"a": "hello", "b": "world"}' // 这是一个json字符串,本质是一个字符串
  • JSON与JavaScript对象互转
    • 要实现从JSON字符串转转换为JavaScript对象,使用JSON.parse()方法:
      var obj = JSON.parse('{"a":"hello", "b":"world"}');
      // 结果为 {a: 'hello', b: 'world'}
      
    • 要实现从JavaScript对象转换成JSON字符串,使用JSON。使用JSON.stringify()方法:
var json = JSON.stringify({a: 'hello', b: 'world'});
// 结果为'{"a": "hello", "b": "world"}'
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值