SpringMVC

1、MVC

  • 什么是MVC?

    MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分。

    M:Model,模型层,指工程中的JavaBean,作用是处理数据。

    JavaBean分为两类:

    • 一类称为实体类Bean:专门存储业务数据的,如Student、User等
    • 一类称为业务处理Bean:指Service或Dao对象,专门用于处理业务逻辑和数据访问

    V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据。

    C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器

    MVC的工作流程:用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器。


2、什么是SpringMVC?

  • 什么是SpringMVC?

    SpringMVC是Spring的一个后续产品,是Spring的一个子项目。SpringMVC是Spring为表述层开发提供的一整套完备的解决方案。在表述层框架历经Strust、WebWork、Strust2等诸多产品的历代更迭之后,目前业界普遍选择了SpringMVC作为JavaEE项目表述层开发的首选方案。

    三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet

  • 为什么需要SpringMVC?

    1. Spring家族原生产品,与IOC容器等基础设施无缝对接
    2. 基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一处理
    3. 表述层细分领域需要解决的问题全方位覆盖,提供全面解决方案
    4. 代码清新简洁,大幅度提升开发效率
    5. 内部组件化程序高,可插拔式组件即插即用,想要什么功能配置相应组件即可
    6. 性能卓著,尤其适合现代大型、超大型互联网项目要求
  • 如何使用SpringMVC?

    1. 创建maven工程
      • 添加web模块
      • 打包方式:war
      • 引入依赖
    2. 配置web.xml
<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
  <servlet-name>springMVC</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-
class>
  <!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
  <init-param>
    <!-- contextConfigLocation为固定值 -->
    <param-name>contextConfigLocation</param-name>
    <!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的
src/main/resources -->
    <param-value>classpath:springMVC.xml</param-value>
  </init-param>
  <!--
作为框架的核心组件,在启动过程中有大量的初始化操作要做
而这些操作放在第一次请求时才执行会严重影响访问速度
因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
-->
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>springMVC</servlet-name>
  <!--
    设置springMVC的核心控制器所能处理的请求的请求路径
    /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
    但是/不能匹配.jsp请求路径的请求-->
  <url-pattern>/</url-pattern>
</servlet-mapping
    <url-pattern>标签中使用/和/*的区别:

/所匹配的请求可以是/login或.html或.js或.css方式的请求路径,但是/不能匹配.jsp请求路径的请求,因此就可以避免在访问jsp页面时,该请求被DispatcherServlet处理,从而找不到相应的页面

/则能够匹配所有请求,例如在使用过滤器时,若需要对所有请求进行过滤,就需要使用/的写法
1. 创建请求控制器
2. 创建SpringMVC的配置文件

<!-- 自动扫描包 -->
<context:component-scan base-package="com.atguigu.mvc.controller"/>
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
  <property name="order" value="1"/>
  <property name="characterEncoding" value="UTF-8"/>
  <property name="templateEngine">
    <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
      <property name="templateResolver">
        <bean
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
 
          <!-- 视图前缀 -->
          <property name="prefix" value="/WEB-INF/templates/"/>
 
          <!-- 视图后缀 -->
          <property name="suffix" value=".html"/>
          <property name="templateMode" value="HTML5"/>
          <property name="characterEncoding" value="UTF-8" />
        </bean>
      </property>
    </bean>
    </property>
</bean>
<!--
 处理静态资源,例如html、js、css、jpg
 若只设置该标签,则只能访问静态资源,其他请求则无法访问
 此时必须设置<mvc:annotation-driven/>解决问题
-->
<mvc:default-servlet-handler/>
<!-- 开启mvc注解驱动 -->
<mvc:annotation-driven>
  <mvc:message-converters>
    <!-- 处理响应中文内容乱码 -->
    <bean
class="org.springframework.http.converter.StringHttpMessageConverter">
      <property name="defaultCharset" value="UTF-8" />
      <property name="supportedMediaTypes">
        <list>
          <value>text/html</value>
          <value>application/json</value>
        </list>
      </property>
    </bean>
  </mvc:message-converters>
</mvc:annotation-driven>
1. 测试HelloWorld

3、SpringMVC支持ant风格的路径

?:表示任意的单个字符
* :表示任意的0个或多个字符
** :表示任意的一层或多层目录
注意:在使用 ** 时,只能使用/**/xxx的方式


