Spring MVC基础(一)

338 阅读25分钟

Spring MVC 简介

MVC 体系结构

三层架构

我们的开发架构⼀般都是基于两种形式,⼀种是 C/S 架构,也就是客户端/服务器;另⼀种是 B/S 架构,也就是浏览器服务器。在 JavaEE 开发中,⼏乎全都是基于 B/S 架构的开发。那么在 B/S 架构中,系统标准的三层架构包括:表现层、业务层、持久层。三层架构在我们的实际开发中使⽤的⾮常多,所以我们课程中的案例也都是基于三层架构设计的。三层架构中,每⼀层各司其职,接下来我们就说说每层都负责哪些⽅⾯:

  • 表现层 :

    也就是我们常说的web 层。它负责接收客户端请求,向客户端响应结果,通常客户端使⽤http 协议请求web 层,web 需要接收 http 请求,完成 http 响应。表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。表现层依赖业务层,接收到客户端请求⼀般会调⽤业务层进⾏业务处理,并将处理结果响应给客户端。表现层的设计⼀般都使⽤ MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系)

  • 业务层 :

    也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项⽬的需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层。业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务⼀致性。(也就是我们说的, 事务应该放到业务层来控制)

  • 持久层 :

    也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进⾏持久化的载体,数据访问层是业务层和持久层交互的接⼝,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进⾏增删改查的。

MVC设计模式

MVC 全名是 Model View Controller,是 模型(model)-视图(view)-控制器(controller) 的缩写, 是⼀种⽤于设计创建 Web 应⽤程序表现层的模式。MVC 中每个部分各司其职:

  • Model(模型):模型包含业务模型和数据模型,数据模型⽤于封装数据,业务模型⽤于处理业务。

  • View(视图): 通常指的就是我们的 jsp 或者 html。作⽤⼀般就是展示数据的。通常视图是依据模型数据创建的。

  • Controller(控制器): 是应⽤程序中处理⽤户交互的部分。作⽤⼀般就是处理程序逻辑的。MVC提倡:每⼀层只编写⾃⼰的东⻄,不编写任何其他的代码;分层是为了解耦,解耦是为了维护⽅便和分⼯协作。

Spring MVC 是什么?

image.png

  • SpringMVC 是一种基于MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于 SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。

  • 使用 Spring 可插入的 MVC 架构,从而在使用 Spring 进行 WEB 开发时,可以选择使用 Spring 的 Spring MVC 框架或集成其他 MVC 开发框架,如 Struts1(现在一般不用),Struts2 等。

  • SpringMVC 已经成为目前最主流的 MVC 框架之一,并且随着 Spring3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架。

  • 它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful 编程风格的请求。

作⽤:1)接收请求 2)返回响应,跳转⻚⾯

为什么学习SpringMVC?

也许你要问,为什么要学习Spring MVC呢?Struts2不才是主流吗?看SSH的概念有多火?

其实很多初学者混淆了一个概念,SSH实际上指的是Struts1.x+Spring+Hibernate。这个概念已经有十几年的历史了。在Struts1.x时代,它是当之无愧的霸主,但是在新的MVC框架涌现的时代,形式已经不是这样了,Struts2.x借助了Struts1.x的好名声,让国内开发人员认为Struts2.x是霸主继任者(其实两者在技术上无任何关系),导致国内程序员大多数学习基于Struts2.x的框架,又一个貌似很火的概念出来了S2SH(Struts2+Spring+Hibernate)整合开发。

不要再被蒙蔽了,看看下面的调查统计吧:

image.png

SpringMVC的市场占有率是40% ,而Struts2只有可怜的6%。这已然说明了学习SpringMVC的必要性了,再说了,SpringMVC本身就是spring家族的一员,与整合spring时,SpringMVC根本无需中间整合包,而struts2得需要。

既然已经知道了SpringMVC的重要性了,那么下面就跟着我一起看看它的神奇之处吧!

原理分析(重点)

六大组件说明

  • DispatcherServlet:前端控制器

    用户请求到达前端控制器,它就相当于mvc模式中的C,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

  • HandlerMapping:处理器映射器

    HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

  • Handler:处理器

    Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。

    由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。

  • HandlAdapter:处理器适配器

    通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

  • View Resolver:视图解析器

    View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

  • View:视图

    springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。

    一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

说明: 在springmvc的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件。需要用户开发的组件有:处理器、视图

架构流程

