Web开发-后端基础篇

与其花时间编写解释你写出的糟糕的代码的注释,不如花时间清理那堆糟糕的代码。

Maven

概述

Maven 是一个 基于 POM(Project Object Model) 的构建工具,它通过一个 pom.xml 文件来管理项目的配置,包括依赖、插件、构建流程等。

Maven的作用包括以下几点:

  • 依赖管理 :自动下载并管理项目所需的第三方库(如JUnit,Spring,MySQL驱动),避免手动引入jar包。
  • Jar包:用于打包Java类、资源文件和元数据的压缩文件格式。
  • 标准化项目结构 :Maven 提倡统一的项目目录结构,关于具体的结构我们下面再讲。
  • 构建自动化 :一条命令即可完成编译、测试、打包、安装、部署等流程。
  • 插件机制 :支持丰富的插件。

1755832290681

Maven项目结构

1
2
3
4
5
6
7
8
9
10
11
12
your-project/
├── pom.xml <-- Maven 项目的核心配置文件
├── src/
│ ├── main/
│ │ ├── java/ <-- 应用/库的源代码(包结构从这里开始)
│ │ ├── resources/ <-- 配置文件、资源文件(会被打包到类路径)
│ │ └── webapp/ <-- (可选)Web 应用的资源,如 HTML、JSP、WEB-INF(用于 Web 项目)
│ └── test/
│ ├── java/ <-- 测试代码(与 main/java 平行的包结构)
│ └── resources/ <-- 测试所需的资源文件
└── target/ <-- 构建输出目录(编译后的类、打包文件等)

Maven Wrapper

Maven Wrapper 是一种让项目自带Maven的“启动器”,它的关键作用是:

  • 保证团队成员使用相同版本的Maven,避免版本不一致的尴尬问题
  • 不需要预先在系统中全局安装Maven,项目开箱即用
  • 常用于CL/CD环境,如GitHub Action,Jenkins,GitLab CL等。
    • CL(持续集成):频繁地(每天多次)将代码集成到主分支,并自动构建、测试 ,从而快速发现问题。
    • CD(持续交付):CI 之后,自动将构建好的产物发布到“可上线”的环境(如测试、预发布环境)

Maven Wrapper 包含的文件

1
2
3
4
5
6
7
8
my-project/
├── mvnw <-- Unix/Linux/Mac 系统用的 shell 脚本
├── mvnw.cmd <-- Windows 系统用的批处理脚本
└── .mvn/
└── wrapper/
├── maven-wrapper.jar <-- 启动器的核心代码
└── maven-wrapper.properties <-- 配置使用的 Maven 版本

当你通过mvnw允许命令时:

1
./mvnw clean install
  1. 检查 .mvn/wrapper/maven-wrapper.properties 中指定的 Maven 版本。
  2. 如果没有这个Maven版本,自动下载。
  3. 使用对应版本的Maven执行 clean install<span> </span>命令

所以在拿到手一个新的Maven项目时,可以优先使用mvnw命令,以确保使用相同的版本减少错误的发生。

Maven配置

Maven官网:Welcome to Apache Maven – Maven

Maven的安装包解压后目录如下:
1755832733852

  • 我们需要将 bin目录添加到环境变量中
  • conf中存储的为Maven的全局配置文件
    • 如果想要用户等级的配置文件,在目录 ${user.home}/.m2/settings.xml

配置阿里云公共镜像

代开maven的配置文件 conf/settings.xml,在 <mirrors></mirrors>标签中添加 mirror 子节点:

1
2
3
4
5
6
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>

配置IDEA

全局Maven配置

全局配置环境需要我们在不打开任何项目时进行配置。

若打开任意项目,进行配置,则为当前项目进行配置,过程一致。

首先点击左下角的齿轮,然后打开设置。
1755833305317

找到Maven设置:

1755833807930

  • Maven主路径:选择Maven文件夹的根目录
  • 用户设置文件:如果添加了私服等,请重重新选择你的配置文件。
  • 或者将你的配置文件放在默认路径下

然后选择运行java程序版本:

1755834008279

最后在Java编译器中也选择相同的Java版本:

1755834099318

创建Maven项目

IDEA中创建Maven项目我们首先新建项目:

1755834231125

填写项目名,以及组织名:

1755834621616

  • GroupidArtifactId 组成了项目的唯一标识符
  • 不同的 Groupid用来区分不同来源的库。
  • 依赖声明:GroupidArtifactId是引入依赖的重要依据。