4、SpringMVC支持路径中的占位符(重点)

  • 原始方式:/deleteUser?id=1

  • rest方式:/deleteUser/1

    SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在通过@PathVariable注解,将占位符所表示的数据赋值给控制器的方法的形参。

<a th:href="@{/testRest/1/admin}">测试路径中的占位符-->/testRest</a><br>
@RequestMapping("/testRest/{id}/{username}")
public String testRest(@PathVariable("id") String id,@PathVariable("username") String username){
    System.out.println("id:" + id + ", username:" + username);
    return "success";
}
//最终输出的内容为 --> id:1, username:admin

5、域对象共享数据

  • 想要域对象共享数据使用哪些ServletAPI?

    • 使用ModelAndView向request域对象共享数据
    • 使用Model向request域对象共享数据
    • 使用Map向request域对象共享数据
    • 使用ModelMap向request域对象共享数据
  • Model、ModelMap、Map的关系?

    Model、ModelMap、Map类型的参数其实实质上都是BindingAwareModelMap类型的

public interface Model{}
public class ModelMap extends LinkedHashMap<String,Object>{}
public class ExtendedModelMap extends ModelMap implements Model{}
public class BindingAwareModelMap extends ExtendedModelMap{}

  • 共享数据域
    • 向session域共享数据
    • 向application域共享数据

6、SpringMVC的视图

  • 什么是视图?

    SpringMVC中的视图是View接口,种类有很多,默认有转发视图和重定向视图。

    ① 当工程引入jstl的依赖,转发视图会自动转换为jstlView。

    ② 若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后得到的是ThymeleafView。

  • 为什么需要视图?

    视图的作用是渲染数据,将模型Model中的数据展示给用户。

  • ThymeleafView

    当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转。

    当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示。

<!-- 
    path:设置处理的请求地址
    view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>
注:

当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:
<mvc:annotation-driven />


7、RESTful(重点)

  • 什么是RESTful?

    REST:Representational State Transfer,表现层资源状态转移。

    REST指的是一组架构约束条件和原则,如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。

    REST原则:

    • 对网络上所有的资源都有一个资源标识符
    • 对资源的操作不会改变标识符
    • 同一资源有多种表现形式(xml、json)
    • 所有操作都是无状态的(Stateless)
1)URI和URL的区别:
    URI:  http://example.com/users/
    URL:  http://example.com/users/{user}(one for each user)
2)什么是无状态性:
    使得客户端和服务器端不必保存对方的详细信息,服务器只需要处理当前的请求,不需了解
    请求的历史。
    可以更容易的释放资源,让服务器利用Pool(连接池)技术来提高稳定性和性能。

  • RESTful的实现
操作 传统方式 REST风格
查询操作 getUserById?id=1 user/1 —> get请求方式
保存操作 saveUser user —> post请求方式
删除操作 deleteUser?id=1 user/1 —> delete请求方式
更新操作 updateUser user —> put请求方式
**使用HiddenHttpMethodFilter**

由于浏览器只支持发送get和post方式的请求

SpringMVC提供了HiddenHttpMethodFilter帮助我们将POST请求转换为DELETE或PUT请求

HiddenHttpMethodFilter处理put和delete请求的条件:

① 当前请求的请求方式必须为post(即表单提交)

② 当前请求必须传输请求参数_method
<form action="/user" method="post">
    <input type="hidden" name="_method" value="put" />
</form>
  • 案例架构
功能 URL地址 请求方式
访问首页 / GET
查询全部数据 /employees GET
删除 /employee/2 DELETE
跳转到添加数据页面 /toAdd GET
执行保存 /employee POST
跳转到更新数据页面 /employee/2 GET
执行更新 /employee PUT
1)控制器方法
@Autowired
EmployeeDao employeeDao;

@RequestMapping(value="/employees",method=RequestMethod.GET)
public String getEmployeeList(Model model){
    Collection<Employee> employeeList = employeeDao.getAll();
    model.addAttribute("employeeList",employeeList);
    return "employee_list";
}
2)创建employee_list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Employee Info</title>
    <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
</head>
<body>
     <table border="1" cellpadding="0" cellspacing="0" style="text-align:center;" id="dataTable">
       <tr>
         <th colspan="5">Employee Info</th>
       </tr>
       <tr>
         <th>id</th>
         <th>lastName</th>
         <th>email</th>
         <th>gender</th>
         <th>options(<a th:href="@{/toAdd}">add</a>)</th>
       </tr>
       <tr th:each="employee:${employeeList}">
         <td th:text="${employee.id}"></td>
         <td th:text="${employee.lastName}"></td>
         <td th:text="${employee.email}"></td>
         <td th:text="${employee.gender}"></td>
         <td>
           <a class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>
           <a th:href="@{'/employee/'+${employee.id}}">update</a>
         </td>
       </tr>
      </table>
    </body>
  </html>


8、HttpMessageConverter

  • 什么是HttpMessageConverter?

    HttpMessageConverter,报文信息转换器,将请求报文转换为java对象,或将java对象转换为响应报文

  • 怎么使用HttpMessageConverter?

    HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity,ResponseEntity

    1)@RequestBody

    @RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值

<form th:action="@{/testRequestBody}" method="post">
    用户名:<input type="text" name="username" /> <br/>
    密码:<input type="password" name="password" /> <br/>
    <input type="submit"/>
</form>

@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
    System.out.println("requestBody : "+ requestBody);
    return "success";
}
输出结果:

requestBody :username=admin&password=123456

**2)RequestEntity**

> RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过**getHeaders()**获取请求头信息,通过**getBody()**获取请求体信息。
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
    System.out.println("requestHeader:" + requestEntity.getHeaders());
    System.out.println("requestBody:" + requestEntity.getBody());
    return "success";
}
输出结果:

requestHeader:[host:"localhost:8080", connection:"keep-alive", content-length:"27",cache-control:"max-age=0", sec-ch-ua:"" Not A;Brand";v="99", "Chromium";v="90", "GoogleChrome";v="90"", sec-ch-ua-mobile:"?0", upgrade-insecure-requests:"1".......

3)@ResponseBody

> @ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应体响应到浏览器。
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
    return "success";
}
输出结果:

浏览器页面显示success

> @ResponseBody还可以和@Controller合成一个注解@RestController

**4)ResponseEntity**

> ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文

9、拦截器

  • 拦截器的配置

    SpringMVC中的拦截器用于拦截控制器方法的执行

    SpringMVC中的拦截器需要实现HandlerInterceptor

    SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置

<bean class="com.atguigu.interceptor.FirstInterceptor"></bean>
<ref bean="firstInterceptor"></ref>
<!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 -->
<mvc:interceptor>
    <mvc:mapping path="/**/" >
    <mvc:exclude-mapping path="/testRequestEntity" />
    <ref bean="firstInterceptor"></ref>
</mvc:interceptor>

<!-- 
    以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的
    请求,通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
 -->
  • 拦截器的三个抽象方法

    • preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值标识是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
    • postHandle:控制器方法执行之后执行postHandle()
    • afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()

    若每个拦截器的preHandle()都返回true
    此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
    preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行

    **若某个拦截器的preHandle()返回了false
    **preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行


10、异常处理器

  • 基于配置的异常处理
//SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver

//HandlerExceptionResolver接口的实现类有:
//DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver

//SpringMVC提供了自定义的异常处理器SimpleMappingEexceptionResolver

<bean class="org.springframework.web.servlet.handler.simpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <!-- 
                properties的键表示处理器方法执行过程中出现的异常
                properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
             -->
             <prop key="java.lang.ArithmeticException">error</prop>
        </props>
    </property>
    <!--
        exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
    -->
    <property name="exceptionAttribute" value="ex"></property>