springmvc流程分析.png

  • 1.用户发送请求至前端控制器DispatcherServlet

  • 2.DispatcherServlet收到请求调用HandlerMapping`处理器映射器。

  • 3.处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet

  • 4.DispatcherServlet通过HandlerAdapter处理器适配器调用处理器

  • 5.HandlerAdapter执行处理器(handler,也叫后端控制器)。

  • 6.Controller执行完成返回ModelAndView

  • 7.HandlerAdapterhandler执行结果ModelAndView返回给DispatcherServlet

  • 8.DispatcherServletModelAndView传给ViewReslover视图解析器

  • 9.ViewReslover解析后返回具体View对象

  • 10.DispatcherServletView进行渲染视图(即将模型数据填充至视图中)。

  • 11.DispatcherServlet响应用户

默认配置文件

spring-webmvc-xxx.jar包中有一个DispatcherServlet.properties文件,该配置中默认加载了一些springmvc默认的其他组件,其中就包括三大组件。【找源码中的resources文件夹】

image.png

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

# 处理器映射器的所有实现类
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

# 适配器处理器的所有实现类
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
   org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
   org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
   org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

# 视图解析器默认使用的实现类
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

SpringMVC的入门案例

1.首先pom.xml文件中引入依赖如下:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.lagou.edu</groupId>
  <artifactId>springmvc-demo</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>springmvc-demo Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <!--引入spring webmvc的依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.12.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>


    <!--json数据交互所需jar,start-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.0</version>
    </dependency>
    <!--json数据交互所需jar,end-->

    <!--文件上传所需坐标-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8080</port>
          <path>/</path>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

2.配置核心的控制器(配置DispatcherServlet),在web.xml配置文件中核心控制器DispatcherServlet

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  
  <!--配置前端控制器DispatcherServlet -->
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!-- 配置Servlet的初始化参数,读取springmvc的配置文件,创建spring容器 -->
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!-- 指定初始化时机,设置为2,表示Tomcat启动时,DispatcherServlet会跟随着初始化 -->
    <!-- 如果不指定初始化时机,DispatcherServlet就会在第一次被请求的时候,才会初始化,而且只会被初始化一次 -->
    <load-on-startup>2</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>

    <!--
     <url-pattern>/</url-pattern>配置前端控制器拦截的路径,也即:哪些请求需要交给前端控制器处理,一般都配置为/,杜绝配置为/*
        
      方式一:带后缀,比如*.action  *.do *.aaa
             该种方式比较精确、方便,在以前和现在企业中都有很大的使用比例
      方式二:/ 不会拦截 .jsp,但是会拦截.html等静态资源(静态资源:除了servlet和jsp之外的js、css、png等)

            为什么配置为/ 会拦截静态资源???
                因为tomcat容器中有一个web.xml(父),你的项目中也有一个web.xml(子),是一个继承关系
                      父web.xml中有一个DefaultServlet,  url-pattern 是一个 /
                      此时我们自己的web.xml中也配置了一个 / ,覆写了父web.xml的配置
            为什么不拦截.jsp呢?
                因为父web.xml中有一个JspServlet,这个servlet拦截.jsp文件,而我们并没有覆写这个配置,
                所以springmvc此时不拦截jsp,jsp的处理交给了tomcat


            如何解决/拦截静态资源这件事?


      方式三:/* 拦截所有,包括.jsp
    -->
    <!--拦截匹配规则的url请求,进入springmvc框架处理-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

3. 编写springmvc.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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
">

    
    <!--开启controller扫描-->
    <context:component-scan base-package="com.lagou.edu.controller"/>


    <!--配置springmvc的视图解析器,即controller中返回的view都会交给视图解析器处理-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 配置spring开启注解mvc的支持,相当于使用默认的处理器映射器HandlerMapping和处理器适配器HandlerAdapter -->
    <mvc:annotation-driven></mvc:annotation-driven>
 
</beans>

4.编写DemoController控制器类,如下:

/**
 * @author huangshuai
 *
 */
@Controller
@RequestMapping("/demo")
public class DemoController  {

    /**
     * url: http://localhost:8080/demo/handle01
     */
    @RequestMapping("/handle01")
    public ModelAndView handle01() {
        Date date = new Date();// 服务器时间
        // 返回服务器时间到前端页面
        // 封装了数据和页面信息的 ModelAndView
        ModelAndView modelAndView = new ModelAndView();
        // addObject 其实是向请求域中request.setAttribute("date",date);
        modelAndView.addObject("date",date);
        // 视图信息(封装跳转的页面信息) 逻辑视图名
        //视图名会被上面配置的视图解析器处理,最后会访问到webapp\WEB-INF\jsp\success.jsp
        modelAndView.setViewName("success");
        return modelAndView;
    }
}

5.编写success.jsp,如下:

<%@ page language="java" isELIgnored="false" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title>Insert title here</title>
    </head>
    <body>
        <!--使用el表达式获取作用域中变量date的值 -->
        跳转成功!服务器时间:${date}
    </body>
</html>

6.测试,使用pom.xml中的tomcat插件启动项目,打开浏览器,访问:

http://localhost:8080/demo/handle01

image.png

image.png

处理器注解开发方式必要的注解主要有以下两个:

  • @Controller注解:在类上添加该注解,指定该类为一个请求处理器,不需要实现任何接口或者继承任何类。
  • @RequestMapping注解:在方法上或者类上添加该注解,指定请求的url由该方法处理。

处理器的返回值是ModelAndView对象,该对象的具体理解如下:

ModelAndView:方法返回值对象,该对象包含两个功能:

  • 一个是将数据存储到Request域中
  • 一个是设置响应视图,比如将视图设置为“/WEB-INF/jsp/success.jsp

注意事项:

@Controller注解的类中每个@RequestMapping注解的方法,最终都会转为HandlerMethod类(而这个类才是SpringMVC注解开发方式中真正意义的处理器)

URL-Pattern配置

上述入门案例的web.xml中配置org.springframework.web.servlet.DispatcherServleturl-pattern标签的拦截路径时,一般情况下有以下三种配置:

  • 带后缀,比如*.action *.do *.aaa,该种方式比较精确、方便,在以前和现在企业中都有很大的使用比例
  • /:不会拦截 .jsp,但是会拦截.html等静态资源(静态资源:除了servlet和jsp之外的js、css、png等)
    • 为什么配置为/ 会拦截静态资源??? 因为tomcat容器中有一个web.xml(父),你的项目中也有一个web.xml(子),是一个继承关系,父web.xml中有一个DefaultServlet(该DefaultServlet会正常处理静态资源的跳转), url-pattern 是一个 /,此时我们自己的web.xml中也配置了一个 / ,覆写了父web.xml的配置。

    • 为什么不拦截.jsp呢?因为父web.xml中有一个JspServlet,这个servlet拦截.jsp文件,正常处理jsp文件的跳转,而我们并没有覆写这个配置,所以springmvc此时不拦截jsp,jsp的处理交给了tomcat。

怎么解决才能让/不拦截静态资源?

方式一:针对静态资源,使用tomcat容器中的web.xml的DefaultServlet处理,在springmvc.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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
">

    <!--开启controller扫描-->
    <context:component-scan base-package="com.lagou.edu.controller"/>


    <!--配置springmvc的视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>


    <!--
        自动注册最合适的处理器映射器,处理器适配器(调用handler方法)
    -->
    <mvc:annotation-driven />

    <!--静态资源配置,方案一-->
    <!--
        原理:添加该标签配置之后,会在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler对象
             这个对象如同一个检查人员,对进入DispatcherServlet的url请求进行过滤筛查,如果发现是一个静态资源请求
             那么会把请求转由web应用服务器(tomcat)默认的DefaultServlet来处理,如果不是静态资源请求,那么继续由
             SpringMVC框架处理
    -->
    <mvc:default-servlet-handler/>
    </bean>
</beans>

但是上述处理会有缺点:只能处理webapp根目录下面的静态资源,如果是其他路径下的静态资源还是会交给springmvc框架处理。所以推荐使用方式二。

方式二:在springmvc.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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
">

    <!--开启controller扫描-->
    <context:component-scan base-package="com.lagou.edu.controller"/>


    <!--配置springmvc的视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>


    <!--
        自动注册最合适的处理器映射器,处理器适配器(调用handler方法)
    -->
    <mvc:annotation-driven />

    <!--静态资源配置,方案二,SpringMVC框架自己处理静态资源
        mapping:约定的静态资源的url规则
        location:指定的静态资源的存放位置
    -->
    <mvc:resources location="classpath:/"  mapping="/resources/**"/>
    <mvc:resources location="/WEB-INF/js/" mapping="/js/**"/>
</beans>

mvc:resources标签中,mapping属性表示匹配的路径,例如请求路径中包含resources开头的,都会在location中配置的classpath下面查找对应的静态资源进行正常跳转。

  • /*:拦截所有,包括.jsp 对于url-pattern配置为/*演示如下:

1.配置web.xml如下:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
 
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!--配置为/* -->
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

2.springmvc.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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
">

    <!--开启controller扫描-->
    <context:component-scan base-package="com.lagou.edu.controller"/>

    <!--配置springmvc的视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--
        自动注册最合适的处理器映射器,处理器适配器(调用handler方法)
    -->
    <mvc:annotation-driven />
</beans>

3.Controller类中方法如下:

@Controller
@RequestMapping("/demo")
public class DemoController  {
 
    /**
     * url: http://localhost:8080/demo/handle01
     */
    @RequestMapping("/handle01")
    public ModelAndView handle01() {
        Date date = new Date();// 服务器时间
        // 返回服务器时间到前端页面
        // 封装了数据和页面信息的 ModelAndView
        ModelAndView modelAndView = new ModelAndView();
        // addObject 其实是向请求域中request.setAttribute("date",date);
        modelAndView.addObject("date",date);
        // 视图信息(封装跳转的页面信息) 逻辑视图名
        modelAndView.setViewName("success");
        return modelAndView;
    }
}

根据上面的handle01方法,以及在springmvc.xml中的视图解析器的配置,当请求http://localhost:8080/demo/handle01时页面就会直接跳转到WEB-INF/jsp/success.jsp页面,但是由于我们配置了DispatcherServleturl-pattern拦截路径为/*,此时就会在Springmvc中查找请求路径为WEB-INF/jsp/success.jspServlet,但是此时没有,所以就会导致404的错误,错误结果如下图所示

image.png

image.png

乱码解决

请求乱码解决之get乱码问题

GET请求乱码原因分析

  • GET请求参数是通过请求行中的URI发送给Web服务器(Tomcat)的。

  • Tomcat服务器会对URI进行编码操作(此时使用的是Tomcat设置的字符集,默认是iso8859-1

  • 到了我们的应用程序中的请求参数,已经是被Tomcat使用ISO8859-1字符集进行编码之后的了。

解决方式1之修改tomcat配置文件

指定UTF-8编码,如下:

<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

解决方式2之对请求参数进行重新编码:

String username = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")

请求乱码解决之post乱码问题

在web.xml中加入:

<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>   

响应乱码解决

使用@RequestMapping注解中的produces属性,指定响应体的编码格式,或者response.setContentType(xxx)来解决

@RequestMapping介绍

value属性

请求URL映射

  • 作用:用于映射URL和HandlerMethod方法。
  • 用法如下:
@RequestMapping(value="/item")
@RequestMapping("/item“)
@RequestMapping(value={"/item",”/queryItem”})

窄化请求映射

  • 作用: 限制此类下的所有方法的访问请求url必须以请求前缀开头,对url进行模块化分类管理。

  • 用法如下:

    访问时的URL是/item/findItem

@RequestMapping("item")
@Controller
public class ItemController {
​
   @RequestMapping("findItem")
   public String findItem(Model model) {
      model.addAttribute("msg", "ItemController...findItem方法执行了");
      return "success";
   }
}

method属性

  • 作用:限定请求URL只能通过指定的method请求方式去访问该HandlerMethod
  • 用法如下

    @RequestMapping(value="/findItem",method=RequestMethod.GET)
    @RequestMapping(value="/findItem",method = RequestMethod.POST)
    @RequestMapping(value="/findItem",method={RequestMethod.GET,RequestMethod.POST})
    

params属性

  • 作用: 通过设置params参数条件,进行访问HandlerMethod的限制

  • 用法如下:

    • URL请求

      <a href="item/removeItem?name=iphone6&price>5000">删除商品,金额大于5000</a>
      <br />
      <a href="item/removeItem?name=iphoneXs&price>7000">删除商品,金额大于7000</a>
      
    • Controller方法:

      @RequestMapping(value="removeItem",params= {"name","price>5000"})
      public String removeItem(Model model) {
          model.addAttribute("msg", "ItemController...removeItem方法执行了");
          return "success";
      }
      

三大组件显式配置

注解映射器和适配器

通过bean标签配置

  • RequestMappingHandlerMapping:注解式处理器映射器

    对类中标记@ResquestMapping的方法进行URL映射,根据ResquestMapping定义的url匹配ResquestMapping标记的方法,匹配成功返回HandlerMethod对象【即@RequestMapping表注的方法】给前端控制器,HandlerMethod对象中封装url对应的方法Method

  • RequestMappingHandlerAdapter:注解式处理器适配器

    对标记@ResquestMapping的方法进行适配、执行。

配置如下:

<!--注解映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/><!--注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

通过mvc标签配置(推荐)

<mvc:annotation-drivern />   

mvc:annotation-drivern标签的作用,就是往spring容器中注册了以下的一些BeanDefinition:(具体流程,参考《源码分析》章节

  • ContentNegotiationManagerFactoryBean
  • RequestMappingHandlerMapping
  • ConfigurableWebBindingInitializer
  • RequestMappingHandlerAdapter
  • CompositeUriComponentsContributorFactoryBean
  • ConversionServiceExposingInterceptor
  • MappedInterceptor
  • ExceptionHandlerExceptionResolver
  • ResponseStatusExceptionResolver
  • DefaultHandlerExceptionResolver
  • BeanNameUrlHandlerMapping
  • HttpRequestHandlerAdapter
  • SimpleControllerHandlerAdapter
  • HandlerMappingIntrospector

视图解析器

  • springmvc.xml文件配置如下:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 该视图解析器,默认的视图类就是JstlView,可以不写 -->
    <property name="viewClass"
        value="org.springframework.web.servlet.view.JstlView" />
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>
  • 配置说明:

    • InternalResourceViewResolver:默认支持JSP视图解析

    • viewClassJstlView表示JSP模板页面需要使用JSTL标签库,所以classpath中必须包含jstl的相关jar 包。此属性可以不设置,默认为JstlView

    • prefixsuffix:查找视图页面的前缀和后缀,最终视图的址为:前缀+逻辑视图名+后缀,逻辑视图名需要在controller中返回的ModelAndView指定,比如逻辑视图名为hello,则最终返回的jsp视图地址 “WEB-INF/jsp/hello.jsp

返回值处理【重点】

不使用@ResponseBody注解修饰

ModelAndView

Controller方法中定义ModelAndView对象并返回,对象中可添加model数据、指定view

void

在Controller方法形参上可以定义request和response,使用requestresponse指定响应结果:

void service(HttpServletRequest request,HttpServletResponse response){}

1、使用request转发向页面,如下:

request.getRequestDispatcher("页面路径").forward(request, response);

2、也可以通过response页面重定向:

response.sendRedirect("url")

3、也可以通过response指定响应结果,例如响应json数据如下:

response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json串");

String(推荐)

逻辑视图名

return "item/item-list";

redirect重定向

return "redirect:testRedirect";

redirect:

  • 相当于“response.sendRedirect()
  • 浏览器URL发生改变
  • Request域不能共享

forward转发

return “forward:testForward";

forward

  • 相当于“request.getRequestDispatcher().forward(request,response)
  • 浏览器URL不发送改变
  • Request域可以共享

使用@ResponseBody注解修饰

ResponseBody注解

ResponseBody和RequestBody注解介绍

  • ResponseBody注解的作用【重点】:
一、ResponseBody注解可以针对Controller返回值类型,使用内置的9种HttpMessageConverter进行匹配,找到合适的HttpMessageConverter进行处理。

二、HttpMessageConverter处理逻辑分为三步:
    1、数据转换,比如Java对象转Json字符串。
    2、指定HttpServletResponse的ContentType值。
    3、将转换之后的数据放到HttpServletResponse对象的响应体返回到页面
  • @RequestBody注解的作用和 @ResponseBody注解正好相反,它是处理请求参数的http消息转换的。

常用的HttpMessageConverter

MappingJacksonHttpMessageConverter
作用:处理POJO类型返回值
​
默认使用MappingJackson的JSON处理能力,将后台返回的Java对象(POJO类型),转为JSON格式输出到页面
​
将响应体的Content-Type设置为application/json;charset=utf-8
StringHttpMessageConverter
作用:处理String类型返回值
调用response.getWriter()方法将String类型的字符串写回给调用者。 
将响应体的Content-Type设置为text/plain;charset=utf-8,即纯文本类型

ResponseBody示例

JSP代码

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!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格式数据参数绑定和返回值处理演示demo</title>
    </head>
    <body>
        <!-- 使用@ResponseBody注解-->
        <a href="${pageContext.request.contextPath}/responsebody/returnString">测试String类型返回值</a>
        <a href="${pageContext.request.contextPath}/responsebody/returnPOJO">测试POJO类型返回值</a>
         <!-- 使用@RestController注解-->
        <a href="${pageContext.request.contextPath}/restcontroller/returnString">测试String类型返回值</a>
        <a href="${pageContext.request.contextPath}/restcontroller/returnPOJO">测试POJO类型返回值</a>
    </body>
</html>

Controller代码

  • 方式1(使用@ResponseBody注解):
@RequestMapping("responsebody")
@Controller
public class MyResponseBodyController {
​
    // @RequestMapping注解中的consumes和produces分别是为请求头和响应头设置contentType
    @RequestMapping(value = "returnString", produces = "text/plain;charset=UTF-8")
    @ResponseBody
    public String returnString() {
        // 如果在使用@ResponseBody注解的前提下,如果返回值是String类型,则返回值会由StringHttpMessageConverter进行处理
        return "查询失败";
    }
​
    @RequestMapping("returnPOJO")
    @ResponseBody
    public User returnPOJO() {
        User user = new User();
        user.setId(1);
        user.setUsername("bingbing");
        user.setSex("女");
​
        // 如果在使用@ResponseBody注解的前提下,如果返回值是POJO类型,则返回值会由MappingJacksonHttpMessageConverter(需要第三方jar包支持)进行处理
        return user;
    }
}
  • 方式2(使用@RestController注解):
/**
 * 用来学习JSON交互
 * @author huangshuai
 *
 */
@RequestMapping("restcontroller")
@RestController //相当于Controller注解和ResponseBody注解的组合
public class MyRestcontroller {
    
    //@RequestMapping注解中的consumes和produces分别是为请求头和响应头设置contentType
    @RequestMapping(value="returnString",produces="text/plain;charset=UTF-8")
    public String returnString() {
        // 如果在使用@ResponseBody注解的前提下,如果返回值是String类型,则返回值会由StringHttpMessageConverter进行处理
        return "查询失败";
    }
    @RequestMapping("returnPOJO")
    public User returnPOJO() throws CustomException{
                
        User user = new User();
        user.setId(1);
        user.setUsername("bingbing");
        user.setSex("女");
        
        // 如果在使用@ResponseBody注解的前提下,如果返回值是POJO类型,则返回值会由MappingJacksonHttpMessageConverter(需要第三方jar包支持)进行处理
        return user;
    }
}

参数绑定处理

  • 什么是参数绑定?

    就是将请求参数串中的value值获取到之后,再进行类型转换,然后将转换后的值赋值给Controller类中方法的形参,这个过程就是参数绑定。

  • 请求参数格式

    默认是key/value格式,比如: http://XXXXX?id=1&type=301

  • 请求参数值的数据类型

    都是String类型的各种值

  • 请求参数值要绑定的目标类型

    Controller类中的方法参数,比如简单类型、POJO类型、集合类型等。

  • SpringMVC内置的参数解析组件

    默认内置了24种参数解析组件(ArgumentResolver)

默认支持的参数类型

Controller方法形参中可以随时添加如下类型的参数(Servlet API支持),处理适配器会自动识别并进行赋值

  • HttpServletRequest

    通过request对象获取请求信息

  • HttpServletResponse

    通过response处理响应信息

  • HttpSession

    通过session对象得到session中存放的对象

  • InputStream、OutputStream

  • Reader、Writer

  • Model/ModelMap

    ModelMap继承自LinkedHashMap,Model是一个接口,它们的底层实现都是同一个类(BindingAwareModelMap),作用就是向页面传递数据,相当于Request的作用,如下:

     model.addAttribute("msg", “测试springmvc”);
    

简单类型

直接绑定

http请求参数的key和controller方法的形参名称一致

注解绑定

请求参数的key和controller方法的形参名称不一致时,需要使用@RequestParam注解才能将请求参数绑定成功。

RequestParam注解

- value:
    参数名字,即入参的请求参数名字
    如value=“itemid”表示请求的参数中的名字为itemid的参数的值将传入
​
- required:
    是否必须,默认是true,表示请求中一定要有相应的参数,否则将报;
    TTP Status 400 - Required Integer parameter 'XXXX' is not present
​
- defaultValue:
    默认值,表示如果请求中没有同名参数时的默认值

绑定POJO类型

要求表单中参数名称和Controller方法中的POJO形参的属性名称保持一致。

绑定集合或者数组类型

简单类型数组

通过HTTP请求批量传递简单类型数据的情况,Controller方法中可以用String[]或者pojo的String[]属性接收(两种方式任选其一),但是不能使用List集合接收。

POJO类型集合或者数组

批量传递的请求参数,最终要使用List<POJO>来接收,那么这个List<POJO>必须放在另一个POJO类中

参数绑定示例

JSP代码

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!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>参数绑定演示demo</title>
</head>
<body>
	<!-- request请求的内容类型主要分为:K/V类型、Multipart类型、JSON类型 -->
	
	<!-- 将request请求参数,绑定到简单类型(基本类型和String类型)方法参数 -->
	<!-- 直接绑定 -->
	<a href="${pageContext.request.contextPath}/user/findUserById?id=1&name=bingbing">查询用户1</a>
	<!-- @RequestParam注解绑定 -->
	<a href="${pageContext.request.contextPath}/user/findUserById2?uid=1">查询用户2</a>
	
	<!-- 将request请求参数,绑定到POJO类型(简单POJO和包装POJO的)方法参数 -->
	<form action="${pageContext.request.contextPath}/user/saveUser" method="post">
		用户名称:<input type="text" name="username"><br /> 
		用户性别:<input type="text" name="sex"><br /> 
		所属省份:<input type="text" name="address.provinceName"><br /> 
		所属城市:<input type="text" name="address.cityName"><br /> 
		<input type="submit" value="保存">
	</form>

	<!-- 将request请求参数,绑定到[元素是简单类型的集合或数组]参数 -->
	<!-- 使用数组接收 -->
	<a href="${pageContext.request.contextPath}/user/findUserByIds?id=1&id=2&id=3">根据ID批量删除用户</a>
	<!-- 使用List接收 -->
	<a href="${pageContext.request.contextPath}/user/findUserByIds2?id=1&id=2&id=3">根据ID批量删除用户</a>
	<!-- 使用Bean的List属性接收 -->
	<a href="${pageContext.request.contextPath}/user/findUserByIds3?uid=1&uid=2&uid=3">根据ID批量删除用户</a>

	
	<!-- 将request请求参数,绑定到[元素是POJO类型的List集合或Map集合]参数 -->
	<form action="${pageContext.request.contextPath}/user/updateUser" method="post">
		用户名称:<input type="text" name="username"><br /> 
		用户性别:<input type="text" name="sex"><br />
		<!-- itemList[集合下标]:集合下标必须从0开始 -->
		<!-- 辅助理解:先将name属性封装到一个Item对象中,再将该Item对象放入itemList集合的指定下标处 -->
		购买商品1名称:<input type="text" name="itemList[0].name"><br />
		购买商品1价格:<input type="text" name="itemList[0].price"><br />
		购买商品2名称:<input type="text" name="itemList[1].name"><br />
		购买商品2价格:<input type="text" name="itemList[1].price"><br />
		<!-- itemMap['item3']:其中的item3、item4就是Map集合的key -->
		<!-- 辅助理解:先将name属性封装到一个Item对象中,再将该Item对象作为value放入itemMap集合的指定key处 -->
		购买商品3名称:<input type="text" name="itemMap['item3'].name"><br />
		购买商品3价格:<input type="text" name="itemMap['item3'].price"><br />
		购买商品4名称:<input type="text" name="itemMap['item4'].name"><br />
		购买商品4价格:<input type="text" name="itemMap['item4'].price"><br />
		<input type="submit" value="保存">
	</form>
	
	<!-- 将request请求参数,绑定到Date类型方法参数 -->
	<!-- 请求参数是:【年月日】 格式 -->
	<a href="${pageContext.request.contextPath}/user/deleteUser?birthday=2018-01-01">根据日期删除用户(String)</a>
	<!-- 请求参数是:【年月日 时分秒】 格式 -->
	<a href="${pageContext.request.contextPath}/user/deleteUser2?birthday=2018-01-01 12:10:33">根据日期删除用户(Date)</a>

	<!-- 文件类型参数绑定 -->
	<form action="${pageContext.request.contextPath}/fileupload" method="post" enctype="multipart/form-data">
		图片:<input type="file" name="uploadFile" /><br /> 
		<input type="submit" value="上传" />
	</form>

</body>
</html>

Controller代码

/**
 * 用来学习参数绑定
 * @author huangshuai
 *
 */
@RequestMapping("user")
@Controller
public class UserController {
	
	@RequestMapping("findUserById")
	public String findUserById(Integer id,Model model,HttpServletRequest request) {
		model.addAttribute("msg", "直接参数绑定接收到的参数:"+id);
		model.addAttribute("msg", "通过Request getParameter参数接收到的参数:"+request.getParameter("id"));
		return "success";
	}
	//	@RequestParam:可以理解为request.getParameter("参数key")
	@RequestMapping("findUserById2")
	public String findUserById2(@RequestParam("uid") Integer id,Model model) {
		model.addAttribute("msg", "接收到的参数:"+id);
		return "success";
	}
	@RequestMapping("saveUser")
	public String saveUser(User user,Model model) {
		model.addAttribute("msg", "接收到的参数:"+user.toString());
		return "success";
	}
	@RequestMapping("deleteUser")
	public String deleteUser(String birthday,Model model) {
		model.addAttribute("msg", "接收到的参数:"+birthday);
		return "success";
	}
	@RequestMapping("deleteUser2")
	public String deleteUser2(Date birthday,Model model) {
		model.addAttribute("msg", "接收到的参数:"+birthday);
		return "success";
	}
	@RequestMapping("findUserByIds")
	public String findUserByIds(Integer[] id,Model model) {
		model.addAttribute("msg", "接收到的参数:"+id);
		return "success";
	}
	@RequestMapping("findUserByIds2")
	public String findUserByIds2(List<Integer> id,Model model) {
		model.addAttribute("msg", "接收到的参数:"+id);
		return "success";
	}
	@RequestMapping("findUserByIds3")
	public String findUserByIds3(User user,Model model) {
		model.addAttribute("msg", "接收到的参数:"+user.getUid());
		return "success";
	}
	@RequestMapping("updateUser")
	public String updateUser(User user,Model model) {
		model.addAttribute("msg", "接收到的参数:"+user.getUid());
		return "success";
	}
}

PO代码

public class User {
    private int id;
    private String username;
    private Date birthday;
    private String sex;

    // 演示包装POJO参数绑定
    private Address address;

    // 演示批量简单类型参数接收
    private List<Integer> uid = new ArrayList<>();

    // 将request请求参数,绑定到[元素是POJO类型的List集合]参数 
    private List<Item> itemList = new ArrayList<>();

    // 将request请求参数,绑定到[元素是POJO类型的Map集合]参数 
    private Map<String, Item> itemMap = new HashMap<>();

    //setter/getter方法
}

日期类型

对于springmvc无法解析的参数绑定类型,比如[年月日时分秒格式的日期]绑定到Date类型会报错,此时需要自定义[参数转换器]进行参数绑定。

1.编写日期类型转换器,即实现Converter接口,Converter<String,Date>中第一个泛型表示原类型,第二个泛型表示目标类型,代码如下:

/**
 * 自定义参数转换器
 * @author huangshuai
 *
 * Converter<String,Date>中第一个泛型表示原类型,第二个泛型表示目标类型。
 * 即可以将前端传进来的String日期格式转为对应的Date类型
 *
 */
public class DateConverter implements Converter<String,Date>{

   @Override
   public Date convert(String source) {
      // 使用Java中 SimpleDateFormat API完成日期转换
      String pattern = "yyyy-MM-dd";
      SimpleDateFormat dateFormat = new SimpleDateFormat(pattern );
      try {
         return dateFormat.parse(source);
      } catch (ParseException e) {
         e.printStackTrace();
      }
      return null;
   }
}

2.在springmvc.xml配置文件中配置Converter如下:

<!-- 加载注解驱动 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 转换器配置 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <!-- 可以配置多种类型转换器-->
    <property name="converters">
        <set>
            <!--类型转换器类全限定类名 -->
            <bean class="com.kkb.ssm.controller.converter.DateConverter"/>
        </set>
    </property>
</bean>

RESTful支持

理解什么是REST之前,先去理解一下什么是HTTP

HTTP介绍

HTTP协议是建立在客户端和服务器之间的一个应用层协议,在客户端和服务器之间需要数据的传输,而传输数据的时候,我们要按照指定的规则或者叫协议去传输数据。

  • HTTP是建立在TCP/IP协议基础之上的一个网络协议。

  • HTTP协议属于网络七层结构中最上层(应用层)的协议。

  • HTTP协议是一个无状态协议(不会记录每次访问时的信息)

  • HTTP是一个客户端和服务器端请求和应答的标准(TCP)。客户端是终端用户,服务器端是网站。

什么是URL/URI?

  • URL: Uniform Resource Identifier,统一资源标识符
它相当于一个网络资源的名称,只是名称的表现形式是/开头的路径形式。
  • URI: Uniform Resource Location,统一资源定位符。
  • URL和URI的区别: URL是URI的子集。

什么是WEB资源?

通过浏览器可以访问到的所有资源都是web资源,web资源分为静态资源动态资源

  • 动态资源是通过后台程序展示页面数据的,比如Servlet请求。
  • 静态资源的数据是不变的,比如HTML、JPG、AVI。

HTTP的作用是什么?

就是为了约束客户端和服务器之间传输web资源时的格式。

HTTP协议版本

- HTTP协议现在有哪些版本?
- 1.0版本和1.1版本的区别是什么?

HTTP1.1和HTTP1.0版本之间最大的区别就是:可以一个连接传输多个web资源

推荐使用HTTP1.1版本!!!

HTTP协议组成

HTTP协议由两部分组成:请求协议信息响应协议信息

请求协议信息

 - 请求协议由哪几部分组成?
 - 请求协议的请求行包含哪些信息?
 - 请求协议的请求头如何理解?请求头中常用的一些配置的作用各自是什么?
 - MIME是什么?常见的MIME类型有哪些?
 - 请求协议的请求体有几种表现形式?

HTTP请求协议信息由三部分组成:请求行、请求头、请求体,简称行头体。

image.png

请求行

也叫请求首行,它包含四部分(请求方法、URI、协议/版本、回车换行):GET /user.html HTTP/1.1

请求方法: GET、POST等8种 互联网中WEB资源操作也有增删改查方法,它们分别是POST、DELETE、PUT和GET。

image.png

URI

Uniform Resource Identifier,统一资源标识符。它相当于一个网络资源的名称,只是名称的表现形式是/开头的路径形式。

URL:Uniform Resource Location,统一资源定位符

URL和URI的区别:URL是URI的子集。

协议/版本

表示这次请求是通过哪个协议发送的,比如HTTP协议、HTTPS协议等,使用的HTTP协议一般都是1.1版本的。

请求头

请求头的信息是以[key:value]形式展现的。一般来说,大多数请求头的信息都不是必须的,我们只需要了解一些常见的请求头信息即可!

请求头说明:

Content-Type是请求消息中非常重要的内容,表示请求正文中的文档属于什么MIME类型。Content-Type: [type]/[subtype]; parameter。例如最常见的就是text/html,它的意思是说返回的内容是文本类型,这个文本又是HTML格式的。
Host指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回
Accept浏览器可接受的MIME类型
Accept-Charset浏览器可接受的字符集
Accept- Encoding浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间
Accept-Language浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到
Authorization授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中
Connection表示是否需要持久连接。如果Servlet看到这里的值为“Keep- Alive”,或者看到请求使用的是HTTP1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入 ByteArrayOutputStream,然后在正式写出内容之前计算它的大小
Content-Length表示请求消息正文的长度
Cookie这是最重要的请求头信息之一,可以在客户端记录访问状态。
From请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它
If-Modified-Since只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答
Pragma指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝
Referer包含一个URL,用户从该URL代表的页面出发访问当前请求的页面,使用场景:防盗链、统计网站访问信息。
User-Agent浏览器类型(客户端类型),如果Servlet返回的内容与浏览器类型有关则该值非常有用
UA-Pixels,UA-Color,UA-OS,UA-CPU由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型

MIME概述: 多用途互联网邮件扩展类型,也叫媒体类型

image.png

  • MIME格式: 大类型/小类型,阅读是反过来,比如text/html,读成html文本。

  • 常见MIME类型如下:

image.png

请求体

也叫请求正文。

  • GET请求的请求体是空的,请求参数都是通过请求行传给服务器端。
  • POST请求的请求体可以承载数据,请求头和请求体之间有一个空行作为分割线。
  • 通过表单POST提交的请求体的表现形式主要有三种:

image.png

  • application/x-www-form-urlencoded:会对中文进行URL编码,并且多个参数以&连接,上传文件只能上传文件名称。

  • text/plain:纯文本方式,不会对中文进行URL编码,不会使用&连接多个key-value参数,上传文件只能上传文件名称。

  • multipart/form-data:多部件表现形式,这种方式主要可以完成文件上传,可以将上传的文件名称和文件内容都传递给服务器端。

image.png

  • 总结

请求协议由三部分组成:行头体

  • 请求首行:请求方法 web资源URI http/1.1

  • 请求头:key value方式,不同的请求头配置会告诉服务器端不同的辅助信息。

  • 请求体:承载传输的具体数据,不过请求体中的数据表现形式有三种,这三种都是通过form表单的enctype属性来决定的。

响应协议信息

  • 响应协议由哪几部分组成?
  • 响应协议的状态行包含哪些信息?
  • 状态行中的状态码的含义分别是如何表示的?列出常见的几个状态码及说明?
  • 响应协议的响应头包含哪些头信息?
  • 响应协议的响应体如何理解?

响应协议由哪几部分组成?

响应协议信息,也由三部分组成:状态行、响应头、响应体(响应正文)。

状态行

HTTP/1.1 200 OK

状态行由协议/版本数字形式的状态码状态描述三部分组成。

image.png

image.png

状态码说明:

image.png

响应头

响应头中的信息也是key value方式展现的。

Content-Type是返回消息中非常重要的内容,表示后面的文档属于什么MIME类型。Content-Type: [type]/[subtype]; parameter。例如最常见的就是text/html,它的意思是说返回的内容是文本类型,这个文本又是HTML格式的。原则上浏览器会根据Content-Type来决定如何显示返回的消息体内容
LocationLocation响应报头域用于重定向接受者到一个新的位置。例如:客户端所请求的页面已不存在原先的位置,为了让客户端重定向到这个页面新的位置,服务 器端可以发回Location响应报头后使用重定向语句,让客户端去访问新的域名所对应的服务器上的资源。当我们在JSP中使用重定向语句的时候,服务器 端向客户端发回的响应报头中,就会有Location响应报头域。
ServerServer响应报头域包含了服务器用来处理请求的软件信息。它和User-Agent请求报头域是相对应的,前者发送服务器端软件的信息,后者发送客户 端软件(浏览器)和操作系统的信息。下面是Server响应报头域的一个例子:Server: Apache-Coyote/1.1
WWW-AuthenticateWWW-Authenticate响应报头域必须被包含在401(未授权的)响应消息中,这个报头域和前面讲到的Authorization请求报头域是 相关的,当客户端收到401响应消息,就要决定是否请求服务器对其进行验证。如果要求服务器对其进行验证,就可以发送一个包含了 Authorization报头域的请求,下面是WWW-Authenticate响应报头域的一个例子:WWW-Authenticate: Basic realm="Basic Auth Test!" 从这个响应报头域,可以知道服务器端对我们所请求的资源采用的是基本验证机制。
Content-Length表示响应消息正文的长度
ExpiresExpires实体报头域给出响应过期的日期和时间。通常,代理服务器或浏览器会缓存一些页面。当用户再次访问这些页面时,直接从缓存中加载并显示给用 户,这样缩短了响应的时间,减少服务器的负载。为了让代理服务器或浏览器在一段时间后更新页面,我们可以使用Expires实体报头域指定页面过期的时 间。当用户又一次访问页面时,如果Expires报头域给出的日期和时间比Date普通报头域给出的日期和时间要早(或相同),那么代理服务器或浏览器就 不会再使用缓存的页面而是从服务器上请求更新的页面。不过要注意,即使页面过期了,也并不意味着服务器上的原始资源在此时间之前或之后发生了改变。
Last-ModifiedLast-Modified实体报头域用于指示资源最后的修改日期及时间。
Set-Cookie设置和页面关联的Cookie。Servlet不应使用response.setHeader("Set-Cookie", ...),而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。
Allow服务器支持哪些请求方法(如GET、POST等)。
Content-Encoding文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept-Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。
响应体

响应体,也叫响应正文,里面包含服务器发给客户端的web资源信息。

响应正文信息返回到浏览器时,浏览器需要根据响应头中Content-type设置的MIME类型来打开响应正文信息。

GET请求和POST请求的区别

  • 提交数据的方式不同

    GET是通过请求行提交请求参数的。
    ​
    POST是通过请求体提交请求参数的。
    
  • 使用场景不同

    GET请求的目的是获取到数据,简单点说,就是客户端向服务器端要东西
    ​
    POST请求的目的是给服务器提交数据。就是客户端向服务器端给东西。
    
  • 传递参数的大小不同

    GET请求是通过请求行中的请求URL传递给客户端的。HTTP协议对请求URL的长度没有限制,但是不同的浏览器对请求URL长度是由限制的。
    ​
    POST请求是通过请求体传递请求参数的。
    
  • 总之POST传递的请求参数大小比GET方式要大,要多。

什么是RESTful?

  • 什么是REST?
REST(英文:Representational State Transfer,简称 REST,意思是:(资源)表述性状态转化)描述了一个架构样式的网络系统, 比如 web 应用程序。
 
它是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
 
它本身并没有什么实用性,其核心价值在于如何设计出符合 REST 风格的网络接口。
  • 什么是RESTful?
REST指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。
  • RESTful的特性:

    • 资源(Resources):
    网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个 URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的 URI 就可以,因此 URI 即为每一个资源的独一无二的识别符。
    
  • 表现层(Representation):
  把资源具体呈现出来的形式,叫做它的表现层 (Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
  • 状态转化(State Transfer):
  每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器, 必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “ 表现层状态转化” 。
  
  具体说, 就是 HTTP 协议里面,四个表示操作方式的动词:GET 、POST 、PUT 、DELETE 。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
  • 如何设计RESTful应用程序的API
路径设计:数据库设计完毕之后,基本上就可以确定有哪些资源要进行操作,相对于的路径也可以设计出来
动词设计:也就是针对资源的具体操作类型,由HTTP动词表示,常用的HTTP动词如下:POST、DELETE、PUT、GET
  • RESTful 的示例:
/account/1  HTTP GET :  得到 id = 1 的 account
​
/account/1  HTTP DELETE: 删除 id = 1 的 account
​
/account/1  HTTP PUT:   更新 id = 1 的 account

SpringMVC对RESTful的支持

RESTful的URL路径变量

  • URL-PATTERN :设置为 / ,方便拦截RESTful 请求。
<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
  • @PathVariable:可以解析出来URL中的模板变量{id}

    • URL:
    http://localhost:8080/ssm/item/1/zhangsan
    
  • Controller:
@RequestMapping(“{id}/{name}”)      
@ResponseBody      
public Item queryItemById(@PathVariable Integer id, @PathVariable String name){}     

RESTful的CRUD

- @RequestMapping:通过设置method属性值,可以将同一个URL映射到不同的`HandlerMethod`方法上
​
- @GetMapping、@PostMapping、@PutMapping、@DeleteMapping注解同`@RequestMapping`注解的`method`属性设置。

RESTful的资源表述

RESTful服务中一个重要的特性就是一种资源可以有多种表现形式,在SpringMVC中可以使用ContentNegotiatingManager这个内容协商管理器来实现这种方式。

内容协商的方式有三种:

  • 扩展名:比如.json表示我要JSON格式数据、.xml表示我要XML格式数据
  • 请求参数:默认是”format”
  • 请求头设置Accept参数:比如设置Accept为application/json表示要JSON格式数据

不过现在RESTful响应的数据一般都是JSON格式,所以一般也不使用内容协商管理器,直接使用@ResponseBody注解将数据按照JSON格式返回。