项目创建完成:

1755835184015

依赖管理

添加依赖

我们打开 pom.xml

  1. 添加标签 <dependencies></dependencies>
1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.18</version>
</dependency>
</dependencies>
  1. <dependencies>中使用 <dependency>引入坐标

  2. 点击刷新按钮,修改生效

    1755835819571

Maven仓库:Maven Repository: Search/Browse/Explore

可以在Maven仓库中搜索想要的插件。

依赖传递

直接依赖:在当前项目中通过依赖配置建立的依赖关系。

间接依赖:被依赖的资源如果依赖其他资源,当前项目间接依赖其他资源。

1755836175761

排除依赖

1755836434136

若项目A 依赖项目B,却并不想要导入项目B中的某个依赖,可以使用依赖排除。

  • 排除依赖:排除依赖指主动断开依赖的资源,被排除的资源无需指定版本。
1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupld>com.itheima</groupld>
<artifactld>maven-projectB</artifactld>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupld>junit</groupld>
<artifactld>junit</artifactld>
</exclusion>
</exclusions>
</dependency>

导入B包,却排除B包依赖的junit

依赖范围

Maven 依赖的 作用范围 (scope)决定了依赖在 编译、测试、运行、打包 等阶段的可用性。

默认情况下,依赖是 compile 范围,可在任何地方使用。

通过 <scope>...</scope> 标签设置依赖范围。

scope 值 主程序 (main) 测试程序 (test) 打包/运行 (package) 示例依赖 典型用途
compile (默认) ✅ 可用 ✅ 可用 ✅ 参与 log4j 常规依赖,编译、测试、运行都需要
test ❌ 不可用 ✅ 可用 ❌ 不参与 junit 仅测试阶段需要
provided ✅ 可用 ❌ 不可用 ❌ 不参与 servlet-api 编译需要,但运行环境会提供
runtime ❌ 不可用 ❌ 不可用 ✅ 参与 JDBC 驱动 运行时需要,编译不需要

使用示例:

1
2
3
4
5
6
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>

生命周期

Maven 构建生命周期包含三个基本的生命周期:

  • clean:清理项目
  • default(build):核心构建流程
  • site:生成项目文档

1755837064023

在Maven中,当你执行生命周期中的任意一个阶段时,都会顺序执行该阶段前面的所有阶段。

但是三大生命周期之间的关系是独立的,例如执行 site不会执行 buildclean

常用阶段

生命周期阶段 作用
clean 移除上一次构建生成的文件
compile 编译项目源代码。
test 运行单元测试。
package 将项目打包成可分发的文件,如 .jar.war
install 将打包文件安装到本地仓库。

运行生命周期

方式一:在IDEA中直接双击对应生命周期

1755837778756

方式二:终端中直接输入 mvn + clean周期 + build周期 + site周期

1755837870027

生命周期的运行原理

生命周期本都是一个个抽象的概念,本身并不执行任何操作。其所有的操作都是由绑定的插件来完成的。

1755837934369

Web入门

spring 官网:Spring | Home

Spring 是一个 轻量级、开源的 Java 企业级应用开发框架 ,目标是通过 IoC(控制反转)AOP(面向切面编程) 简化企业应用开发,提升代码的可维护性与扩展性。

常见子项目:

子项目 作用
Spring Boot 快速构建独立可运行应用
Spring Data 简化数据库访问
Spring Security 安全与权限控制
Spring Cloud 微服务架构支持
Spring Batch 批处理任务

Spring发展到今天已经形成一种开发生态圈,Spring提供了若干子项目,每个项目用于完成特定的功能。

SpringBoot入门

Spring Boot 是 基于 Spring 框架的快速开发框架 ,旨在 简化 Spring 应用的创建、配置与部署 ,让开发者专注于业务逻辑,而不是繁琐的环境搭建与配置。

创建SpringBoot项目

1755872154802

我们不直接创建Maven项目,而是直接创建SpringBoot项目。然后点击下一步:

1755872265595

这里我们选择一些我们需要安装的包,这里我只选择了Spring Web包。

创建完毕:

1755872346789

简单体验SpringBoot

文件 SpringbootQuickStartApplication.java是SpringBoot的启动类。我们通过该类启动SpringBoot项目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package top.ulna520.springbootquickstart;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootQuickStartApplication {

public static void main(String[] args) {
SpringApplication.run(SpringbootQuickStartApplication.class, args);
}

}