</bean>
  • 基于注解的异常处理
//@ControllerAdvice 将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController{
    //@ExceptionHandler用于设置所标识方法处理的异常
    @ExceptionHandler(ArithmeticException.class)
    //ex表示当前请求中处理中出现的异常对象
    public String handleArtimeticException(Exception ex, Model model){
        model.addAttribute("ex",ex);
        return "error";
    }
}

11、SpringMVC执行流程

  • SpringMVC常用组件

    • DispatcherServlet:前端控制器,不需要工程师开发,由框架提供

      作用:统一处理请求和响应,整个流程控制的中心,由它调用其他组件处理用户的请求

    • HandlerMapping:处理器映射器,不需要工程师开发,由框架提供

      作用:根据请求的url、method等信息查找Handler,即控制器方法

    • Handler:处理器,需要工程师开发

      作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理

    • HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供

      作用:通过HandlerAdapter对处理器(控制器方法)进行执行

    • ViewResolver:视图解析器,不需要工程师开发,由框架提供

      作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView

    • View:视图

      作用:将模型数据通过页面展示给用户

  • DispatcherServlet初始化过程

    DispatcherServlet本质上是一个Servlet,所以天然的遵循Serlvet的生命周期。所以宏观上是Servlet生命周期来进行调度。

    A→初始化WebApplicationContext

    所在类:org.springframework.web.servlet.FrameworkServlet
    

    B→创建WebApplicationContext

    所在类:org.springframework.web.servlet.FrameworkServlet
    

    C→DispatcherServlet初始化策略

    FrameworkServlet创建WebApplicationContext后,刷新容器,调用onRefresh(wac),此方法在DispatcherServlet中进行了重写,调用了initStrategies(context)方法,初始化策略,即初始化DispatcherSerlvet的各个组件。
    
    所在类:org.springframework.web.serlvet.DispatcherServlet
    
protected void initStrategies(ApplicationContext context) {
   initMultipartResolver(context);
   initLocaleResolver(context);
   initThemeResolver(context);
   initHandlerMappings(context);
   initHandlerAdapters(context);
   initHandlerExceptionResolvers(context);
   initRequestToViewNameTranslator(context);
   initViewResolvers(context);
   initFlashMapManager(context);
}
  • DispatcherServlet调用组件处理请求

    A→processRequest()

    FrameworkServlet重写HttpServlet中的service()和doXxx(),这些方法中调用了processRequest(request,response)
    
    所在类:org.springframework.web.servlet.FrameworkServlet
    

    B→doService()

    所在类:org.springframework.web.servlet.DispatcherServlet
    

    C→doDispatch()

    所在类:org.springframework.web.servlet.DispatcherServlet
    

    D→processDispatchResult()

  • SpringMVC的执行流程

    1)用户向服务器发送请求,请求被SpringMVC前端控制器DispatcherServlet捕获

    2)DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:

    a)不存在
    
      i . 再判断是否配置了**mvc:default-servlet-handler**
    
      ii . 如果没配置,则控制台映射查找不到,客户端展示404错误
    
      iii . 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404错误
    
    b)存在则执行下面的流程
    

    3)根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExceptionChain执行链对象的形式返回

    4)DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapter

    5)如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(...)方法【正向】

    6)提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求,在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作

    a)**HttpMessageConveter**:将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
    
    b)数据转换:对请求消息进行数据转换。如String转成Integer、Double等
    
    c)数据格式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等
    
    d)数据验证:验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
    

    7)Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象。

    8)此时将开始执行拦截器的postHandle(...)方法【逆向】

    9)根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View来渲染视图。

    10)渲染视图完毕执行拦截器的afterCompletion(...)方法【逆向】

    11)将渲染结果返回客户端

posted @ 2022-01-21 14:56  ArosyCat  阅读(18)  评论(0编辑  收藏  举报