【JavaEE】创建SpringBoot第一个项目,Spring Web MVC⼊⻔,从概念到实战的 Web 开发进阶之旅

【JavaEE】创建SpringBoot第一个项目,Spring Web MVC⼊⻔,从概念到实战的 Web 开发进阶之旅
💬 欢迎讨论:如对文章内容有疑问或见解,欢迎在评论区留言,我需要您的帮助!

👍 点赞、收藏与分享:如果这篇文章对您有所帮助,请不吝点赞、收藏或分享,谢谢您的支持!

🚀 传播技术之美:期待您将这篇文章推荐给更多对需要学习JavaEE语言、低代码开发感兴趣的朋友,让我们共同学习、成长!

1.什么是 Spring Web MVC?

官⽅对于 Spring MVC 的描述是这样的:

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web MVC”, comes from the name of its source module (spring-webmvc)

引⽤来⾃:Spring官方
翻译为中⽂:

Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从⼀开始就包含在 Spring框架中。它的正式名称“Spring Web MVC”来⾃其源模块的名称(Spring-webmvc),但它通常被称为"Spring MVC".

什么是Servlet呢?

Servlet 是⼀种实现动态⻚⾯的技术。 准确来讲Servlet是⼀套 Java Web 开发的规范,或者说是⼀套 Java Web 开发的技术标准。 只有规范并不能做任何事情,必须要有⼈去实现它. 所谓实现 Servlet 规范,就是真正编写代码去实现 Servlet 规范提到的各种功能,包括类、⽅法、属性等。

Servlet 规范是开放的,除了 Sun 公司,其它公司也可以实现 Servlet 规范,⽬前常⻅的实现了 Servlet 规范的产品包括 Tomcat、Weblogic、Jetty、Jboss、WebSphere 等,它们都被称为"Servlet 容器"。 Servlet 容器⽤来管理程序员编写的 Servlet 类.

从上述定义我们可以得出⼀个信息:Spring Web MVC 是⼀个 Web 框架. 下⾯咱们简称之为: Spring MVC

然⽽要真正的理解什么是 Spring MVC?我们⾸先要搞清楚什么是 MVC?

2. MVC 和 SringMVC

2.1 MVC的定义

MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构设计模式,它把软件系统分为模型、视图和控制器三个基本部分。

在这里插入图片描述
  1. View(视图) : 指在应⽤程序中专⻔⽤来与浏览器进⾏交互,展⽰数据的资源.
  2. Model(模型): 是应⽤程序的主体部分,⽤来处理程序中数据逻辑的部分.
  3. Controller(控制器):可以理解为⼀个分发器,⽤来决定对于视图发来的请求,需要⽤哪⼀个模型来处理,以及处理完后需要跳回到哪⼀个视图。即⽤来连接视图和模型
⽐如去饭店吃饭:
客⼾进店之后, 服务员来接待客⼾点餐, 客⼾点完餐之后, 把客⼾菜单交给前厅, 前厅根据客⼾菜单给后厨下达命令. 后厨负责做饭, 做完之后, 再根据菜单告诉服务员, 这是X号餐桌客⼈的饭.
在这个过程中
服务员就是View(视图), 负责接待客⼾, 帮助客⼾点餐, 以及把饭端到顾客面前
前厅就是Controller(控制器), 根据⽤⼾的点餐情况, 来选择给哪个后厨下达命令.
后厨就是Model(模型), 根据前厅的要求来完成客⼾的⽤餐需求

2.2SpringMVC的定义

MVC 是⼀种架构设计模式, 也是⼀种思想, 而Spring MVC 是对 MVC 思想的具体实现。 但是,Spring MVC还是⼀个Web框架。

总结来说,Spring MVC 是⼀个实现了 MVC 模式的 Web 框架.

所以, Spring MVC主要关注有两个点:

  1. MVC

Web框架
Spring MVC 全称是 Spring Web MVC
其实, 在创建 Spring Boot 项⽬时,我们勾选的 Spring Web 框架其实就是 Spring MVC 框架:

在这里插入图片描述


可以看到,Spring Web的介绍是:
Build web, including RESTful, applications using Spring MVC. Uses Apache Tomcat as the default embedded container.

这时候可能有些学⽣就懵了, 前⾯创建的不是SpringBoot项⽬吗? 怎么⼜变成了Spring MVC项⽬? 他们之间到底有着什么样的关系?

SpringBoot是2014年发布的, Spring 是2004年发布的, 在2014年发布之前, 就不能⽤Spring实现MVC架构吗? 显然不是了。

Spring Boot 只是实现Spring MVC的其中⼀种⽅式⽽已。
Spring Boot 可以添加很多依赖, 借助这些依赖实现不同的功能. Spring Boot 通过添加Spring Web MVC框架, 来实现web功能.

⽐如: 厨房可以⽤来做饭, 但真实实现做饭功能的是⽕以及各种做饭相关的⻝材和⼯具. 厨房就好⽐是SpringBoot, 厨房可以装柜⼦, 实现收纳功能, 装燃⽓灶等, 实现做饭功能. 做饭这个事, 就是MVC, 在⼏千年前, 有⽕有⻝材就可以实现做饭.

不过Spring在实现MVC时, 也结合⾃⾝项⽬的特点, 做了⼀些改变, 相对⽽⾔, 下⾯这个图或许更加合适⼀些.

在这里插入图片描述


不过核⼼没变
⽐如上⾯的例⼦中, 去饭店吃饭. ⼀些饭店是前厅来负责接待客⼾, 帮助客⼾点餐, 也就是Controller来负责接收⽤⼾的请求.

2.3再次理解MVC和SpringMVC

一个项目并不是单纯的是SpringBoot项目,也不是单纯的是SpringMVC项目,他们之间是有交集的。

SpringBoot是帮助我们快速搭建一个项目;SpringMVC是项目中的一个模块,是我们使用中的一个依赖,这个依赖就是下面的代码:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

MVC简单的来说就是把一个项目分成了三个部分:

在这里插入图片描述

现在的SpringMVC把MVC的模式改变了一些:

在这里插入图片描述


我们用一个例子:

在这里插入图片描述

SpringBoot是创建SpringMVC的一种方式,创建MVC方式有很多

当前阶段,MVC的概念发生了一些变化,后端开发人员不涉及前端的也页面开发,所以也就没有view层

所以view又有了一层解释:之前返回的是视图,现在返回的是试图所需要的数据
如下:

在这里插入图片描述

3.学习Spring MVC

3.1学习SpringMVC的重点

既然是 Web 框架, 那么当⽤⼾在浏览器中输⼊了 url 之后,我们的 Spring MVC 项⽬就可以感知到⽤⼾的请求, 并给予响应。

咱们学习Spring MVC, 重点也就是学习如何通过浏览器和⽤⼾程序进⾏交互. 主要分以下三个⽅⾯:

  1. 建⽴连接:将⽤⼾(浏览器)和 Java 程序连接起来,也就是访问⼀个地址能够调⽤到我们的 Spring 程序。
  2. 请求: ⽤⼾请求的时候会带⼀些参数,在程序中要想办法获取到参数, 所以请求这块主要是 获取参数的功能.
  3. 响应: 执⾏了业务逻辑之后,要把程序执⾏的结果返回给⽤⼾, 也就是响应.

对于 Spring MVC 来说,掌握了以上 3 个功能就相当于掌握了 Spring MVC.

3.2 项⽬准备

Spring MVC 项⽬创建和 Spring Boot 创建项⽬相同,在创建的时候选择 Spring Web 就相当于创建了 Spring MVC 的项⽬。

在这里插入图片描述


创建项⽬时, 勾选上 Spring Web 模块即可,如下图所示:

在这里插入图片描述

3.3 建⽴连接

在 Spring MVC 中使⽤RequestMapping注解来实现 URL 路由映射 ,RequestMapping注解的作用就是浏览器连接程序,我们先来看看代码怎么写:
创建好一个SpringBoot项目后,再main路径下会有一个默认的controllerjava程序,实现了⽤⼾通过浏览器和程序的交互:

importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.ModelAttribute;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.ResponseBody;@ControllerpublicclassBasicController{// http://127.0.0.1:8080/hello?name=lisi@RequestMapping("/hello")//路由规则注解@ResponseBodypublicStringhello(@RequestParam(name ="name", defaultValue ="unknown user")String name){return"Hello "+ name;}//......}

上述的@RequestMapping("/hello")中的/hello是一个子路径。

运行main路径下的主程序,再用浏览器搜索:127.0.0.1:8080/hello

在这里插入图片描述

大致流程如下:

在这里插入图片描述

3.3.1RequestMapping 的路径使用

能够借助 @RequestMapping 注解为控制器类或者方法指定请求路径,进而让 Spring 知晓当接收到特定 URL 请求时该调用哪个方法。

RequestMapping的作用:

  1. 放在方法上,修饰方法
  2. 放在类上,修饰类

当我们在类上添加RequestMapping后再按原来的路径搜索:

在这里插入图片描述


搜索结果:

在这里插入图片描述

添加类的路径:http://127.0.0.1:8080/basic/hello,再次搜索:

在这里插入图片描述

3.3.1请求RequestMapping的方式

再浏览器中我们只能使用GET请求,想用更多的请求只能借助Postman工具。

使用postman进行测试后发现RequestMapping可以满足Get、Post等几乎所有请求。

怎么使用规定方式的请求呢?

RequestMapping中使用method属性。

在这里插入图片描述


这时再用postman测试,发现只能get请求能拿到数据

3.4请求

3.4.1 什么是路径请求?

访问不同的路径, 就是发送不同的请求。 在发送请求时, 可能会带⼀些参数, 所以学习Spring的请求, 主要是学习如何传递参数到后端以及后端如何接收。

咱们主要是使⽤浏览器和Postman来模拟传递参数.

后端开发⼈员⽆需过度关注如何传递参数, 了解即可, 实际开发中以Postman测试为主.
⽐如餐厅的厨师, 不关注⽤⼾是在店⾥下单, 还是外卖平台下单, 或者⼩程序下单, 只需要知道如何接收订单, 根据订单做出对应的菜肴就可以了。

3.4.2不传递参数

在上述的举例中如果没有传参设置的是默认值@RequestParam(name = "name", defaultValue = "unknown user") String name

// http://127.0.0.1:8080/hello?name=@RequestMapping("/hello")@ResponseBodypublicStringhello(@RequestParam(name ="name", defaultValue ="unknown user")String name){return"Hello "+ name;}

如果不传递参数,name的默认值为unknown user

在这里插入图片描述

为了方便演示,设置以下的类和方法:

@RequestMapping("/basic")@ControllerpublicclassBasicController{// http://127.0.0.1:8080/hello?name=@RequestMapping("/hello")@ResponseBodypublicStringhello(String name){return"接受到的参数是:"+ name;}}

引用类型不传参:搜索的路径为127.0.0.1:8080/basic/hello

在这里插入图片描述


显然,不传参是默认传的是null

如果我们把方法改成以下的类型的参数:

// http://127.0.0.1:8080/hello?name=@RequestMapping("/hello")@ResponseBodypublicStringhello(int age){return"接受到的参数是:"+ age;}

搜索结果:

在这里插入图片描述


这是为什么呢?

答:int是基本数据类型,基本数据类型是值类型,它们不能接受 null 值。像byteshortlongfloatdoublecharboolean都是基本数据类型,都不能接受null

解决办法:

1.可以将基本数据类型替换为对应的包装类,包装类可以接受 null

@GetMapping("/hello")@ResponseBodypublicStringhello(Integer age){if(age ==null){// 处理 age 为 null 的情况 age =0;// 或者其他默认值}// 处理逻辑return"接受到的参数是:"+age;}

2.使用 @RequestParamdefaultValue 属性为参数设置默认值。

@GetMapping("/hello")@ResponseBodypublicStringhello(@RequestParam(value ="age", defaultValue ="0")int age){// 处理逻辑return"接受到的参数是:"+age;}

3.4.3传递单个参数

引用类型接收单个参数, 在 Spring MVC 中直接⽤⽅法中的参数就可以,⽐如以下代码:

@RequestMapping("/basic")@ControllerpublicclassBasicController{// http://127.0.0.1:8080/hello?name=@RequestMapping("/hello")@ResponseBodypublicStringhello(String name){return"接受到的参数是:"+ name;}}

搜索地址:127.0.0.1:8080/basic/hello?name=zhangsan

在这里插入图片描述


解释:

在这里插入图片描述

3.4.4传递2个参数

方法如下:

// http://127.0.0.1:8080/hello?name=@RequestMapping("/hello")@ResponseBodypublicStringhello(String name,Integer age){return"接受到的name参数是:"+ name+",接受到的age参数是:"+age;}

(1)搜索地址:127.0.0.1:8080/basic/hello?name=zhangsan

在这里插入图片描述

(2)搜索地址:127.0.0.1:8080/basic/hello?age=20

在这里插入图片描述

(3)搜索地址:127.0.0.1:8080/basic/hello?age=20&name=zhangsan

在这里插入图片描述

(4)搜索地址:127.0.0.1:8080/basic/hello?name=zhangsan&age=20

在这里插入图片描述

由(3)(4)可得,传参的顺序不影响接受参数。

3.4.5传递对象

创建一个User类:

publicclassUser{privateString name;privateInteger age;publicStringgetName(){return name;}publicvoidsetName(String name){this.name = name;}publicIntegergetAge(){return age;}publicvoidsetAge(Integer age){this.age = age;}@OverridepublicStringtoString(){return"User{"+"name='"+ name +'\''+", age="+ age +'}';}}

BasicController类中添加如下方法:

@RequestMapping("/save_user")@ResponseBodypublicStringsaveUser(User u){return u.toString();}

在浏览器中搜索:127.0.0.1:8080/basic/save_user?name=lisi&age=20

结果:

在这里插入图片描述


传递参数被包装成了User类型的对象。

3.4.6传递一个数组

方法如下:

@RequestMapping("/basic")@ControllerpublicclassBasicController{// http://127.0.0.1:8080/hello?name=lisi@RequestMapping("/hello")@ResponseBodypublicStringhello(String[] arrayName){return"接收到的参数是:"+Arrays.toString(arrayName);}}

在浏览器搜索:127.0.0.1:8080/basic/hello?arrayName=zhangsan,lisi,wangwu

在这里插入图片描述


当我们请求时,同一个参数赋值多个时,浏览器会帮我们封装为数组,然后传参。

运行后,在浏览器搜索:127.0.0.1:8080/basic/hello?arrayName=zhangsan&arrayName=lisi&arrayName=wangwu

在这里插入图片描述


当我们请求时,同一个参数有多个时,浏览器会帮我们封装为数组,然后传参。

3.4.8传递一个集合

方法如下:

@RequestMapping("/basic")@ControllerpublicclassBasicController{// http://127.0.0.1:8080/hello?name=lisi@RequestMapping("/hello")@ResponseBodypublicStringhello(List<String> listName){return"接收到的参数listName是:"+ listName.toString();}}

在浏览器搜索:127.0.0.1:8080/basic/hello?listName=zhangsan,lisi,wangwu
结果:

在这里插入图片描述


这是什么原因呢?
看一看程序运行的控制面板:

在这里插入图片描述


翻译:未找到接口 java.util.List 的主构造函数或单个唯一构造函数。
通俗一点:浏览器把传递的参数封装成了数组,列表接收不了数组类型的参数

解决办法-使用@RequestParam注解:

@RequestMapping("/basic")@ControllerpublicclassBasicController{// http://127.0.0.1:8080/hello?name=lisi@RequestMapping("/hello")@ResponseBodypublicStringhello(@RequestParamList<String> listName)// @RequeatParam注解{return"接收到的参数listName是:"+ listName.toString();}}

再次搜索:127.0.0.1:8080/basic/hello?listName=zhangsan,lisi,wangwu

在这里插入图片描述


再次搜索:127.0.0.1:8080/basic/hello?listName=zhangsan&listName=lisi&listName=wangwu

在这里插入图片描述

3.4.8后端参数重命名

看如下代码:

// http://127.0.0.1:8080/hello?name=@RequestMapping("/hello")@ResponseBodypublicStringhello(@RequestParam("name")String username){return"接受到的name参数是:"+ username;}

浏览器搜索:127.0.0.1:8080/basic/hello?name=lisi

结果:

在这里插入图片描述


传参的是name,为什么username也有值呢?

答:name的值复制给了username

浏览器搜索:127.0.0.1:8080/basic/hello?username=lisi
结果:

在这里插入图片描述


不是传username的值了吗,为什么会报错呢?

看一看RequestParam的源码:

在这里插入图片描述


required属性改为false后就可以使用username

代码如下:

@RequestMapping("/hello")@ResponseBodypublicStringhello(@RequestParam(value="name",required=false)String username){return"接受到的name参数是:"+ username;}

浏览器搜索:127.0.0.1:8080/basic/hello?username=lisi

结果:

在这里插入图片描述


可以看到,username自己得不到传的值,username只能复制user的值。

如果进行了重命名,就必须要使用@RequestParam注解中的名字。

Read more

解析ESP-SparkBot开源大模型AI桌面机器人的ESP32-S3核心方案

解析ESP-SparkBot开源大模型AI桌面机器人的ESP32-S3核心方案

ESP-SparkBot是一款基于乐鑫ESP32-S3微控制器构建的开源大模型AI桌面机器人。该项目集成了语音交互、图像识别、远程遥控与多媒体功能于一体,通过创新的边缘-云端协同架构,在低成本硬件上实现了复杂的多模态交互能力,为嵌入式AI应用提供了一个高性价比的参考设计。 一、核心硬件与技术特性 ESP-SparkBot的核心是乐鑫ESP32-S3-WROOM-1-N16R8模组。该模组集成了双核Xtensa® LX7 32位处理器,主频高达240MHz,并配备了512KB片上SRAM。这一计算配置为设备在边缘侧执行实时音频采集、预处理和轻量级AI推理(如语音活动检测、本地关键词识别)提供了必要的算力基础。 在连接性方面,ESP32-S3内置了2.4GHz Wi-Fi 4 (802.11 b/g/n)和蓝牙5.0 (BLE)双模无线通信模块。这使得ESP-SparkBot能够稳定地连接网络,与云端大语言模型(LLM)服务进行数据交换,同时也支持通过手机App进行蓝牙配网和本地控制。丰富的I/O接口,包括I2S、I2C、SPI和ADC等,使其能够灵活扩展多种外设。在项目中,这些接

【论文阅读】Gaussian Grouping: Segment and Edit Anything in 3D Scenes

【论文阅读】Gaussian Grouping: Segment and Edit Anything in 3D Scenes

摘要 高斯投影(Gaussian Splatting)实现了高质量、实时的三维场景新视点合成。不过,它仅专注于外观和几何建模,缺乏对细粒度的物体级场景理解。为了解决这一问题,我们提出了 Gaussian Grouping,将高斯点扩展为联合重建和分割开放世界三维场景中的任意内容。我们为每个高斯添加了一个紧凑的身份编码(Identity Encoding),使得这些高斯点能够根据其在三维场景中的物体实例或“物体/背景”的成员关系进行分组。并不依赖昂贵的三维标签,我们在可微渲染过程中通过利用 Segment Anything Model (SAM) 的二维掩码预测,以及引入的三维空间一致性正则化,对身份编码进行监督。与隐式的 NeRF 表示相比,我们表明离散且分组的三维高斯点能够在三维中以高视觉质量、细粒度和高效性来重建、分割和编辑任意内容。 引言 本文旨在构建一个 expressive 的三维场景表示,不仅对外观和几何进行建模,还捕捉场景中每个实例和物体的身份信息。我们的方法以最近的三维高斯投影(Gaussian Splatting)为基础,将其从纯粹的三维重建扩展到细粒度的场景

【无人机动态路径规划】粒子群优化算法PSO求解复杂三维环境下多无人机动态避障路径规划问题附MATLAB代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 🍎 往期回顾关注个人主页:Matlab科研工作室  👇 关注我领取海量matlab电子书和数学建模资料  🍊个人信条:格物致知,完整Matlab代码获取及仿真咨询内容私信。 🔥 内容介绍 一、背景 (一)多无人机应用场景与挑战 在当今科技发展的背景下,多无人机协同作业在众多领域展现出巨大潜力,如物流配送、环境监测、应急救援以及军事侦察等。在复杂三维环境中执行任务时,无人机面临诸多挑战。这些环境可能包含山脉、建筑物、高压电线等各种障碍物,并且环境状态可能动态变化,例如突发的自然灾害导致新的障碍物出现或原有的障碍物发生移动。多无人机之间还需避免相互碰撞,确保协同作业的安全性与高效性。因此,如何为多无人机规划出既能避开障碍物又能适应环境动态变化的路径,成为亟待解决的关键问题。 (二)传统路径规划方法的局限性 传统的路径规划算法,如 Dijkstra 算法和 A * 算法,在简单、静态的环境中能够有效地找到从起点到终点的最优路

PX4无人机|MID360使用FAST_LIO,实现自主飞行及定点——PX4无人机配置流程(六)

PX4无人机|MID360使用FAST_LIO,实现自主飞行及定点——PX4无人机配置流程(六)

PX4固件版本为1.15.4 qgc地面站版本为4.4.5 飞控,使用微空科技MicoAir743V2 机载电脑:12代i5,ubuntu20.04 安装位置:mid360的接口对应飞机的后方 推荐阅读px4+vio实现无人机室内定位_px4+室内视觉定位-ZEEKLOG博客 和飞控连接机载电脑相关,有用 代码参考: PX4|基于FAST-LIO mid360的无人机室内自主定位及定点悬停_fastlio mid360-ZEEKLOG博客 使用视觉或动作捕捉系统进行位置估计 | PX4 指南(主) --- Using Vision or Motion Capture Systems for Position Estimation | PX4 Guide (main) 一.px4飞控设置 建议看官方文档:Using Vision or Motion