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

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 "";
}
相关推荐
人间寥寥情难诉1 小时前
LRU算法本地实现
java·算法·spring
djBe17esS1 小时前
实战:Java 日志中打印服务器 IP,快速区分多服务器日志归属
java·服务器·tcp/ip
woai33642 小时前
JVM学习-基础篇-垃圾回收
java·jvm·学习
七夜zippoe2 小时前
应用安全实践(一):常见Web漏洞(OWASP Top 10)与防护
java·前端·网络·安全·owasp
nFBD29OFC2 小时前
Spring Cloud生态地图——注册、配置、网关、负载均衡与可观测的组合拳
spring·spring cloud·负载均衡
Zzj_tju2 小时前
Java 从入门到精通(十一):异常处理与自定义异常,程序报错时到底该怎么处理?
java·开发语言
aP8PfmxS22 小时前
Lab3-page tables && MIT6.1810操作系统工程【持续更新】
java·linux·jvm
无籽西瓜a2 小时前
【西瓜带你学设计模式 | 第十二期 - 装饰器模式】装饰器模式 —— 动态叠加功能实现、优缺点与适用场景
java·后端·设计模式·软件工程·装饰器模式
吴声子夜歌2 小时前
Node.js——zlib压缩模块
java·spring·node.js