SpringMVC静态资源访问、annotation-driven的使用原理及数据响应模式
一、概述

二、SpringMVC静态资源访问
当我们的web.xml文件这样配置的时候是访问不通的:
java
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--配置前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置一下我们Spring配置文件的路径-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc2.xml</param-value>
</init-param>
<!-- 设置启动时加载-->
<load-on-startup>2</load-on-startup>
</servlet>
<!-- 配置前端控制器的映射-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>


这是因为在解析时把我们的html文件和图片文件当成了一个Servlet,同时DispatcherServlet又不具备处理这两种文件的能力,但是我们的apache的TomCat具备,但是它的url-pattern被前端控制器覆盖了:
java
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
解决对策如下:
1、方法一:再次激活DefaultServlet url-pattern配置得更精确一点
java
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--配置前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置一下我们Spring配置文件的路径-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc2.xml</param-value>
</init-param>
<!-- 设置启动时加载-->
<load-on-startup>2</load-on-startup>
</servlet>
<!-- 配置前端控制器的映射-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 再次激活DefaultServlet url-pattern配置得更精确一点-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- 下面是指定图片的-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/img/*</url-pattern>
</servlet-mapping>
</web-app>


2、在SpringMVC的配置文件中配置静态资源映射
java
<mvc:resources mapping="/image/*" location="/img/"/>
其中"/image/*"是客户端访问的路径,"/img/"是图片的实际路径,这样配置就将两者映射起来了:

3、在SpringMVC的容器中配置一个DefaultServletHttpRequestHandler 处理器
该方式是注册了一个 DefaultServletHttpRequestHandler 处理器,静态资源的访问都由该处理器去处理,这也是开发中使用最多的:
java
<mvc:default-servlet-handler/>
这样无论静态资源在哪都可以访问得到。
三、annotation-driven的使用原理
3.1背景
但我们去掉下面的代码时页面就跑不出来了
java
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

那为什么会这样呢,不是说没有配置的话SpringMVC不是我帮我们加载它的所有实现吗?我们来看看原理:
从spring-webmvc-5.3.7.jar的spring.handlers进入MvcNamespaceHandler的DefaultServletHandlerBeanDefinitionParser类,看它的Pase方法:
java
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
java
parserContext.getRegistry().registerBeanDefinition(handlerMappingBeanName, handlerMappingDef);
那面那两段代码已经表明是将(SimpleUrlHandlerMapping.class注册到SpringMVC容器里面了,然而(SimpleUrlHandlerMapping.class本质又是一个HandlerMapping:

所以SpringMVC容器就不会再默认加载别的HandlerMapping了,SimpleUrlHandlerMapping又无法处理这些静态资源,所以最终无法加载无法。
如果使用 <mvc:resources mapping="/image/*" location="/img/"/>,也是一样的,其内部依然会自己注册一个SimpleUrlHandlerMapping。
3.2解决对策
使用mvc:default-servlet-handler/,该标签内部会帮我们注册 RequestMappingHandlerMapping、注册 RequestMappingHandlerAdapter 并注入 Json 消息转换器、日期转换器等。
PS:mvc:annotation-driven 标签在不同的版本中,帮我们注册的组件不同,Spring 3.0.X 版本注册是 DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter,由于框架的发展,从 Spring 3.1.X 开始注册组件变为 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter。
四、数据响应同步模式:同步转发与重定向(传统)


4.1同步转发
java
@GetMapping(value = {"/quick","/quickxxx","/quickyyy"})
public String quick()
{
System.out.println("quick is running");
return "forward:/index.jsp";
}

4.2重定向
java
@GetMapping(value = {"/quick","/quickxxx","/quickyyy"})
public String quick()
{
System.out.println("quick is running");
return "redirect:/index.jsp";
}

地址改变了。
4.3在转发的过程中通过ModelAndView携带实体数据
java
// 转发方式:在转发的过程中携带实体数据,一般是通过ModelAndView来携带的
@GetMapping("/res3")
public ModelAndView res3(ModelAndView modelAndView){
// 模型与视图
// 先准备模型:就是将一个bean封装到模型中(相当于数据封装)
User user = new User();
user.setUsername("MM");
user.setAge(18);
modelAndView.addObject(user);
// 下面是准备视图ModelAndView,
modelAndView.setViewName("index.jsp");
// 访问"/res3",返回一个ModelAndView,Spring收到以后先解析视图,SpringMVC收到后解析视图将模型存储到域当中,视图就可以获取模型显示数据了
// ModelAndView不仅可以在参数列表中,还可以在方法中直接new一个,使用Modle也有
// ModelAndView使用得较少只是了解一下
return modelAndView;
}
4.4直接以响应体的方式进行转发
java
// 直接以响应体的方式进行转发
@GetMapping("/res4")
// 代表告诉SpringMVC框架将响应的数据以响应体的形式进行封装,不要再把它当成视图名了
@ResponseBody
public String res4() {
return "Hello MM";
}
五、数据响应异步模式
5.1回写Json格式的数据
5.1.1简单地封装为字符串:
java
package com.itheima.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.pojo.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ResponseControllerm2 {
//一般异步请求是将带有特定格式的字符串返回,这里是返回特定的字符串,但是逻辑还是使用响应体封装字符串
@GetMapping("/ajax/req1")
@ResponseBody
public String res1(){
return "{\"username\":\"haohao\",\"age\":18}";
}
}
5.1.2使用ObjectMapper将对象自动转化为JSON格式的字符串
java
@GetMapping("ajax/req2")
@ResponseBody
public String red2() throws JsonProcessingException {
User user = new User();
user.setUsername("MM");
user.setAge(19);
// 使用ObjectMapper将类转化为Json格式的字符串
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(user);
return s;
}
5.1.2封装为实体
java
@GetMapping("ajax/req3")
@ResponseBody
public User red3() throws JsonProcessingException {
User user = new User();
user.setUsername("MM");
user.setAge(19);
return user;
}
当然如果很多方法都需要使用到@ResponseBody,也可以直接将@ResponseBody写到类上面去:
java
@ResponseBody
@Controller
public class ResponseControllerm2 {
我们在进行前后端开发的过程中常常是将JSON和Resful风格结合起来,此时我们就可以使用一个注解:@RestController写在类上面,@RestController是@ResponseBody和@Controller的结合:
java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Controller;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(
annotation = Controller.class
)
String value() default "";
}