Spring MVC学习

目录大纲

• Spring MVC的介绍
• Spring MVC的运行原理
• 各个组件介绍:前端控制器,处理器适配器,处理器映射器,视图解析器
• Spring MVC的使用Demo
• 处理器和映射器的配置(xml配置和注解)
• Spring MVC和Mybatis的整合(SSM整合)
• 方法入参和返回值的详解
• 图片上传功能(大文件处理的问题:图片,视频,音频)
• JSON格式的数据传输
• 异常处理机制
• RESTFul风格的接口
• 拦截器

Spring MVC的介绍

Java EE体系结构包括四层,从上到下应用层,web层,业务层,持久层,Struts和Spring MVC是web层的框架,Spring是业务层的框架,Hibernate和Mybatis是持久层框架
Spring MVC是一种基于Java,实现了Web的MVC设计模式,请求驱动类型的轻量级web框架,即使用了MVC架构模式的思想,将Web层进行指责解耦,基于请求驱动指的就是请求响应模型,
框架存在的目的是帮助简化开发,Spring MVC简化web开发

MVC模式的演进

在早期的Java Web的开发中,统一将显示层,控制层、数据层的操作交给JSP或者是JavaBean来进行处理
在这里插入图片描述
早期的web开发的交互图存在大的弊端
JSP和JavaBean之间严重耦合,Java代码和HTML代码也耦合
开发中不经要掌握Java,还需要掌握前端技术
前后端存在相互依赖,前端需要等待后端完成,后端也依赖前端完成,才能进行有效的测试,代码难以复用的(前后端分离)
很快提出了新的方式:servlet+Jsp_JavaBean所替代,早期的MVC模式演进如下:
在这里插入图片描述
用户请求首先到达servlet,然后根据请求调用相应的Java Bean,将结果显示交给JSP去完成,这样的模式成为MVC模式
M代表模型(Model)
模型就是数据,就是dao、bean
V代表视图(View)
就是页面、JSP用来展示模型中的数据
C代表控制器(Controller)
控制器就是把不同的数据(Model),显示在不同的视图上(View),servlet扮演控制器的角色
在Spring中给出的MVC的方法如下:
在这里插入图片描述
传统的模型层被差分成业务层(Service)和数据访问层(Dao),在service下可以通过Spring的声明式操作数据访问层
特点:
结构松散:几乎所有的SpringMBC使用各类视图
松耦合,各个模块分离
与Spring无缝集成

Spring 架构

在这里插入图片描述
Spring MVC是属于SPring的一个模块
SpringMVC和Spring无序通过中间整合即可以进行使用
Spring MVC 是一个基于MVC的Web框架

Spring MVC的运行原理

在这里插入图片描述
具体流程:
第一步:用户发起request请求(URL)到前端控制器DispatcherServlet,并且URL会被封装为Handler
第二步:前端控制器请求处理映射器HandlerMapping查找Handler,可以通过注解、xml进行查找
第三步:处理器映射器向前端控制器返回映射结果:处理器映射器链HandlerExecutionChain对象,包含一个Handler处理器对象、多个HandlerInterceptor拦截器对象
第四步:前端控制器请求处理器处理器适配器HandlerAdapter请求执行Handler
第五步:处理器适配器执行Handler
第六步:处理器执行完返回给适配器ModelAndView对象,ModelAndView是一个SPring MVC底层的对象,包含了Model和View,Model是业务对象的返回的模型数据,View是逻辑视图名
第七步:处理器适配器返回给前端控制器ModelAndView对象
第八步:前端控制器请求视图解析器ViewResolver解析视图,视图解析器将逻辑视图名解析为具体的View(jsp…)
第九步:视图解析器向前端控制器返回View
第十步:前端控制器进行页面渲染,视图渲染将模型数据(ModelAndView对象)填充到response域
第十一步:前端控制器向用户响应结果

Spring MVC各组件的介绍

1.前端控制器DispatcherServlet

由框架提供,不需要开发人员开发
Spring MVC的入口函数,接收请求,响应结果,相当于转发器,中央处理器,有了DispatcherServlet减少其他组件之间的耦合度。它相当于MVC模型中的C(控制器)

2.处理器映射器HandlerMapping

由框架提供,不需要开发人员开发
根据请求URL查找Handler,即处理器(Controller),Spring MVC提供不同的映射器实现不同的映射方式,例如:配置文件方法,实现接口方式,注解方式等…