我们相对于启动类创建java类:controller/HelloSpring.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package top.ulna520.springbootquickstart.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController //将返回值直接写入HTTP响应体
public class HelloSpring {
@RequestMapping("/hello") //定义请求路径的映射
public String sayHello() {
return "Hello Spring Boot!";
}
}

直接点击上方启动按钮启动服务:

1755872869831

web服务器默认监听端口:8080

1755872851110

Http协议

Http概述

HTTP (Hyper Text Transfer Protocol,超文本传输协议)

  • 定义:规定了 浏览器服务器 之间数据传输的规则
  • 作用:用于传输 HTML、JSON、图片、视频等各种资源

核心特点:

特点 说明 影响
基于 TCP 协议 面向连接,数据传输可靠 需要三次握手,安全性高
请求-响应模型 一次请求对应一次响应 客户端主动发起,服务端被动响应
无状态协议 不记录事务处理的上下文 - 缺点:多次请求间无法共享数据``- 优点:处理速度快、资源占用少

1755873434278

Http-请求协议

HTTP 请求报文由 请求行 + 请求头 + 请求体 组成。

1755873833309

请求行(Request Line)

  • 格式:请求方法 请求路径 协议版本
  • 示例:
    • GET /brand/findAll?name=OPPO&status=1 HTTP/1.1
    • POST /brand HTTP/1.1
  • 作用:告诉服务器要做什么(方法)、访问哪个资源(路径)、使用哪个协议版本

常见请求方法:

  • GET :获取资源
  • POST :提交数据
  • PUT :更新资源(整体替换)
  • PATCH :部分更新资源
  • DELETE :删除资源

请求头(Request Headers)

  • 从第二行开始,格式为 Key: Value
  • 用于传递客户端环境信息、请求参数、数据格式等

常见请求头:

请求头 说明
Host 请求的主机名
User-Agent 浏览器版本,例如 Chrome 浏览器的标识类似 Mozilla/5.0 ... Chrome/79,IE 浏览器的标识类似 Mozilla/5.0 (Windows NT ...) like Gecko
Accept 表示浏览器能接受的资源类型,如 text/*/image/**/* 表示所有
Accept-Language 表示浏览器偏好的语言,服务器可以据此返回不同语言的网页
Accept-Encoding 表示浏览器可以支持的压缩类型,例如 gzipdeflate
Content-Type 请求主体的数据类型
Content-Length 请求主体的大小(单位:字节)

请求体

  • 仅在部分方法(如 POST、PUT、PATCH)中存在
  • 用于传递数据(表单、JSON、XML 等)

示例:

1
2
3
4
5
6
7
{
"status": 1,
"brandName": "黑马",
"companyName": "黑马程序员",
"id": "",
"description": "黑马程序员"
}

GET与POST对比

对比项 GET POST
数据位置 URL 查询参数 请求体
数据长度 有限制(取决于浏览器/服务器) 理论无限制
安全性 参数暴露在 URL 中 参数在请求体中,相对更隐蔽
常用场景 查询数据 提交数据、上传文件

HTTP-响应协议

HTTP 响应报文由 响应行 + 响应头 + 响应体 三部分构成。

1755874487939

响应行

  • 位置 :响应数据的第一行
  • 格式协议版本 状态码 状态描述
  • 示例:
1
HTTP/1.1 200 OK
  • 作用 :告知客户端响应的协议版本、处理结果状态及简要说明

状态码大全:HTTP 响应状态码

状态码分类:

状态码范围 分类 说明
1xx 临时状态码 请求已接收,提示客户端继续请求或忽略
2xx 成功 请求已成功接收并处理完成
3xx 重定向 需要客户端发起新的请求以完成处理
4xx 客户端错误 请求有误,责任在客户端(如资源不存在、未授权、禁止访问等)
5xx 服务器错误 处理出错,责任在服务端(如程序异常)

响应头

  • 从第二行开始,格式为 Key: Value
  • 用于描述响应的元信息(数据类型、长度、连接方式等)

常见响应头:

响应头 说明
Content-Type 响应内容的类型,例如 text/htmlapplication/json
Content-Length 响应内容的长度(字节数)
Content-Encoding 响应内容的压缩算法,例如 gzip
Cache-Control 缓存策略,例如 max-age=300 表示最多缓存 300 秒
Set-Cookie 设置 Cookie 到浏览器,用于会话或状态管理

