在之前的博客中,我们认识到了网络编程,也成功写出了一款基于Java多线程、socket应用、自定义消息协议技术的通信系统。在整个开发过程中,我们需要自定义协议、关心数据的收发与解包、处理多线程并行等等问题。我们不禁会想:如果这是一个更大型的Web项目,消息的收发要求更高、线程更加复杂的情况下,我们岂不是会寸步难行?有没有什么好东西能让我只关心实现业务逻辑本身,把我们从繁琐的基础设施中解放出来?你别说,还真有,这就是我们今天的主角:Spring Boot框架。今天我们先初步认识一下Spring Boot到底是什么,以及一些基础的应用方式。
一、从"自己搭基础设施"到"约定优于配置"
框架的核心作用就是把重复的基础设施搭建这一步抽走,Spring Boot依靠的最核心思想就是:约定优于配置。
简单来说,框架就是把项目里面大部分长得一样的设定都提前配置好了。比如做一个Web服务,默认就是8080端口,默认就是用内嵌Tomcat,默认接收HTTP请求。只要程序员不主动指定,那么框架就会按照这些默认的值配置环境;当然,如果程序员有特殊的需求,同样也可以手动写配置。这就好比我们经常去一家饭店吃饭,进门老板就可以直接按照你的习惯上菜了,只有你某天想换口味了才需要跟老板特意声明。
二、用IDEA创建Spring Boot项目
IDEA在新建项目时会有一个Spring Boot的选项,我们点击选择,名称可以自定,类型我们可以选择maven,方便后续添加依赖。设置完毕之后,我们就可以点击下一步。

来到下一步后,IDEA会让你勾选开发需要用到的依赖,我们没有必要全部都勾选上,下面我们就来讲一下一个带前后端和数据库的基础Web服务都需要哪些,以及他们的作用。
① 地基
- Web→Spring Web :这是使用Spring Boot进行Web开发的必选项,加入了这个依赖框架才会把内嵌Tomcat和接收HTTP请求的能力带进来,也是我们使用@RestController这类注解前提。
- Developer Tools → Lombok :这是一个非常实用的依赖,可以使用特定的注解省去我们手写 getter 和 setter 方法的麻烦,在写实体类的时候非常好用。
② 数据库相关
- SQL → Spring Data JPA 或 SQL → MyBatis Framework :这是持久层框架,二选一即可。JPA可以帮助我们自动生成SQL语句,简单的CRUD可以省去不少麻烦;而 MyBatis 则是常常与Spring Boot配合使用的数据库框架(后面会专门写一篇博客介绍),主要是讲SQL握在自己手里,灵活可控。
- SQL → MySQL Driver 或 H2 Database :连接数据库是需要依靠驱动的。大家可以根据自己使用的数据库勾选对应的驱动,如Oracle Driver 、PostgreSQL Driver等等,选用之后便可以直接在IDEA中连接对应的数据库,实现数据库的可视化监控和操作;而 H2 Database 是一个内存数据库,启动即用,比较适合初期不想装特定的数据库的小白。
③ 非必要
- Developer Tools → Spring Boot DevTools :热重载。在改动了后端的代码后可以直接自动重启,无需停止运行后再重新运行。
- Security → Spring Security :认证和授权。勾选之后所有的接口都默认被保护,访问 /hello 都会自动跳转到登录页面。
- Testing → Spring Boot Test :带Junit、Mockito等测试库,在后端代码完成后想写单元测试时可以加上该依赖。
- I/O → Validation :参数校验。用到@Not Null、@Size这类注解校验前端传来的数据时需要用到该依赖。

勾选完毕后,点击创建,我们就成功创建好了一个原始的Spring Boot项目,主要的项目目录如下:

在开始写代码之前,我们需要先认识一下这些文件都有什么作用:
① 启动类:WenTestApplication
在 src/main/java 目录下,IDEA已经自动创建好了一个带main方法的类。这是整个项目的总开关,只要运行一下这个文件里面的main方法,Spring Boot 就会拉起内嵌的 Tomcat 服务器,运行整个项目的后端代码。
② 核心配置文件:application.properties
在 src/main/resources 目录下,这个文件是整个项目的控制中心,用于配置项目的环境变量。前面提到Spring Boot遵循"约定优于配置",所以这个文件默认是空的,所有的配置都默认使用原始值。但后续的开发如果你想改端口(比如不想用8080)、连接数据库(配置数据库账号和密码)、改项目的根路径等,全部都可以在这个文件中指定。
③ 静态资源与模板:static 和 templates
- static:用来存放纯静态的资源,比如图片、CSS、JS文件。写好的前端可以直接放在这个目录下,浏览器可以直接访问到。
- templates :用来存放模板页面(比如HTML文件)
④ 依赖管家:pom.xml
这是管理项目依赖的总文件,我们在创建项目时勾选的依赖全部都变成了这里的<dependency>标签。在开发过程中若想加入其他的第三方依赖,可以到对应的官网上寻找"使用maven添加依赖"的说明,创建一个<dependency>标签加进去,刷新一下Maven即可。
三、基础概念:Spring Boot的三层架构
在真正开始写接口之前,我们需要先清楚一个基础的常识:在Spring Boot中,代码并不是全部塞在一个文件里的而是按照职责被分成了几个"层"。为什么要分层呢?因为如果把接收请求、业务计算、连接数据库的代码全都写在同一个目录下,那当项目稍微复杂起来时,就会非常混乱难以维护和管理。分层的目的就是各司其职,提高代码的可读性和可维护性。
最经典的划分,就是我们常说的"三层架构":
① Controller 类(控制层):接待员
Controller 类的任务只有一个:和前端打交道。它负责接收浏览器(或APP)发来的HTTP请求,解析前端需要什么数据或者调用什么函数,然后把任务转交给后面的 Service。等Service把结果计算好之后,Controller 再负责把结果打包,响应给前端。
② Service 类(业务逻辑层):主厨
Service 类负责的是真正的业务逻辑计算。比如"用户登录"这个功能,Service 需要负责的任务就是判断密码对不对、判断账号是否可用、生成登录凭证等等业务逻辑。它在 Controller 和数据库之间起到承上启下的作用,地位非常重要,是项目的核心大脑。
③ Entity 类(实体类) / Mapper 类(数据访问层):仓库管理员
这一层负责的是和数据库打交道,而 Entity 类则是这一层的核心。它就是数据库里一张表的Java映射。比如数据库中有一张user表,里面有id、username、password三个字段,那么对应的我们就要建一个user实体类,里面包含这三个属性。类的名字对应表名,类的属性对应表的列,类的每一个对象就是数据库表里的一行数据。
而 Mapper 类/接口,主要是用于封装一些数据库的操作方法,也就是SQL语句。通常我们会配合着Mybatis 框架一起使用,对应的实体建一个 Mapper 接口,继承 BaseMapper 类,便可以使用Mybatis 中封装好的一些数据库操作方法。
四、实战演示:搭一个简单的登录接口
① 实体类(Entity):接受前端传来的数据
我们在启动类所在的包下,新建一个包叫entity。在里面新建一个User类:
java
package com.inventory.webtest.entity;
import lombok.Data;
@Data
public class User {
private String username;
private String password;
}
在这里,这个实体类的作用是作为数据的载体:前端传来的JSON,会被转换成这个User对象。方便我们在代码中进行操作。注意:此处采用了@Data注解,已封装好getter 和setter 方法。
② 业务层(Service):真正做判断的地方
在包下新建一个service包,新建一个UserService类:
java
package com.inventory.webtest.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public boolean login(String username, String password) {
// 在这里我们先不查数据库,硬编码账号密码
return "admin".equals(username) && "123456".equals(password);
}
}
注意类上的@Service注解。这是在告诉Spring:"这个类属于业务层,请你把它的对象创建好,放进容器中管理起来"。这样,等到 Controller 要使用的时候直接拿这个类的对象就行,不用自己new了。
③ 控制层(Controller):接待前端请求
在包下新建一个controller包,新建一个UserController类:
java
package com.inventory.webtest.controller;
import com.inventory.webtest.entity.User;
import com.inventory.webtest.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
public String login(@RequestBody User user) {
String username = user.getUsername();
String password = user.getPassword();
// 收到请求后,转交给 Service 去判断
boolean isSuccess = userService.login(username, password);
if (isSuccess) {
return "登录成功!";
}else {
return "账号或密码错误!";
}
}
}
这个类涉及到几个非常关键的注解,下面一一讲解:
- @PostMapping("/login") :和 @GetMapping 注解类似,表示 /login 路径的请求由这个方法来处理。两者的区别在于,涉及密码等敏感信息的传递,通常会使用POST请求(数据藏在请求体里),而不是GET请求(数据暴露在URL上)。
- @RequestBody :这个注解是告诉Spring,把该路径请求中前端传来的 JSON 数据,自动解析并转换成后面跟着的类的对象。
- @Autowired :这是 Spring 最核心的魔法。以前我们需要对象得自己new UserService( ),现在只要标上这个注解,Spring 就会自动去它管理的容器里把 UserService 找出来并注入进来。这就叫依赖注入。
五、前端:看一眼真实的前后端交互链路
① 写一个极简的登录页面
在 static 下新建一个文件,叫login.html:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>登录测试</title>
<style>
/* 稍微加几行样式,让它看着不那么简陋 */
body { font-family: sans-serif; padding: 50px; }
input { margin-bottom: 10px; padding: 5px; display: block; }
button { padding: 5px 15px; }
</style>
</head>
<body>
<h2>用户登录</h2>
<input type="text" id="username" placeholder="请输入账号">
<input type="password" id="password" placeholder="请输入密码">
<button onclick="doLogin()">登录</button>
<p id="result" style="color: red; font-weight: bold;"></p>
<script>
function doLogin() {
// 1. 从输入框获取数据
const user = {
username: document.getElementById('username').value,
password: document.getElementById('password').value
};
// 2. 用浏览器自带的 fetch 向后端发送 POST 请求
fetch('/login', {
method: 'POST',
headers: {
// 重点:告诉后端,我发过去的是 JSON 格式的数据
'Content-Type': 'application/json'
},
// 把 JS 对象转成 JSON 字符串塞进请求体
body: JSON.stringify(user)
})
.then(response => response.text()) // 后端返回的是纯文本字符串
.then(data => {
// 3. 把后端返回的结果显示在页面上
document.getElementById('result').innerText = data;
})
.catch(error => {
document.getElementById('result').innerText = "请求出错了:" + error;
});
}
</script>
</body>
</html>
② 拆解:前后端到底是怎么交互的?
这个页面的代码不多,但它完整体现了现代前后端分离交互的标准流程。我们逐行拆解:
a. 静态页面哪里来的?
因为我们将 login.html 文件放在了 static 目录下,Spring Boot 就默认把它当成了可以直接对外公开的文件。当浏览器请求 /login.html 时,内嵌的 Tomcat 直接把这段HTML代码返回给浏览器去渲染。
b.数据是怎么发出去的?
首先,用户可以在前端的输入框中输入账号和密码,输入完毕点击登录按钮后,触发button的onclick条件,执行JS代码中的doLogin函数。
在JS的doLogin方法中,通过 document.getElementById('username').value 方法可以在整个HTML页面里寻找 id 为 username 的那个标签(也就是输入框)中的值,也就是用户输入的内容。
接着,用 const user = { username: ..., password: ... }; 创建一个对象,将我们刚刚拿到的输入框中的值组装成一个 user 对象。
然后,用JSON.stringify(user)将对象变为文本。前端要发数据给后端,网络传输只能传文本,所以要把对象变成一串标准的JSON字符串。这串字符串通过网络传到后端,对应到@RequestBody注解便将这个JSON数据还原成 User 对象。
接着,用 fetch() 方法发送网络请求。fetch 是现代浏览器提供的原生网络请求工具,接受两个参数:目的地(路径),配置项(method、headers、body)。
最后,处理后端返回的结果。.then 就是用来接受结果的,在这里的第一个.then 拿到的是整个HTTP响应,但我们只需要里面的文本数据,所以用 .text() 提取出来;第二个 .then 拿到的是 data,对应下来就是后端返回的"登录成功!"或"账号密码错误!"。完成接收后,简单地用document.getElementById('result').innerText = data; 把这句话写回到页面即可。
六、效果演示
在本地先启动Spring Boot后端服务,接着访问http://localhost:8080/login.html,输入账号密码点击登录即可完成测试。通过前面的后端搭建和前后端交互,一个简单的Spring Boot项目就搭建完毕了!

七、总结:初识Spring Boot的"三板斧"
通过上面的介绍,你应该明白了Spring Boot在搭建一个Web项目时的方便了,它让我们真正做到了把我们从繁重的搭建基础设施工作中解放出来,只关心核心业务逻辑的实现。那么总结一下就是"三板斧":
第一板斧:约定优于配置(替你搭台子)
我们不需要再写ServerSocket,不需要手动解析 HTTP,甚至不需要配置Tomcat,只需要勾选Spring Web 依赖,Spring Boot就会按照默认的约定搭好基础设施,让我们一上来就能直接写业务逻辑。
第二板斧:分层解耦(让你各司其职)
通过Controller、Service、Entity的明确分工,我们实现了代码的解耦,避免了不同功能的代码产生混乱,实现了代码的高可读性和高可维护性。
第三板斧:依赖注入(帮你管对象)
这是 Spring 的核心灵魂。我们不用再去剥洋葱似的 new 一堆新对象,Spring 会自动把对象创建好并把线连好。它省掉的不仅是一个 new ,更是庞大项目中错综复杂的依赖管理灾难。
八、附录:常用注解
| 注解 | 所在位置 | 一句话作用 |
|---|---|---|
| @SpringBootApplication | 启动类 | 项目的总开关,开启自动配置和组件扫描。 |
| @RestController | Controller 类上 | 标记这是一个接收 Web 请求的类,方法返回的字符串直接响应给前端。 |
@GetMapping |
Controller 方法上 | 绑定 HTTP 的 GET 请求(比如查询数据),括号里写访问路径。 |
@PostMapping |
Controller 方法上 | 绑定 HTTP 的 POST 请求(比如提交、登录),括号里写访问路径。 |
@RequestBody |
Controller 方法参数上 | 把前端发来的 JSON 字符串,自动解析并转换成 Java 对象。 |
@Service |
Service 类上 | 告诉 Spring 这是业务逻辑类,启动时把它创建好并放进容器里。 |
@Autowired |
Controller 属性上 | 依赖注入,告诉 Spring 把容器里的对象自动拿过来给这个变量用。 |
@Data |
实体类(Entity)上 | Lombok 提供的注解,自动生成 getter、setter、toString 等方法,省去手写模板代码的麻烦。 |