3.处理器适配器HandlerAdapter

由框架提供,不需要开发人员开发
按照特定的规则(HandlerAdapter要求的规则)去执行Handler,进而通过适配器适配到真正执行的处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行

4.处理器Handler

需要开发人员进行开发
按照HandlerAdapter的要求进行编写,这样适配器才可以正确的找到执行Handler,处理器是后端控制器。由于Handler处理用户的具体业务逻辑,一般情况下需要开发人员根据业务进行开发

5.视图解析器ViewResolver

由框架提供,不需要开发人员开发
进行视图解析,根据逻辑视图名解析成真正的视图View。首先根据逻辑视图名解析成物理视图名即具体的页面地址,在生成View的视图对象,最后将View进行渲染将处理结果通过页面展示给用户,Spring MVC框架提供和诺的View视图类型,包括jstlView,freemarkerView,pdfView…等,一般情况下需要通过页面标签或页面模板的技术将模型数据通过页面展示给用户

6.视图View

需要开发人员进行开发
View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)

Spring MVC的使用步骤

一.创建Spring MVC的项目

通过maven创建一个webapp
在这里插入图片描述

创建好的项目目录如下,并且所有的前端页面均放在webapp下:
在这里插入图片描述

二.引入SpringMVC 依赖jar包

通过maven管理jar,将依赖添加到pom.xml文件中

<!--Spring MVC依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

SpringMVC是SPring的一部分,所以Spring MVC运行时需要Spring的依赖完整的依赖
完整依赖如下:
在这里插入图片描述

三.配置前端控制器(在web.xml配置文件中)

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <!--配置前端控制器-->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <!--
        第一种:*.action或者*.do:访问是以*.action或者*.do结尾的,由DispatcherServlet进行解析
        第二种:/:所有的URL都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet解析(css\js),
        使用此方式可以实现RESTFul风格的URL(以第二种使用方法为主)
        第三种:/*:使用这种配置,最终要转发到一个jsp页面是:任然会有DispatcherServlet进行jsap地质,不能通过jsp页面扎到Handler,会报错
        -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

servlet-name相当于命名标签,可以随便给但最好有意义些,servlet-class为通过maven导入的相应jar包里,DispatcherServlet类的全限定名(即路径),打开DispatcherServlet的源码在其构造函数上你可以通过注释看到一些相关的配置方法
注意:前端控制器是需要使用org.springframework.web.servlet.DispatcherServlet,即该拦截器将所有的请求统一交给DispatcherServlet处理(spring-webmvc)

三.创建Spring-mvc.xml配置文件以及其他目录结构

在main包下创建resource资源目录,再其下创建spring-mvc.xml配置文件,配置映射器,适配器,解析器等
在main目录下创建我们用来写代码的java包并将其设置为Sources Root包,在com包下创建contrller包
在这里插入图片描述

<?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-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
    <!--配置Spring MVC的需要扫描注解的包路径-->
     <context:component-scan base-package="com.tulun.contrller"/>
    <!--处理器映射器,适配器采用Spring MVC提供的默认的处理器处理,不需要显性的配置-->
    <mvc:annotation-driven/>
    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--配置前缀-->
    <property name="prefix"  value="/WEB-INF/jsp/"/>
    <!--配置后缀-->
    <property name="suffix"  value=".jsp"/>
   </bean>
</beans>

注意:将spring-mvc.xml文件的加载需要在web.xml文件中配置。
在新版中直接配置mvc:annotation-driven/开启springmvc框架注解支持即可打开处理映射器和处理适配器

四.配置前端控制器(在web.xml中)

在这里插入图片描述
servlet-name即控制器的名称,你可以随便起但还是尽量有意义些
servlet-class为DispatcherServlet类在导入的jar包中的绝对路径
init-param即初始化信息:param-name的名字不能随便起,只能用contextConfigLocation作为参数,代表指定springmvc配置的加载位置
param-name需要指定出spring-mvc.xml配置文件的路径
//如果不去配置contextConfigLocation则会有图示中的默认加载位置
也可以去配置load-on-start标签控制DispatcherServlet的优先级

五.编写Handler(Controller层)

Controller好像是依赖注入的注解?
RequestMapping为url的注解,方法为返回的页面,而此页面需要放到WEB-INF下,比如tulun是个jsp页面,那可以再单独创建一个jsp包放在其下,springmvc.xml配置文件中的前缀后缀即这里的路径,因为你如果全部是jsp页面,全部都放在WEB-INF下的话那路径的前缀和后缀都是一样的

/*
处理器
 */