响应体

  • 位于响应头之后的空行后
  • 存放实际返回的数据内容(HTML、JSON、图片等)

示例(JSON 数据):

1
2
3
4
5
6
7
8
[
{
"id": 1,
"brandName": "阿里巴巴",
"companyName": "腾讯计算机系统有限公司",
"description": "玩玩玩"
}
]

HTTP-协议解析

Http协议解析用于解析客户端发送的HTTP请求,并发送标准的Http响应。通常我们并不自己手动完成这一部分。我们可以直接借助于第三方Web服务器解析http请求,如Tomcat、Jetty、WebLogic、WebSphere等

Web服务器

一种软件程序,对 HTTP 协议 的操作进行封装,使开发者无需直接处理协议细节,从而简化 Web 开发。

核心作用:

  • 协议封装 :屏蔽底层 HTTP 请求/响应的复杂性。
  • 资源提供 :向客户端(浏览器)提供 HTML、CSS、JS、图片等资源。
  • 动态处理 :与后端应用(如 Servlet、PHP、Node.js)协作,生成动态内容。

Tomcat

Tomcat官网:Apache Tomcat® - Welcome!

Tomcat是Apache软件基金会一个核心项目,是一个开源免费的轻量级Web服务器,支持Servlet/JSP少量JavaEE规范。

Tomcat也被称为Web容器、Servlet容器。Servlet程序需要依赖于Tomcat才能运行。

Tomcat安装目录:

1755876082862

启动:执行 bin/start.bat脚本

关闭

  • 直接关闭窗口
  • 执行 bin/shutdown.bat
  • Ctrl + C

端口配置

conf/server:(默认8080)

1755876396327

HTTP协议默认端口号为80,如果将Tomcat端口号改为80,则将来访问Tomcat时,将不用输入端口号。

部署应用程序:将项目放在webapps目录下即可。

SpringBoot中的依赖

起步依赖

Spring Boot 提供了 Starter 机制,利用maven的依赖传递,将常用依赖打包成一个统一入口,简化配置。

依赖名 版本 作用
spring-boot-starter-web 2.7.4 Web 应用开发的核心依赖,包含 Spring MVC、JSON 处理、内嵌 Tomcat 等
spring-boot-starter-test 2.7.4 单元测试依赖,包含 JUnit、Mockito、Spring Test 等

SpringBoot-Web程序的启动依赖于内置Tomcat服务器。

所有的起步依赖:Spring Boot Reference Documentation

在SpringBoot中,起步依赖的的引入无需指定版本version:

1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

这是由于所有的SpringBoo项目都继承于一个版本的SpringBoot父工程:

1
2
3
4
5
6
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

在父工程中会自动引入与版本号对应的起步依赖。

请求响应

SpringMVC流程

SpringBoot本身只是一个Spring 应用的快速开发脚手架,真正负责处理Web请求的是SpringBoot起步依赖 start-web包中的SpringMVC框架。

1755916210434

1755917254433

流程:

