【SpringMVC】快速体验 SpringMVC接收数据 第一期

文章目录

  • [一、SpringMVC 介绍](#一、SpringMVC 介绍)
    • [1.1 主要作用](#1.1 主要作用)
    • [1.2 核心组件和调用流程理解](#1.2 核心组件和调用流程理解)
  • 二、快速体验
  • 三、SpringMVC接收数据
  • [3.1 访问路径设置](#3.1 访问路径设置)
    • [3.1.1 精准路径匹配](#3.1.1 精准路径匹配)
    • [3.1.2 模糊路径匹配](#3.1.2 模糊路径匹配)
    • [3.1.3 类和方法级别区别](#3.1.3 类和方法级别区别)
    • [3.1.4 附带请求方式限制](#3.1.4 附带请求方式限制)
    • [3.1.5 进阶注解 与 常见配置问题](#3.1.5 进阶注解 与 常见配置问题)
  • [3.2 接收参数(重点)](#3.2 接收参数(重点))
    • [3.2.1 param 和 json参数比较](#3.2.1 param 和 json参数比较)
    • [3.2.2 param参数接收](#3.2.2 param参数接收)
    • [3.2.3 路径 参数接收](#3.2.3 路径 参数接收)
    • [3.2.4 json参数接收](#3.2.4 json参数接收)
  • [3.3 接收Cookie数据](#3.3 接收Cookie数据)
  • [3.4 接收请求头数据](#3.4 接收请求头数据)
  • [3.5 原生Api对象操作](#3.5 原生Api对象操作)
  • [3.6 共享域对象操作](#3.6 共享域对象操作)
    • [3.6.1 属性(共享)域作用回顾](#3.6.1 属性(共享)域作用回顾)
    • [3.6.2 Request级别属性(共享)域](#3.6.2 Request级别属性(共享)域)
    • [3.6.3 Session级别属性(共享)域](#3.6.3 Session级别属性(共享)域)
    • [3.6.4 Application级别属性(共享)域](#3.6.4 Application级别属性(共享)域)
  • 总结
    • [@EnableWebMvc 原理](#@EnableWebMvc 原理)

一、SpringMVC 介绍

SpringMVC 官网 文档

面向表述成controller的框架

原生servlet API 开发:

java 复制代码
protected void doGet(HttpServletRequest request, HttpServletResponse response) 
                                                        throws ServletException, IOException {  
    String userName = request.getParameter("userName");
    
    System.out.println("userName="+userName);
}

基于SpringMVC开发代码:

java 复制代码
@RequestMapping("/user/login")
public String login(@RequestParam("userName") String userName,Sting password){
    
    log.debug("userName="+userName);
    //调用业务即可
    
    return "result";
}

1.1 主要作用

SSM框架构建起单体项目的技术栈需求!其中的SpringMVC负责表述层(控制层)实现简化!

SpringMVC的作用主要覆盖的是表述层,例如:

  • 请求映射
  • 数据输入
  • 视图界面
  • 请求分发
  • 表单回显
  • 会话控制
  • 过滤拦截
  • 异步交互
  • 文件上传
  • 文件下载
  • 数据校验
  • 类型转换
  • 等等等

最终总结:

  1. 简化前端参数接收( 形参列表 )
  2. 简化后端数据响应(返回值)
  3. 以及其他...

1.2 核心组件和调用流程理解

DispatcherServlet 做整体请求处理调度。以及其他组件辅助对请求和响应的处理呈现。

SpringMVC处理请求流程:

SpringMVC涉及组件理解:

  1. DispatcherServlet : SpringMVC提供,我们需要使用web.xml配置使其生效,它是整个流程处理的核心,所有请求都经过它的处理和分发!
  2. HandlerMapping : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它内部缓存handler(controller方法)handler访问路径数据 ,被DispatcherServlet调用,用于查找路径对应的handler
  3. HandlerAdapter : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它可以处理请求参数和响应数据 ,每次DispatcherServlet都是通过handlerAdapter间接调用handler,他是handler和DispatcherServlet之间的适配器
  4. Handler : handler又称处理器,他是Controller类内部的方法简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果!
  5. ViewResovler : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效!视图解析器主要作用简化模版视图页面查找的,但是需要注意,前后端分离项目,后端只返回JSON数据,不返回页面,那就不需要视图解析器!所以,视图解析器,相对其他的组件不是必须的!

二、快速体验

  1. 需求
  2. 配置分析
  • DispatcherServlet : 设置处理所有请求!
  • HandlerMapping,HandlerAdapter,Handler 需要加入到IoC容器,供DispatcherServlet调用!
  • Handler自己声明(Controller)需要配置HandlerMapping中供DispatcherServlet查找!
  1. 准备项目
  • 创建项目
    springmvc-base-quick
  • 导入依赖

web -> servlet

ioc -> spring-context

mvc -> spring-webmvc

xml 复制代码
<properties>
    <spring.version>6.0.6</spring.version>
    <servlet.api>9.1.0</servlet.api>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <!-- springioc相关依赖  -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- web相关依赖  -->
    <!-- 在 pom.xml 中引入 Jakarta EE Web API 的依赖 -->
    <!--
        在 Spring Web MVC 6 中,Servlet API 迁移到了 Jakarta EE API,因此在配置 DispatcherServlet 时需要使用
         Jakarta EE 提供的相应类库和命名空间。错误信息 "'org.springframework.web.servlet.DispatcherServlet'
         is not assignable to 'javax.servlet.Servlet,jakarta.servlet.Servlet'" 表明你使用了旧版本的
         Servlet API,没有更新到 Jakarta EE 规范。
    -->
    <dependency>
        <groupId>jakarta.platform</groupId>
        <artifactId>jakarta.jakartaee-web-api</artifactId>
        <version>${servlet.api}</version>
        <scope>provided</scope>
    </dependency>

    <!-- springwebmvc相关依赖  -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>

</dependencies>
  • 模块转为maven/web

  • controller 声明

java 复制代码
/**
 * @Description: handler -> springmvc/hello return "hello springmvc";
 */
@Controller
public class HelloController {

    // 对外访问的地址 到handlerMapping注册的注解
    @RequestMapping("springmvc/hello")
    // 直接返回字符串给前端,不用找视图解析器
    @ResponseBody
    public String hello(){
        System.out.println("HelloController.hello");
        // 返回给前端
        return "hello springmvc";
    }
}
  • SpringMVC 核心组件配置类
java 复制代码
/**
 * @Description: 
 *      1. controller 配置IOC容器
 *      2. handlerMapping handlerAdapter 加入到IOC容器
 */
@Configuration
@ComponentScan("com.wake.controller")
public class MvcConfig {
    
    @Bean
    public RequestMappingHandlerMapping handlerMapping(){
        return new RequestMappingHandlerMapping();
    }
    
    @Bean
    public RequestMappingHandlerAdapter handlerAdapter(){
        return new RequestMappingHandlerAdapter();
    }
}
  • SpringMVC 环境搭建
java 复制代码
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * @Description: 可以被web项目加载,会初始化IOC容器,会设置dispatcherServlet的地址
 */
public class SpringMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    /**
     * 设置SpringMVC对应的配置
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{MvcConfig.class};
    }

    /**
     * 配置SpringMVC内部自带servlet的访问地址
     * @return
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}
  • 测试
    • 配置tomcat 启动项目

三、SpringMVC接收数据

3.1 访问路径设置

  • 原生servlet注解:
    • @WebServlet("必须使用 / 打头")
  • springmvc 注解:
    • @RequestMapping("不必须使用 / 打头")
    • user/login 或 /user/login

3.1.1 精准路径匹配

精准地址: /user/login

java 复制代码
@Controller
public class UserController {

    @RequestMapping
    public void general(){

    }

    @RequestMapping("user/login")
    @ResponseBody
    public String login(){
        return "login";
    }

    @RequestMapping("user/register")
    @ResponseBody
    public String register(){
        return "register";
    }
}

3.1.2 模糊路径匹配

  • * 代表任意一层字符串
    • /user/*
      • /user/a
      • user/b
      • ...
      • 不能是 /user/a/b
      • 如果想准确匹配两层,那么就写"/*/*"以此类推
  • ** 代表任意层任意字符串
    • /user/**
      • user
      • user/a
      • /user/a/b
      • user/a/b/c/d
      • ...
java 复制代码
    @RequestMapping("/user/*")
    public String general(){
        return "show";
    }

3.1.3 类和方法级别区别

  • 关于 类上和方法上添加 @RequestMapper
    • 类上提取通用的访问地址
    • 方法上是具体的handler地址
    • 访问 : 类地址 + 方法地址
java 复制代码
@Controller
@RequestMapping("/user")
public class UserController {

    // 不写默认是直接使用 /user 地址
    @RequestMapping
    public String general(){
        return "show";
    }

    @RequestMapping("/login")
    @ResponseBody
    public String login(){
        return "login";
    }

    @RequestMapping("/register")
    @ResponseBody
    public String register(){
        return "register";
    }
}

3.1.4 附带请求方式限制

客户端 -> http(get | post | put | delete ) -> ds -> handler

HTTP 协议定义了八种请求方式,在 SpringMVC 中封装到了下面这个枚举类:

java 复制代码
public enum RequestMethod {
  GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}

默认情况下:@RequestMapping("/login") 地址正确,任何请求方式都可以访问。

如果需要特定指定:

  • method = RequestMethod.POST
  • method = {RequestMethod.POST,RequestMethod.GET}
java 复制代码
@Controller
@RequestMapping("/user")
public class UserController {

    //多个用 {}
    @RequestMapping(method = {RequestMethod.POST,RequestMethod.GET})
    public String general(){
        return "show";
    }

    // 单个
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    @ResponseBody
    public String login(){
        return "login";
    }

    // 直接用注解
    @GetMapping("/register")
    @ResponseBody
    public String register(){
        return "register";
    }
}

不符合的请求方式:会出现 405 异常!

3.1.5 进阶注解 与 常见配置问题

@RequestMapping 的 HTTP 方法特定快捷方式变体:

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

注意:

进阶注解只能用在handler方法上!不能添加到类上!

java 复制代码
@RequestMapping("/login",method=RequestMethod.GET)
||
@GetMapping("/login")

报错:

There is already 'demo03MappingMethodHandler' bean method com.wake.mvc.handler.Demo03MappingMethodHandler#empGet() mapped.

原因:

多个 handler 方法映射了同一个地址,导致 SpringMVC 在接收到这个地址的请求时不知道该找哪个 handler 方法处理。

3.2 接收参数(重点)

3.2.1 param 和 json参数比较

  • param

    • 格式 字符串: "key = value & key = value"
    • 参数会被编码为 ASCII 码
      • name=john doe,则会被编码为 name=john%20doe
    • 参数没有顺序限制
    • 参数仅支持字符串类型、数值类型和布尔类型等简单数据类型
    • 参数不支持嵌套
  • json

    • {key:value,key:value}
    • 参数会被编码为 UTF-8
    • 键值对是有序排列,参数是有序的
    • JSON 类型的参数则支持更复杂的数据类型,如数组、对象等
    • 支持嵌套,可以传递更为复杂的数据结构

param 类型的参数适用于单一的数据传递,而 JSON 类型的参数则更适用于更复杂的数据结构传递。

.

在实际开发中,常见的做法是:

在 GET 请求中采用 param 类型的参数,

在 POST 请求中采用 JSON 类型的参数传递。

3.2.2 param参数接收

  • 1. 直接接值
java 复制代码
@Controller
@RequestMapping("param")
public class ParamController {

    @GetMapping("data")
    @ResponseBody
    public String data(String name,int age){
        System.out.println("name : " + name+"age : " +age);
        return "name : " + name+"age : " +age;
    }
 }

http://localhost:8080/param/data?age=0&name=haha

  1. 参数 地址 名称相同
  2. 可以不传递 包装类不报错是直接为Null

包装类参数可以为空,简单数据类型不能为空会报错(500)

Request processing failed: java.lang.IllegalStateException: Optional int parameter 'age' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.

请求处理失败:java.lang.IllegalStateException:可选的 int 参数"age"存在,但由于被声明为基元类型,因此无法转换为 null 值。请考虑将其声明为相应基元类型的对象包装器。


  • 2.@RequestParam注解
java 复制代码
    @GetMapping("data1")
    @ResponseBody
    public String data1(@RequestParam(value = "account") String username,
                        @RequestParam(required = false,defaultValue = "1") int pwd){
        System.out.println(username + " , "+ pwd);
        return username + " , "+ pwd;
    }
  • value = "account" 设置传值地址名字为account,输入别的报错(400)
  • required = false 设置当前参数 非必需 (默认是必须{TRUE}
  • defaultValue = "1" 默认值为1

    400 必须传递数值的参数 没有传递数值

  • 3.特殊场景接值
  1. 集合 多值 ?hbs=A&hbs=B
    参数前需要加注解 不然会报错
java 复制代码
    @GetMapping("data2")
    @ResponseBody
    public String data2(@RequestParam List<String> hbs){
        System.out.println("hbs : "+hbs);
        return "ok!";
    }


  1. 实体类参数
java 复制代码
    @GetMapping("data3")
    @ResponseBody
    public String data3(User user){
        System.out.println("user : "+user);
        return user.toString();
    }

实体类 需要对应属性的setget方法 ->

实体类属性名、形参与地址实参 名字要相同

3.2.3 路径 参数接收

java 复制代码
@Controller
@RequestMapping("path")
@ResponseBody
public class PathController {

    /*
    * path/账号/密码
    *   必须使用@PathVariable
    * */

    @RequestMapping("{account}/{password}")
    public String login(@PathVariable(value = "account") String username,
                        @PathVariable String password){
        System.out.println(username + " , " +password);
        return username + " , " +password;
    }
}

必须写完整格式:否则404

3.2.4 json参数接收

报错: 415 不支持数据类型

原因:

JAVA原生API , 只支持路径参数和param参数 request.getParameter("key"); param 不支持json

json 是前端格式

解决:

  1. 导入JSON处理的依赖
  2. handlerAdapter配置json转换器 (MvcConfig配置类中添加注解
xml 复制代码
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.0</version>
        </dependency>
java 复制代码
@EnableWebMvc   // HandlerAdapter配置JSON转换器

success!!

3.3 接收Cookie数据

java 复制代码
@Controller
@ResponseBody
@RequestMapping("cookie")
public class CookieController {

	// 获取cookie
    @RequestMapping("data")
    public String data(@CookieValue("cookieName") String value){
        System.out.println("cookie : " + value);
        return value;
    }

	// 创建一个cookie
    @GetMapping("cooking")
    public String cookie(HttpServletResponse response){
        Cookie cookie = new Cookie("cookieName", "wake");
        response.addCookie(cookie);
        return "OK!";
    }
}

先创建一个cookie

再获取cookie

3.4 接收请求头数据

java 复制代码
@Controller
@ResponseBody
@RequestMapping("header")
public class HeaderController {

    @RequestMapping("data")
    public String header(@RequestHeader("Host") String host){
        System.out.println("host" + host);
        return "host" + host;
    }
}

3.5 原生Api对象操作

SpringMVC 官方文档 原生API 操作指南

Controller method argument 控制器方法参数 Description
jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse 请求/响应对象
jakarta.servlet.http.HttpSession 强制存在会话。因此,这样的参数永远不会为 null
java.io.InputStream, java.io.Reader 用于访问由 Servlet API 公开的原始请求正文。
java.io.OutputStream, java.io.Writer 用于访问由 Servlet API 公开的原始响应正文。
@PathVariable 接收路径参数注解
@RequestParam 用于访问 Servlet 请求参数,包括多部分文件。参数值将转换为声明的方法参数类型。
@RequestHeader 用于访问请求标头。标头值将转换为声明的方法参数类型。
@CookieValue 用于访问Cookie。Cookie 值将转换为声明的方法参数类型。
@RequestBody 用于访问 HTTP 请求正文。正文内容通过使用 HttpMessageConverter 实现转换为声明的方法参数类型。
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap 共享域对象,并在视图呈现过程中向模板公开。
Errors, BindingResult 验证和数据绑定中的错误信息获取对象!
java 复制代码
/**
 * 如果想要获取请求或者响应对象,或者会话等,可以直接在形参列表传入,并且不分先后顺序!
 * 注意: 接收原生对象,并不影响参数接收!
 */
@Controller
public class ApiController {

    //方案二 :  servletContext 会自动装入到IOC容器 直接全局注入
    @Autowired // ioc容器获取对应类型实体对象(组件) 并且 自动装配
    private ServletContext servletContext;

    public void data1(HttpServletRequest request,
                      HttpServletResponse response,
                      HttpSession session){

        //使用原生对象就可以使用相应方法
        //servletContext [ 1. 最大的配置文件 2.全局最大共享域 3.核心API getRealPath ]
        //方案一 : request 获取 session 获取
        ServletContext servletContext = request.getServletContext();
        ServletContext servletContext1 = session.getServletContext(); 
    }
}

3.6 共享域对象操作

一个 requset 请求的数据 可以存放在公共空间 其他request可以拿数据进行相应操作

3.6.1 属性(共享)域作用回顾

在 JavaWeb 中,共享域指的是在 Servlet 中存储数据,以便在同一 Web 应用程序的多个组件中进行共享和访问。

常见的共享域有四种:ServletContextHttpSessionHttpServletRequestPageContext

  1. ServletContext 共享域:
    • ServletContext 对象可以在整个 Web 应用程序中共享数据,是最大的共享域。
    • 一般可以用于保存整个 Web 应用程序的全局配置信息,以及所有用户都共享的数据。
    • ServletContext 中保存的数据是线程安全的。
  2. HttpSession 共享域:
    • HttpSession 对象可以在同一用户发出的多个请求之间共享数据,但只能在同一个会话中使用。
    • 比如,可以将用户登录状态保存在 HttpSession 中,让用户在多个页面间保持登录状态。
  3. HttpServletRequest 共享域:
    • HttpServletRequest 对象可以在同一个请求的多个处理器方法之间共享数据。
    • 比如,可以将请求的参数和属性存储在 HttpServletRequest 中,让处理器方法之间可以访问这些数据。
  4. PageContext 共享域:
    • PageContext 对象是在 JSP 页面Servlet 创建时自动创建的。
    • 它可以在 JSP 的各个作用域中共享数据,包括pageScoperequestScopesessionScopeapplicationScope 等作用域。

同一 Web 应用程序的多个组件之间传递数据,并且可以将数据保存在不同的共享域中,根据需要进行选择和使用。

3.6.2 Request级别属性(共享)域

用原生API就好了!

  1. 使用原生 request 对象
java 复制代码
@RequestMapping("/attr/request/original")
@ResponseBody
public String testAttrOriginalRequest(
    
        // 拿到原生对象,就可以调用原生方法执行各种操作
        HttpServletRequest request) {
    
    request.setAttribute("requestScopeMessageOriginal", "i am very happy[original]");
    
    return "target";
}
  1. 使用 Model 类型的形参
java 复制代码
@RequestMapping("/attr/request/model")
@ResponseBody
public String testAttrRequestModel(
    
        // 在形参位置声明Model类型变量,用于存储模型数据
        Model model) {
    
    // 我们将数据存入模型,SpringMVC 会帮我们把模型数据存入请求域
    // 存入请求域这个动作也被称为暴露到请求域
    model.addAttribute("requestScopeMessageModel","i am very happy[model]");
    
    return "target";
}
  1. 使用 ModelMap 类型的形参
java 复制代码
@RequestMapping("/attr/request/model/map")
@ResponseBody
public String testAttrRequestModelMap(
    
        // 在形参位置声明ModelMap类型变量,用于存储模型数据
        ModelMap modelMap) {
    
    // 我们将数据存入模型,SpringMVC 会帮我们把模型数据存入请求域
    // 存入请求域这个动作也被称为暴露到请求域
    modelMap.addAttribute("requestScopeMessageModelMap","i am very happy[model map]");
    
    return "target";
}
  1. 使用 Map 类型的形参
java 复制代码
@RequestMapping("/attr/request/map")
@ResponseBody
public String testAttrRequestMap(
    
        // 在形参位置声明Map类型变量,用于存储模型数据
        Map<String, Object> map) {
    
    // 我们将数据存入模型,SpringMVC 会帮我们把模型数据存入请求域
    // 存入请求域这个动作也被称为暴露到请求域
    map.put("requestScopeMessageMap", "i am very happy[map]");
    
    return "target";
}
  1. 使用 ModelAndView 对象
java 复制代码
@RequestMapping("/attr/request/mav")
public ModelAndView testAttrByModelAndView() {
    
    // 1.创建ModelAndView对象
    ModelAndView modelAndView = new ModelAndView();
    // 2.存入模型数据
    modelAndView.addObject("requestScopeMessageMAV", "i am very happy[mav]");
    // 3.设置视图名称
    modelAndView.setViewName("target");
    
    return modelAndView;
}

3.6.3 Session级别属性(共享)域

java 复制代码
@RequestMapping("/attr/session")
@ResponseBody
public String testAttrSession(HttpSession session) {
    //直接对session对象操作,即对会话范围操作!
    return "target";
}

3.6.4 Application级别属性(共享)域

springmvc会在初始化容器的时候,将servletContext对象存储到ioc容器中!

java 复制代码
@Autowired
private ServletContext servletContext;

@RequestMapping("/attr/application")
@ResponseBody
public String attrApplication() {
    
    servletContext.setAttribute("appScopeMsg", "i am hungry...");
    
    return "target";
}

总结

@EnableWebMvc 原理

@EnableWebMvc注解效果等同于在 XML 配置中,可以使用 <mvc:annotation-driven> 元素

@EnableWebMvc

帮助我们添加 :
HandlerMappingHandlerAdapter 以及给这两个都加入到IOC容器和添加上了json转换器

源码:

相关推荐
無限進步D3 小时前
Java 运行原理
java·开发语言·入门
難釋懷4 小时前
安装Canal
java
是苏浙4 小时前
JDK17新增特性
java·开发语言
不光头强4 小时前
spring cloud知识总结
后端·spring·spring cloud
GetcharZp7 小时前
告别 Python 依赖!用 LangChainGo 打造高性能大模型应用,Go 程序员必看!
后端
阿里加多7 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood7 小时前
java中`==`和`.equals()`区别
java·开发语言·python
小小李程序员7 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai