从零到跑通:Spring Boot 入门实战全记录

吴庆丰_码农
2025-06-27 09:34
阅读 451

去年公司要做一个内部管理平台,时间紧任务重。作为后端组的主力开发之一,我被指派用 Spring Boot 搭建后端服务。当时我们团队里对 Spring Boot 了解不深,虽然都知道它“简化了 Spring 配置”、“内嵌 Tomcat”,但真要上手时还是遇到不少困惑。

作为一个有五年经验的后端工程师,我经历过从传统 XML 配置的 Spring MVC 项目一步步过渡到 Spring Boot 的过程。这篇分享是我亲身经历的一次快速上手实践,希望能帮你在 60 分钟内真正理解 Spring Boot 是怎么一回事,并搭建起自己的第一个工程。


开始前的准备:我们需要做什么?

开始前的准备:我们需要做什么?

目标是搭建一个简单的 API 服务:支持用户注册、登录和查看个人信息的功能。数据存储使用 MySQL。我们的重点在于:

  • 快速初始化 Spring Boot 工程
  • 基本结构与注解使用
  • 数据库操作(JPA)
  • 接口开发与测试
  • 踩坑点及解决方案

工具链:

  • Java 17
  • IntelliJ IDEA 2023.x
  • Maven 3.8.x
  • MySQL 8.x
  • Postman 测试接口

环境初始化:第一次用 Spring Initializr 创建项目

环境初始化:第一次用 Spring Initializr 创建项目

微服务架构示意图-2

一开始我去了 start.spring.io 页面,这是最推荐的官方入口。选择以下选项:

  • Project: Maven
  • Language: Java
  • Spring Boot Version: 3.x(建议当前稳定版本)
  • Group & Artifact: 自定义(比如 com.example.demo)
  • Dependencies:
    • Spring Web
    • Spring Data JPA
    • MySQL Driver
    • Lombok(可选)

点击 "Generate" 后下载 zip 包,导入到 IDE 中就可以开始写了。

这里有个小插曲:刚开始同事说他之前用 Spring Boot 2.x 创建的项目没问题,但我这边用上了 3.x 结果连 MySQL 依赖都要自己手动配驱动类名(因为 Spring Boot 3 升级了 Jakarta EE 9,默认去掉了 javax 包名)——这点我们在后面配置时会提到。


项目结构初探:简单但很规范

引入后的结构非常干净:

src/
└── main/
    ├── java/
    │   └── com.example.demo/
    │       ├── DemoApplication.java
    │       └── controller/
    │       └── model/
    │       └── repository/
    │       └── service/
    └── resources/
        ├── application.properties
        └── data.sql

这个结构遵循的是典型的 Spring Boot 多层架构风格:

  • Controller 层处理 HTTP 请求
  • Service 层业务逻辑
  • Repository 是数据访问层
  • Model 映射实体类
  • resources 用于放配置和初始化 SQL

接下来我们先搭数据库。


数据库设计:简约而不简单

我们只需要一张用户表来实现基本功能,字段包括:

CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `username` varchar(50) NOT NULL UNIQUE,
  `password` varchar(255) NOT NULL,
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP
);

注意几点:

  • 用户名唯一性通过数据库层面保证
  • 密码建议哈希加密,我们暂时先存明文做演示
  • created_at 使用默认值减少代码处理负担

接着,在 resources 目录下创建 data.sql 文件,用于插入初始数据:

INSERT INTO user (username, password) VALUES ('admin', '123456');

这样启动应用的时候会自动执行这个文件,可以方便调试。


配置数据库连接:Spring Boot 真的省事了吗?

打开 application.properties 文件,添加以下配置:

spring.datasource.url=jdbc:mysql://localhost:3306/your_db_name?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

微服务架构示意图-1

⚠️ 如果你也是 Spring Boot 3.x 的用户,一定要指定 driver-class-name!不然会提示找不到 JDBC 类。

然后加上 JPA 的自动建表设置(可选):

spring.jpa.hibernate.ddl-auto=update

这个时候我犯了一个错误:之前习惯写成 hibernate.hbm2ddl.auto=update,结果发现没生效。这提醒我们要以 Spring Boot 提供的属性优先为准。


编写实体类:Lombok 救命

在 model 包下新建 User 实体:

@Entity
@Table(name = "user")
@Data  // 来自 Lombok,自动提供 getter/setter/toString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String username;

    private String password;

    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;
}

用到了几个关键注解:

  • @Entity: 声明这是一个持久化实体
  • @Table: 对应数据库表名
  • @Id, @GeneratedValue: 主键策略
  • @Column: 字段映射,可以配置是否唯一、是否可空等
  • Lombok 的 @Data 和构造方法注解让代码简洁很多

数据访问层:JPA 让你少些一半代码

Repository 接口继承 JpaRepository 就行:

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}

JPA 支持方法命名规则自动生 SQL 查询,比如这里的 findByUsername 方法就不用手动写 SQL。

⚠️ 注意返回值用了 Optional,这样在调用时能有效规避 NPE 问题。我在早期开发时就因为忽视 null 检查出过 bug。


Service 层:别急着写,先想清楚职责

Service 层主要封装业务逻辑,比如用户注册流程中的验证、密码加密等。这里先做个简单示例:

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public Optional<User> getUserByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    public User registerUser(String username, String password) {
        if (userRepository.findByUsername(username).isPresent()) {
            throw new RuntimeException("用户名已存在");
        }
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);  // 这里应该加盐加密
        return userRepository.save(user);
    }
}

这段代码可能你会觉得太简单,但它其实包含了几个值得思考的设计点:

  • 是否该在 Service 中抛异常?答案是肯定的,业务异常要有统一处理方式
  • 密码直接保存明文不可取,生产环境应该用 BCryptPasswordEncoder 加密
  • 返回类型为 Entity 是否合适?后续考虑封装 DTO 减少暴露敏感字段

控制器层:RESTful API 设计原则

现在我们来编写控制器:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/{username}")
    public ResponseEntity<?> getUser(@PathVariable String username) {
        Optional<User> user = userService.getUserByUsername(username);
        return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
    }

    @PostMapping("/register")
    public ResponseEntity<User> registerUser(@RequestBody RegisterRequest request) {
        User savedUser = userService.registerUser(request.getUsername(), request.getPassword());
        return ResponseEntity.ok(savedUser);
    }
}

对应的请求体:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class RegisterRequest {
    private String username;
    private String password;
}

有几个注意事项:

  • 使用 RESTful 风格设计 URL,资源名词复数形式
  • GET 和 POST 动词准确体现语义
  • 统一响应格式?这个看团队规范,初期可以直接返回对象
  • 控制器中尽量不要出现复杂逻辑,转而交给 Service

启动项目:别忘了检查日志

运行 DemoApplication 启动类之后,控制台输出一堆信息。正常情况下你应该能看到类似如下内容:

Tomcat started on port(s): 8080 (http)
Started DemoApplication in 3.2 seconds (process running for 3.8)

这个时候我们可以用 Postman 测试一下接口。

示例请求:

注册新用户

POST /api/users/register
{
  "username": "testuser",
  "password": "123456"
}

获取用户信息

GET /api/users/testuser

踩过的坑和解决办法总结

这一路上踩了不少坑,都是实际开发中最常见的:

1. Spring Boot 启动慢

  • 问题现象:首次运行特别慢,等好久才启动成功
  • 解决方案:
    • 设置 debug=true 查看具体阻塞步骤
    • 升级内存配置 -Xms512m -Xmx1024m
    • 拆分大项目为多个模块

2. JPA 自动建表失败

  • 问题现象:表没有自动生成或更新
  • 解决方案:
    • 检查 DDL-AUTO 配置是否正确
    • 检查实体字段是否都有正确注解
    • 确保数据库有足够权限

3. MySQL 时区报错

  • 问题现象:连接数据库时报错,提示时区未设置
  • 解决方案:
    • 在 JDBC URL 中显式加上 serverTimezone=UTC
    • 或者改为 Asia/Shanghai(根据本地时区)

4. No session found in request 异常

  • 出现场景:在懒加载字段读取时报错
  • 解决方案:
    • 不要在 Controller 层直接返回含懒加载字段的实体
    • 用 DTO 手动封装需要的数据,避免引发 Hibernate LazyInitializationException

总结:这套方案带来了什么收益?

从项目上线到现在已经三个月了,这套基于 Spring Boot 的架构帮助我们实现了以下几个核心价值:

  1. 快速迭代能力提升

    • 新增接口平均只需 30 分钟完成开发+测试
    • 模块化清晰,代码易维护
  2. 良好的可扩展性

    • 日后可以轻松接入 Redis 缓存、定时任务、日志监控等功能
    • 完全具备向微服务迁移的能力
  3. 运维成本低

    • 内嵌 Tomcat 省去了部署中间件的麻烦
    • 只需打个 jar 包就能跑,便于 CI/CD

我的 Spring Boot 学习建议

如果你是刚入门的新手,或者正在考虑转型后端,下面是我的一些建议:

✅ 初学者建议:

  • 不要一开始追求完美架构,先把基础功能跑起来
  • 多动手改代码,别只看文档不实操
  • 遇到错误时多看日志,培养定位问题的能力

🚧 中进阶方向:

  • 熟悉 Spring Boot 的自动化配置原理
  • 掌握 Actuator 监控、Swagger 文档生成
  • 理解自动装配机制,尝试写一个 Starter

🔐 安全与性能:

  • 生产环境务必启用 HTTPS
  • 接口参数校验要用 Bean Validation 注解
  • 关键路径考虑限流、防止暴力破解

结语:Spring Boot 的魅力在于“恰到好处”

写到这里,突然想起最初用 XML 配置 Spring 的时候,每次新增一个 bean 就要改好几个 xml 文件,而现在只需一个 @Service 注解即可。

Spring Boot 最大的价值不是让你少写代码,而是帮你屏蔽了冗余配置,把注意力集中在真正的业务开发上。

如果你能在 60 分钟内跟着这篇文章从头到尾跑一遍,相信你会发现,所谓“框架”,不过是让我们专注解决问题的方式而已。

下次我们还可以聊聊如何在 Spring Boot 中优雅地写单元测试、使用 AOP、整合 Kafka 等话题。希望我们能一起成长!


作者:张工 | 5年Java后端工程师 | 当前专注于企业级系统架构
如果你喜欢这类实战导向的技术文章,欢迎点赞收藏,也可以留言告诉我你想学的内容~

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