阶段 主要参与者 关键动作 备注
1. 请求进入 浏览器 → Tomcat 浏览器发起 HTTP 请求 URL 可能包含路径参数、查询参数
2. 封装请求 Tomcat 创建 HttpServletRequest / HttpServletResponse 对象 由 Servlet 容器负责
3. 前端控制器 DispatcherServlet 统一入口,拦截请求 在 web.xml 或 Java Config 中注册
4. 查找处理器 HandlerMapping 根据 URL 找到对应的 Handler(通常是 Controller 方法) 支持多种映射策略(注解、XML 等)
5. 调用处理器 HandlerAdapter 适配并调用目标 Controller 方法 负责参数绑定、类型转换
6. 返回结果 Controller 返回 ModelAndView 或其他类型(如 @ResponseBody REST 接口可能直接返回 JSON
7. 视图解析 ViewResolver 将视图名解析为具体视图(JSP、Thymeleaf 等) REST 场景可跳过
8. 渲染视图 视图对象 将模型数据渲染为 HTML/JSON 输出写入 HttpServletResponse
9. 响应返回 Tomcat 将响应发送给浏览器 HTTP 状态码、响应头、响应体

BS架构:浏览器 / 服务器架构

CS架构:客户端 / 服务器架构

Postman

postman官网:Postman

Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。

作用:常用于进行接口测试。

1755917995877

可以模拟浏览器发送各种请求,用来测试程序API

获取简单参数

原始方式

HttpServletRequest 对象代表了客户端发送给Servlet的HTTP请求。通过这个对象,你可以获取到请求的所有信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package top.ulna520.springbootquickstart.controller;

//import...

@RestController
public class UserController {
@RequestMapping("/user")
public String getNameAndAge(HttpServletRequest request) {
String name = request.getParameter("name");
String age = request.getParameter("age");

int ageInt = Integer.parseInt(age);
System.out.println("name:" + name + ",age:" + ageInt);
return "name:" + name + ",age:" + ageInt;
}
}

常用方法:

  • getParameter(String name):获取指定名称的请求参数的值(返回 String)。如果参数不存在或有多个同名参数,行为可能不确定。
  • getParameterValues(String name): 获取指定名称的所有请求参数的值(返回 String[])。
  • getParameterMap(): 获取所有请求参数的 Map<String, String[]>

使用GET请求:

1755919377600

SpringMVC方法

参数名与形参变量名相同,定义形参即可接收参数。

1
2
3
4
5
6
7
8
9
10
11
12
package top.ulna520.springbootquickstart.controller;

// import...

@RestController
public class UserController {
@RequestMapping("/user")
public String getNameAndAge(String name, Integer age) {
System.out.println("name:" + name + ",age:" + age);
return "name:" + name + ",age:" + age;
}
}

RequestMapping允许所有的请求方式

  • 当没有客户端没有发送形参名一致的参数时,值为 null

使用post请求:

1755919648159

@RequestParam

如果方法形参名与请求参数名称不匹配,或者需要对于参数进行一些额外配置,可以使用RequestParam。

基本用法:

1
2
3
4
@GetMapping("/hello")
public String hello(@RequestParam("name") String username) {
return "Hello, " + username;
}

常用属性:

属性名 类型 说明 默认值
value / name String 请求参数名(两者等价,二选一即可) 必填(除非参数名与方法形参名一致且开启参数名推断)
required boolean 是否必须提供该参数 true
defaultValue String 当参数缺失或为空时使用的默认值,设置后 required 自动失效 null

实体参数

创建实体

实体参数就是将Http请求的参数封装到一个实体对象中。

首先我们创建User类:Model/User.java

1
2
3
4
5
6
7
8
9
10
11
12
package top.ulna520.springbootquickstart.Model;

public class User {
private String name;
private Integer age;

public User(Integer age, String name) {
this.age = age;
this.name = name;
}
}

要想能够成功封装,一下两个条件满足一个即可:

  1. 只有一个 public有参构造函数,若有多个构造方法,Spring不知道该用哪个。
  2. 有无参构造方法(优先使用),以及有对饮成员的set方法。

推荐的方法:将 User 定义成标准 JavaBean

  • 提供 public 无参构造器;
  • 提供字段的 getter/setter;
  • 这样 Spring 的 DataBinder 就能通过无参构造创建实例并按名称填充属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package top.ulna520.springbootquickstart.Model;

public class User {
private String name;
private Integer age;

public void setAge(Integer age) {
this.age = age;
}

public void setName(String name) {
this.name = name;
}
}

实体变量

1
2
3
4
5
6
7
8
@RestController
public class UserController {
@RequestMapping("/user")
public String getNameAndAge(User user) {
System.out.println(user);
return user.toString();
}
}

Spring会自动将参数封装进入变量,没有参数对应的值为null;

1755921743737

实例嵌套

我们再创建一个Address类,并且Address类是User类的一个成员变量:

Model/Address.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package top.ulna520.springbootquickstart.Model;

public class Address {
private String province;
private String city;
private String street;

public void setProvince(String province) {
this.province = province;
}

public void setCity(String city) {
this.city = city;
}

public void setStreet(String street) {
this.street = street;
}
}

Model/User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package top.ulna520.springbootquickstart.Model;

public class User {
private String name;
private Integer age;
private Address address;

public void setAge(Integer age) {
this.age = age;
}

public void setName(String name) {
this.name = name;
}

public void setAddress(Address address) {
this.address = address;
}

public Address getAddress() { //嵌套类必须要有grtter
return address;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}

如果没有嵌套类的getter,导致 Spring 无法自动创建并绑定 address,最终为 null。

1755922489489

数组集合参数

数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数

1
2
3
4
5
6
// 3. 数组集合参数
@RequestMapping("/arrayParam")
public String arrayParam(String[] hobby) {
System.out.println(Arrays.toString(hobby));
return "OK";
}

集合参数:请求参数名与形参集合名称相同且请求参数为多个,@RequestParam绑定参数关系

1
2
3
4
5
@RequestMapping("/listParam")
public String listParam(@RequestParam List<String> hobby) {
System.out.println(hobby);
return "OK";
}

这里 @RequestParam用于绑定集合参数。

日期时间类型的参数

请求示例

1
http://localhost:8080/dateParam?updateTime=2022-12-12 10:05:45

控制器示例

1
2
3
4
5
6
7
@RequestMapping("/dateParam")
public String dateParam(
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime
){
System.out.println(updateTime);
return "OK";
}

@DateTimeFormat 注解

  • 将请求参数的字符串按指定格式解析为 LocalDateTimeLocalDateLocalTime 等日期类型。
  • 常用属性:
    • pattern:日期时间格式化模式(必须与请求参数格式一致)。
    • 常见模式:yyyy-MM-ddyyyy-MM-dd HH:mm:ss

请求参数的日期格式必须与pattern完全一致,否则抛出400错误。

Json参数

postman发送json请求

1755923999960

Sping接收Json数据

首先定义好与Json数据对应数据模型如:User 类,Address类。

1
2
3
4
5
6
7
8
@RestController
public class UserController {
@RequestMapping("/user")
public String getNameAndAge(@RequestBody User user) {
System.out.println(user);
return user.toString();
}
}
  • @RequestBody注解用于将请求体中的json数据绑定到Java对象。

路径参数

声明动态参数:

1
2
3
4
5
@RequestMapping("/path/{id}")	//声明动态参数
public String pathParam(@PathVariable Integer id) {
System.out.println(id);
return "OK";
}
  • @PathVariable 注解:用于获取路径参数

示例:

1
2
3
4
5
6
@GetMapping("/{year}/{month}/{day}/{filename}/")
public Message returnBlogByPath(
@PathVariable int year,
@PathVariable int month,
@PathVariable int day,
@PathVariable String filename)

响应

@ResponseBody

  • 类型 :方法注解 / 类注解

  • 位置

    • 方法上 :仅作用于该方法
    • 类上 :作用于类中所有方法
  • 功能

    • 将方法返回值直接写入 HTTP 响应体(Response Body)
    • 如果返回值是 实体对象 / 集合 ,会自动序列化为 JSON 格式响应

示例:

1
2
3
4
5
6
@RequestMapping("/hello")
@ResponseBody
public String hello() {
System.out.println("Hello World~");
return "Hello World~";
}

如果一整个类的方法都直接返回数据,可以直接使用 @RestController注解。

等价于:

1
2
@Controller
@ResponseBody

@Controller

  • 用于标注一个是控制器组件(Controller),由SpringMVC扫描并注册到容器中,用来接收和处理前端请求。

统一响应封装

背景问题:

  • 不同接口返回类型不一致(String、对象、集合),前端解析复杂
  • 缺乏统一的响应格式,不便于状态码、提示信息、数据的统一管理

设定一个返回类,所有的返回信息由Result类进行包装:

1
2
3
4
5
6
7
8
9
public class Result {
// 响应码:1 成功;0 失败
private Integer code;
// 提示信息
private String msg;
// 返回数据
private Object data;
// getter/setter/构造方法...
}

静态资源

static目录作用:

  • 存放静态资源 (无需 Controller 处理,直接由 Spring MVC 静态资源处理器返回)
  • 常见文件类型:
    • HTML、CSS、JavaScript
    • 图片(JPG、PNG、GIF…)
    • 字体文件(TTF、WOFF…)
    • 其他前端静态资源

默认静态资源目录(优先级)

Spring Boot 内置了静态资源映射,默认会从以下位置查找(优先级从高到低):

  1. classpath:/META-INF/resources/
  2. classpath:/resources/
  3. classpath:/static/最常用
  4. classpath:/public/
  5. ${user.dir}/public/(运行目录下的 public

优先级规则 :同名文件会返回优先级高的目录中的版本。

访问规则

URL访问路径 = 应用根路径 + 文件名

例如:

1
src/main/resources/static/logo.png

访问:

1
http://localhost:8080/logo.png

与Controller的优先级

如果URL同时匹配Controller路径和静态资源路径:

  • Controller优先

例如:、

  • /foo 有一个 Controller 方法
  • /static/foo 下也有一个文件
  • 访问 /foo → 返回 Controller 的结果

默认首页

如果静态资源目录下有index.html,访问 /会直接返回它。

获取resources文件夹下的资源

ClassLoader方式(简单)

假设我们要读取 src/main/resources/readme.txt

1
This is a great way to get

Controller中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
public class ContentController {
@RequestMapping("/content")
public String getContent(@RequestBody User user) {
String file = getClass().getClassLoader().getResource("readme.txt").getPath();
String content = "";
try {
InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "UTF-8");
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
content += line;
}
} catch (Exception e) {
e.printStackTrace();
}
return content;
}
}

成功读取

1755958277427

Spring的 ClassPathResource

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
public class ContentController {
@RequestMapping("/content")
public String getContent(@RequestBody User user) {
ClassPathResource resource = new ClassPathResource("readme.txt");
String content = "";
try {
InputStreamReader isr = new InputStreamReader(resource.getInputStream(), "UTF-8");
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
content += line;
}
} catch (Exception e) {
e.printStackTrace();
}
return content;
}
}