@Controller
public class ControllerDemo {
    @RequestMapping("/tulun")
    public String tulun() {
        //返回ModelAndView对象
        //View的逻辑视图名:"/Web-INF/jsp/tulun.jsp"
        return "tulun";
    }
}

虽然写的是返回tulun,但实际返回值是其路径

六.编写View(JSP页面)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    </head>
    <body>
      <h1 >Hello Tulun</h1>
    </body>
</html>

在这里插入图片描述
部署服务
web代码的执行需要部署到服务器上,常见的服务器由Tomcat、Jetty,Jetty属于轻量级的服务器,在idea中直接使用插件的形式来添加jetty服务器
在全局配置文件pom.xml中的build标签中给定Jetty插件

<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>maven-jetty-plugin</artifactId>
    <version>6.1.24</version>
    <configuration>
        <connectors>
            <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                <!--端口号-->
                <port>8080</port>
                <maxIdleTime>30000</maxIdleTime>
            </connector>
        </connectors>
        <contextPath>/</contextPath>
    </configuration>
</plugin>

在这里插入图片描述

七.启动过程:

在这里插入图片描述

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

启动服务器

在浏览器上输入URL:localhost:8088/tulun
在这里插入图片描述
如果显示以上信息,说明用户请求的整个过程没有问题,获取到了响应结果

入门案例执行流程分析

  1. 当启动Tomcat服务器的时候,web.xml配置了load-on-startup标签,所以会创建DispatcherServlet对象,
    就会加载其init-param标签下的springmvc.xml配置文件
  2. springmvc.xml中开启了注解扫描,那么会将自己写的带@Controller注解的HelloController类的对象就会被创建到IOC容器中默认为单例
  3. 从index.jsp发送请求,请求会先到达DispatcherServlet核心控制器被拦截下,其中web.xml中的servlet-mapping标签的url-pattern就可以配置拦截的信息情况,DispatcherServlet根据配置@RequestMapping注解上的path路径找到执行的具体方法
  4. 根据执行方法的返回值,再根据web.xml配置的视图解析器,去指定的目录下查找指定名称的JSP文件
  5. Tomcat或Jetty等服务器渲染页面,做出响应

RequestMapping注解

  1. RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系,可以作用在方法和类上,在方法上即根路径,在类上为子路径
    细节:(1).路径可以不编写 / 表示应用的根目录开始
    (2). 细节:${ pageContext.request.contextPath }也可以省略不写,但是路径上不能写 /

  2. 在请求中@RequestMapping注解的作用有两点:
    • 窄化请求URL
    在类上添加@RequestMapping注解,对URL进行分类管理,可以在这里定义根路径,最终的访问URL是跟路径+子路径
    • 限制HTTP的请求方法
    @RequestMapping( value = “userList”,method = RequestMethod.POST),如果限制请求为POST请求是,请求getqingqiu则报错

  3. RequestMapping的属性
    (1). path 指定请求路径的url
    (2). value value属性和path属性是一样的
    (3). mthod 指定该方法的请求方式
    (4). params 指定限制请求参数的条件
    (5). headers 发送的请求中必须包含的请求头

Spring MVC 参数绑定

参数绑定,简单来说就是客户端发送请求,而请求中包含一些数据,数据到达Controller,就是参数绑定的过程
参数绑定过程
在Spring MVC中,提交请求的数据是通过方法形参接收,从客户端请求key-value数据,经过参数绑定,将key-value 数据绑定到controller的形参上,然后通过controller就可以直接使用该参数了
在这里插入图片描述
这里主要涉及的是参数绑定组件,需要先理解请求的数据转化为我们需要的数据称之为参数绑定组件,也就是参数绑定转换器, Spring MVC内置很多参数转换器,在极少数情况需要实现自定义的参数转换器

默认支持的类型

Spring MVC支持的默认的参数类型,直接在方法形参上给出默认的类型的声明,就可以直接使用了
HttpServletRequest对象
HttpServletResponse对象
HttpSession对象
Model/ModelMap对象
直接在Controller的方法形参上给定以上类型对象,就可以直接使用这些对象,在参数绑定过程中吗,遇到这种类型直接进行绑定
HttpServletRequest:通过request对象来获取请求信息
HttpServletResponse:通过response处理响应信息
HttpSession:通过Session对象得到session信息(缓存信息的存储)
Model/ModelMap:Model是接口,ModelMap是一个接口实现,将model数据填充到request中,即使使用Model,内部绑定的还是ModelMap的实现

    //默认的类型
    @RequestMapping("defaultParamType")
    public void defaultParamType(HttpServletRequest request, HttpServletResponse response, HttpSession session, Model model) throws IOException {
        //获取前端请求的数据
        request.getParameter("userName");
        //设置数据
        request.setAttribute("repsonseParam","request");
        //响应结果
        response.getWriter().write("response");
        
        //设置session信息
        session.setAttribute("sessionParam","session");
        
        //Model参数设置
        model.addAttribute("modelParam","model");
        
    }

基本的数据类型

基本的数据类型包括:byte,short,int,long ,float,double,char,Boolean
以int为类讲解
JSP页面

<form action="${pageContext.request.contextPath}/user/basicDataType" method="get">
            <input name="username" value="20" type="text"/>
            <input type="submit" value="提交"/>
          </form>

controller层代码

  //基本的数据类型
    @RequestMapping("dataType")
    public String DataType() {
        return "dataType";
    }
    @RequestMapping("basicDataType")
    public void basicDataType(int username){
        System.out.println("username:"+username);
    }

注意:表单的Input的name值和controller的参数变量名保持一致,可以完成数据绑定的
如果不一致呢?需要使用@RequestParam注解完成
通过@RequestParam对简单类型的数据进行绑定
在这里插入图片描述

自定义类型

  • 自定义的类型
public class User9 {
    //用户ID
    private int id;
    private String userName;
    private String sex;
    private String address;
}

最好实现序列化接口可以防止乱码

  • JSP页面
 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
        <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
        <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
        <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
        <html>
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>详情</title>
        </head>
        <body>

        <form action="${pageContext.request.contextPath }/user/editUserSubmit" method="get">
        <table width="60%" border="1"align="center">
        <h1 align="center">修改用户信息</h1>
        <tr>
        <td>用户ID</td>
        <td> <input type="text" name="id" value="${user.id }" readonly ></td> </td>
        </tr>
        <tr>
        <td>用户名</td>
        <td> <input id="username" type="text" name="userName" value="${user.userName }"></td> </td>
        </tr>
        <tr>
        <td>性别</td>
        <td> <input id="sex" type="text" name="sex" value="${user.sex }"></td> </td>
        </tr>
        <tr>
        <td>地址</td>
        <td> <input id="address" type="text" name="address" value="${user.address }"></td> </td>
        </tr>
        <tr ><td colspan="2" align="center"><input   type="submit" value="提交"/></td></tr>
        </table>
        </form>
        </body>
        </html>

注意:表达的name属性和pojo实体类的属性保持一致才能映射成功
如果类型不一致需要自定义类型转换器,给定一个自定义类实现Conterver接口,该接口是一个泛型需要指定转化的类型,例如将前端的String数据转化为日期类型即Conterver<String,Date>
并且包含引用类型的,name用引用名.属性
controller层代码
在这里插入图片描述
JSP页面
在这里插入图片描述

集合类型

集合类型:数组、List、Map等施常用的数据传输的类型
以List为例讲解
在这里插入图片描述

Spring MVC 请求返回值

返回ModelAndView类型

返回结果定位为ModelAndView类型,将Model和view进行分别设置

 @RequestMapping( value = "userList",method = {RequestMethod.POST,RequestMethod.GET})
    public ModelAndView userList() {
        //这个是需要Service获取数据结果
        List<User9> list = userService.getAllUser();
        //返回ModelAndView结果
        ModelAndView modelAndView = new ModelAndView();
        //填充数据
        modelAndView.addObject("users",list);
        //指定视图
        modelAndView.setViewName("userList");
        return modelAndView;
    }

返回String类型

返回逻辑视图名

真正路径=前缀+逻辑视图名+后缀

  @RequestMapping("dataType")
    public String DataType() {
        //逻辑视图名 =》视图路径=前缀("WEB-INF/jsp/")+逻辑视图名("dataType")+后缀(".jsp")
        return "dataType";
    }

redirect:重定向

重定向特点:浏览器的URL地址会发生改变,修改后的request的数据无法传递给重定向的页面的,应为重定向后重新进行request(request是无法共享)

  @RequestMapping("editUserSubmit")
    public String editUserSubmit(User9 user) {
        System.out.println("editUserSubmit:"+user);
        //通过重定向和页面转发来进行介绍 "user/userList"
        return "redirect:userList";
    }

在这里插入图片描述

forward:页面转发

浏览器的URL地址不变,request共享

 @RequestMapping("editUserSubmit")
    public String editUserSubmit(User9 user) {
        System.out.println("editUserSubmit:"+user);
        //通过重定向和页面转发来进行介绍 "userList"
        return "forward:userList";
    }

在这里插入图片描述
问题:redirect和forward的区别?

返回void类型

在controller方法的形参上可以定义HttpServletRquest和HttpServletResponse的类型的参数,使用request和response类响应结果

使用request转向页面

public void defaultParamType(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
  request.getRequestDispatcher("userList").forward(request,response);
}

使用response页面重定向

public void defaultParamType(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
  response.sendRedirect("userList");
}

通过response指定响应结果

例如通过JSON数据返回

 public void defaultParamType(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
      response.setCharacterEncoding("utf-8");
      response.setContentType("application/json;charset=utf-8");
      response.getWriter().write("hello word");
    }

前端控制器的源码分析

URL进入前端控制器的过程
在这里插入图片描述
前端控制器的实现类为:
org.springframework.web.servlet.DispatcherServlet
在前端控制器类中会执行调用doService方法

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //省略无关代码,关于日志打印的
        try {
            doDispatch(request, response);
        }
        finally {
            //省略无关代码
        }
    }
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
            try {
                //和大文件传输有关的协议校验
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
                //getHandler是前端控制器调用处理器映射器查找Handler
                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                //调用处理器适配器执行Handler,得到执行结果ModelAndView ,
                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                //视图解析,将model数据填充到request域
                applyDefaultViewName(request, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Error err) {
            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

在前端控制器和各组件的交互,通过doservice方法内部调用不同的方法和其他组件进行交互

处理器和映射器的配置

Spring MVC大部分使用都是全注解配置,但是全注解也是由非注解发展来的。
Spring MVC的两种注解性:注解形式和非注解形式
功能需求:用户信息列表查询
用户实体(User.java)

public class User9 {
    private int id; 
    private String userName;
    private String sex;
    private String address;
}

视图JSP(UserList.jsp)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
        <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
        <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
        <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>用户列表</title>
</head>
<body>
   <table border="1" align="center">
    <h1 align="center"> 用户列表</h1>
      <tr>
        <td>用户ID</td>
        <td>用户姓名</td>
        <td>用户性别</td>
        <td>地址</td>
      </tr>
      <c:foreach items="${users}" var="user">
        <tr>
            <th>${user.id}</th>
            <th>${user.userName}</th>
            <th>${user.sex}</th>
            <th>${user.address}</th>
        </tr>
      </c:foreach>
   </table>
</body>
</html>

基于XML的形式配置处理器适配器等

在spring-mvc.xml文件中配置处理器(包含处理器映射器、处理器适配器、视图解析器)
配置处理器适配器

SimpleControllerHandlerAdapter源码中,可以看出适配器只能实现Controller接口的Handler,即开发Controller,才能由SimpleControllerHandlerAdapter适配器执行
在这里插入图片描述

Handler实现

/**
 * Handler:基于XML配置实现
 * 需要实现org.springframework.web.servlet.mvc.Controller接口
 * 才能被适配器适配并执行
 */
public class ControllerXML implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
         //这个是需要通过数据拿到结果,实现静态数据模拟实现
        ArrayList <User9> list = new ArrayList <>();
        User9 user9 = new User9();
        user9.setId(1);
        user9.setUserName("张三");
        user9.setSex("男");
        user9.setAddress("西安");
        list.add(user9);
        User9 user1 = new User9();
        user1.setId(2);
        user1.setUserName("小明");
        user1.setSex("男");
        user1.setAddress("陕西西安");
        list.add(user1);
        //返回ModelAndView结果
        ModelAndView modelAndView = new ModelAndView();
        //填充数据
        modelAndView.addObject("users",list);
        //指定视图
        modelAndView.setViewName("userList");
        return modelAndView;
    }
}

处理器配置