分层解耦

三层架构

1755958735921

  • controller:控制层,接收前端发送的请求,对请求进行处理,并返回响应数据
  • service:业务逻辑层,处理具体的业务逻辑
  • dao:数据访问层 / 持久层, 负责数据访问操作,包括数据的增、删、改、查(CRUD)

IOC & DI

对象的创建与依赖管理交给外部容器,而不是由代码自己 new 出来。

概念 全称 定义 作用
IOC Inversion of Control(控制反转) 对象的创建控制权由程序自身转移到外部容器 降低代码耦合度,让对象的依赖关系由容器管理
DI Dependency Injection(依赖注入) 容器在运行时将应用所需的依赖资源注入到对象 让对象无需自己创建依赖,专注于业务逻辑
Bean - IOC 容器中创建和管理的对象 统一生命周期管理,可被注入到其他对象中

没有IOC/DI时:

  • 每个 Controller 可能会自己 new 一个 Service 实例
    • 结果:
    • 对象创建分散、重复
    • 耦合度高(Controller 依赖具体实现类)
    • Service 的状态和配置无法统一管理

有IOC/DI时

  • Service对象 由 IOC 容器 创建并管理(通常是单例 Bean)。
  • 多个 Controller 只需声明依赖(@Autowired),容器会自动将依赖注入。
  • 容器会在运行时将同一个 Service 实例注入到不同的 Controller 中。