<?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-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
       <!--配置处理器映射器-->
      <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
      <!--配置处理器适配器:所有处理器都需要来实现HandlerAdapter的接口-->
      <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
      <!--配置Handler实例,将Handler交给Spring 管理-->
      <bean name="/userlist" class="com.tulun.contrller.ControllerXML"/>
    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--配置后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

注意:在XML配置文件中处理器适配器和处理器映射器需要使用的两个框架类

org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping

将配置文件spring-mcv1.xml需要加载到web.xml中

基于注解的形式配置处理器适配器等

处理器映射器在spring3.1之前使用
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
处理器映射器在spring3.1之后使用

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

处理器适配器在spring3.1之前使用
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
处理器适配器在spring3.1之后使用

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

配置如下:

   <!--处理器适配器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
    <!--配置处理器映射器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
    <!--
    使用mvc:annotation-driven代替上面处理器映射器和处理器适配器的配置
    annotation-driven默认加载很多的参数绑定方法
    实际开发中都是使用mvc:annotation-driven
    处理器映射器,适配器采用Spring MVC提供的默认的处理器处理,不需要显性的配置
    -->
    <mvc:annotation-driven/>

Handler的实现

/*
基于注解实现的Handler
 */
@Controller
public class ControllerDemo {
    
    @RequestMapping("userList")
    public ModelAndView userList() {
        //这个是需要通过数据拿到结果,实现静态数据模拟实现
        ArrayList<User9> list = new ArrayList <>();
        User9 user9 = new User9();
        user9.setId(1);
        user9.setUserName("张三");
        user9.setSex("男");
        user9.setAddress("西安");
        list.add(user9);
        User9 user1 = new User9();
        user1.setId(2);
        user1.setUserName("小明");
        user1.setSex("男");
        user1.setAddress("陕西西安");
        list.add(user1);
        //返回ModelAndView结果
        ModelAndView modelAndView = new ModelAndView();
        //填充数据
        modelAndView.addObject("users",list);
        //指定视图
        modelAndView.setViewName("userList");
        return modelAndView;
    }
}

处理器配置

<?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-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
    <!--配置Spring MVC的需要扫描注解的包路径-->
     <context:component-scan base-package="com.tulun.contrller"/>
    
    <mvc:annotation-driven/>
    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--配置后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

图片上传功能

Spring MVC中如何实现文件的上传和下载功能
以图片上传为例介绍器使用步骤

相关依赖

   <!--文件操作依赖-->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.4</version>
    </dependency>
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>

编写JSP

在页面通过form表达提交的类型为enctype="multipart/form-data"的数据

   <form action="/upload" method="post" enctype="multipart/form-data">
      <input type="file" name="picture"/>
      <input type="submit" value="上传"/>
    </form>

在mvc的配置文件中配置multipart类型解析器

  <!--图片上传-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置上传文件的最大尺寸大小-->
        <property name="maxUploadSize">
            <value>500000</value>
        </property>
    </bean>

controller层开发

   @RequestMapping("fileTest")
    public String fileTest() {
        return "fileupload";
    }
//    文件上传操作
    @RequestMapping("upload")
    public void upload(MultipartFile picture) throws IOException {
        if (picture == null) return;
        //文件存储路径
        String path="/Users/gongdezhe/Desktop/tmp/";
        //获取文件的原始名称
        String imgName = picture.getName();
        System.out.println(imgName);
        //新存储文件路径
        File file = new File(path + imgName);
        //将内存数据写入磁盘
        picture.transferTo(file);
    }

在上传文件时,绑定的参数类型为MultipartFile,通过.transferTo方法将上传的文件进行保存

通过JSON进行数据交互

JSON的数据交互在前后端是比较通用的,在前后端分离开发中尤其重要
介绍JSON在Spring MVC中的使用

添加JSON依赖

 <!--JSON数据处理-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.4.6</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.4.6</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.4.6</version>
    </dependency>

使用注解@ResponseBody返回数据

//JSON返回前台数据
@RequestMapping( "responseJson")
@ResponseBody  //将Java对象转化为json数据
public  List<User9> userList() {
    List<User9> list = userService.getAllUser();
    return list;
}

注:注解@ResponseBody可以添加在方法上,也可以直接添加在方法返回类型前
执行结果:
在这里插入图片描述

接收JSON数据

页面通过jquery中的ajax来提交数据

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>JSON测试</title>
    <%--引入jquery:使用ajax提交数据--%>
    <script type="text/javascript" src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"/>
    <script type="text/javascript">
      $(function(){
        $("#btn").click(function(){
           $.ajax({
             contentType:"  application/json;charset=UTF-8",
             <%--JSON--%>
             data:'{"id":1,"userName":"李四"}',
             dataType:"json",
             type:"post",
             url:"${pageContext.request.contextPath }/user/responseJson"
           })
        }
        )
      });
    </script>
    </head>
    <body>
     <input type="button" value="JSON测试" id="btn"/>
    </body>
    </html>

controller层开发

@RequestMapping( "responseJson")
//@RequestBody 将前端的JSON数据解析绑定成Java对象
public  void responseJson(@RequestBody User9 user9) {
    System.out.println(user9);
}

注:接收的bean前使用注解@requestBody

异常处理机制

系统异常分为两类:预期异常和运行时异常
系统中到、service、controller层出现异常通过Throws Exception向上抛出,在Spring MVC中前端控制器将有异常处理器进行异常处理,SpringMVC 提供全局异常处理器进行统一处理,一个系统只有一个异常处理器

自定义异常类

对不同的异常类型定义不同的异常类,继承Exception

/**
 * 自定义异常
 * 针对预期的异常,需要在程序中抛出
 */
public class CustomException extends Exception {
    //异常信息
    private String message;
    public CustomException(String message) {
        super(message);
        this.message = message;
    }
    @Override
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}

全局异常处理器

Spring MVC提供了一个HandlerExceptionResolver接口
处理思路:
解析出异常类型,如果该类型是系统自定义的异常,直接取出异常信息,在错误页面展示,如果该异常类型不是系统自定义的类型,构造一个自定义的异常类型

/**
 * 异常处理器
 */
public class CustomExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        CustomException exception = null;
        if (ex instanceof CustomException) {
            exception = (CustomException)ex;
        } else {
            //不是系统自定义类型
            exception = new CustomException("未知异常");
        }
        //错误信息
        String message = exception.getMessage();
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("message", message);
        modelAndView.setViewName("error");
        return modelAndView;
    }
}

错误页面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>错误</title>
    </head>
    <body>
     <h1>${message}</h1>
    </body>
    </html>

配置异常处理器

在mvc配置文件中将异常处理器交给Spring管理

   <!--全局的异常处理器-->
    <bean class="com.tulun.expetion.CustomExceptionResolver"/>

异常的使用

//自定义类型
    //基本的数据类型
    @RequestMapping("{id}")
    public ModelAndView user(@PathVariable("id") int id) throws CustomException {
       //可预期的异常,直接进行抛出
        if (id > 35) {
            System.out.println("参数非法");
            throw new CustomException("参数非法");
        }
        Random random = new Random();
        //模拟一个不可预期的异常
        int i = id / random.nextInt(2);//0 1
        User9 user9 = new User9();
        user9.setId(1);
        user9.setUserName("lisi");
        user9.setSex("男");
        user9.setAddress("西安");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("user",user9);
        modelAndView.setViewName("editUser");
        return modelAndView;
    }

实验结果示例:
在这里插入图片描述

RESTFul

RESTFul是一种软件架构风格,不是标准。只是提供了一种设计原则和约束条件,主要适用于客户端和服务端交互的软件。
基于HTTP协议实现。为了提高系统的可伸缩性,降低应用之间耦合度,方便框架分布式处理程序,基于这种风格更加简单,具有层次感
在RESTFul中,对用户请求的URL使用同一个URL而用不同的请求方法get/post/delete/put等方式的请求进行区分,这样方便前后端分离的开发旁边前端开发人员不会对请求的资源地址产生混淆,形成一个统一的接口

使用规定

在HTTP协议中,四个表示操作方式的动词:GET/Post/Put/Delete,分别代表四种基本操作。
GET用来请求资源,POST用来新建立资源,也可以是更新操作,Put操作用来更新资源,delete操作用来删除资源
GET:对应select是从服务端进行查询,可以在服务器通过请求的参数区分查询方式
POST:对应Create在服务器创建有一个资源,调用Insert操作
PUT:对应update操作,在服务端更新资源,调用update操作
DELETE ,对应delete操作,在服务端进行删除资源调用delete语句
对URL进行规范,写RESTFul风格的URL
非RESTFul风格UFL:http://…/queryUserList?id=12&userName=‘sdf&sdf’
Rest风格:http://…/queryUserList/12
特点:URL简洁,将参数通过URL传递给服务端
HTTP方法规范:不管是删除、修改、添加。。。使用的URL是一致的,区分不同的删除等操作是通过HTTP的方法决定的,
例:http://…/queryUserList/12 方法设置为delete是表示是删除12这个用户ID的信息,方法设置为get方法表示是查询用户ID为12 的用户