IOC/DI入门

@Component

作用

  • 声明一个类是 Spring 管理的 Bean
  • 相当于告诉 IOC 容器:“这个类需要被扫描、实例化,并纳入容器管理”
  • 默认 Bean 名是类名首字母小写(可通过 @Component("beanName") 自定义)

衍生注解

注解 说明 位置
@Component 声明 bean 的基础注解 不属于以下三类时,用此注解
@Controller @Component 的衍生注解 标注在控制器类上
@Service @Component 的衍生注解 标注在业务类上
@Repository @Component 的衍生注解 标注在数据访问类上(由于与 MyBatis 整合,用的少)

针对经典的三层架构,Spring已经预设了经典的类型注解,平时我们优先使用 @Controller@Service@Repository

不属于这几类我们再使用:@Component

@Autowired注解

作用

  • 自动注入依赖对象 (DI 的实现方式之一)
  • 告诉 Spring:在容器中找到匹配的 Bean,并注入到当前字段、构造器或 Setter 方法中

注入规则

  1. 默认按类型(byType)匹配

    • 容器中必须有且只有一个该类型的 Bean
  2. 若有多个同类型 Bean

    • 再按名称(byName)匹配(字段名或参数名)
  3. 可配合 @Qualifier("beanName")

    • 精确指定要注入的 Bean
    • 默认按类型注解,有多个同类型注解 @Qualifier才生效
    1
    2
    @Autowired
    @Qualifier("dogA") // 精确指定注入 dogA

其他相关注解:

  • @Primary:指定优先对象。被 @Primary注解修饰的对象将优先注解。

    1
    2
    @Primary
    @Component
  • @Resource:通过名称Bean进行依赖注入(来源JDK 包)

    1
    2
    @Resource(name = "dogA")
    //无需Autowired注解

Web开发-后端基础篇
http://blog.ulna520.com/2025/08/22/Web开发-后端基础篇_20250822_101151/
Veröffentlicht am
August 22, 2025
Urheberrechtshinweis