Spring MVC中实现RESTFul风格接口

Spring MVC对RESTFul应用提供一下支持:
使用@RequestMapping指定要处理的请求的URL和HTTP请求的动作类型
利用@PathVariable注解将URL的变量映射到处理方法的参数上
前端利用ajax,在客户端发出put,delete的动作要求
在这里插入图片描述
在这里插入图片描述
一般的应用格式如下:
@RequestMapping(value="/{id}",method=RequestMethod.GET)
@RequestMapping(value="/{id}",method=RequestMethod.POST)
@RequestMapping(value="/{id}",method=RequestMethod.DELETE)
@RequestMapping(value="/{id}",method=RequestMethod.PUT)

拦截器

Spring MVC中拦截器类似servlet开发中的拦截器
SPring MVC中的拦截器需要实现一个接口

自定义拦截器类

自定义拦截器类,实现一个接口,HandlerIntercepter

/**
 * 自定义拦截器
 */
public class CustomInterceptor implements HandlerInterceptor {
    
    //进入Handler之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * true:表示放行
         * false:表示拦截,不向下执行
         */
        /**
         * 应用场景:
         * 用户省份认证,身份鉴权
         * 如果认证不通过表示当前用户没有登录,需要次方法栏拦截不在往下执行
         */
        System.out.println("进入Handler之前执行");
        return false;
    }
    //进入Handler方法之后,返回ModelAndView之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("进入Handler方法之后,返回ModelAndView之前执行");
    }
    
    //执行Handler之后执行此方法
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行Handler之后执行此方法");
    }
}
/**
 * 自定义拦截器
 */
public class CustomInterceptor implements HandlerInterceptor {
    //进入Handler之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * true:表示放行
         * false:表示拦截,不向下执行
         */
        /**
         * 应用场景:
         * 用户身份认证,身份鉴权
         * 如果认证不通过表示当前用户没有登录,需要次方法栏拦截不在往下执行
         */
        System.out.println("进入Handler之前执行");
        return true;
    }
    //进入Handler方法之后,返回ModelAndView之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("进入Handler方法之后,返回ModelAndView之前执行");
        /**
         * 应用场景:
         * 引用场景是将页面的公共的模型数据(菜单导航栏)传给视图,也可以是同一的指定视图
         */
    }
    //执行Handler之后执行此方法
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行Handler之后执行此方法");
        /**
         * 统一的异常处理或者是同一日志处理
         */
    }
}
拦截器配置
    <!--拦截器的配置-->
    <mvc:interceptors>
        <!--配置具体拦截器-->
        <mvc:interceptor>
            <!--
            /** 表示所有的URL包含的子路径
            /* 表示所有的URL的根路径
            -->
            <mvc:mapping path="/**"/>
            <bean class="com.tulun.utils.CustomInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

Spring MVC拦截器针对HandlerMapping进行拦截设置

<!--拦截器的配置-->
<mvc:interceptors>
    <!--配置具体拦截器-->
    <mvc:interceptor>
        <!--
        /** 表示所有的URL包含的子路径
        /* 表示所有的URL的根路径
        -->
        <mvc:mapping path="/**"/>
        <bean class="com.tulun.utils.CustomInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

如果在某个HandlerMapping中配置拦截吗,经过该映射器拦截成功的Handler会使用该拦截器
Spring MVC配置类似全局的拦截器,将拦截器注入到每一个映射器中
在配置多个拦截器时,一定要注意在配置中的顺序问题,统一的日志处理拦截器,一定要在preHandle一定是放行,且将他放在拦截器的第一个位置,登录认证拦截器,应该放在第二个位置
拦截器1:身份鉴权 preHandle true:false
拦截器2:统一的日志处理 preHandle true , afterCompletion
A:先拦截器1在拦截器2 B:先拦截器2在拦截器1 C:无所谓顺序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值