系列文章目录
>Spring 系列 · 第 1 篇
> 面向 Java 初学者和有一定 Java Web 基础的开发者。
> 技术环境:Java 17+ · Spring Boot 4.0.8 · Spring Framework 7.x · Spring MVC · Maven · Jakarta Servlet 6.1
> 示例项目:book-manager(图书管理 REST API)
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 系列文章目录
- 前言
- [一、Spring 简介:它到底解决了什么问题?](#一、Spring 简介:它到底解决了什么问题?)
- [1.1 从【自己 new 对象】到【交给 Spring 管】](#1.1 从【自己 new 对象】到【交给 Spring 管】)
- [1.2 Spring Framework、Spring MVC、Spring Boot 的关系](#1.2 Spring Framework、Spring MVC、Spring Boot 的关系)
- [1.3 Spring 主要模块概览](#1.3 Spring 主要模块概览)
- [二、Spring 核心概念](#二、Spring 核心概念)
- [2.1 IoC 与 DI](#2.1 IoC 与 DI)
- [2.2 Bean 与 ApplicationContext](#2.2 Bean 与 ApplicationContext)
- [2.3 常用注解速查](#2.3 常用注解速查)
- [2.4 Bean 生命周期与作用域(简要)](#2.4 Bean 生命周期与作用域(简要))
- [三、Spring 基础示例:最小 IoC 程序](#三、Spring 基础示例:最小 IoC 程序)
- [3.1 目录结构](#3.1 目录结构)
- [3.2 Service 接口与实现](#3.2 Service 接口与实现)
- [四、Spring MVC 概述](#四、Spring MVC 概述)
- [4.1 什么是 MVC?](#4.1 什么是 MVC?)
- [4.2 Spring MVC 核心组件](#4.2 Spring MVC 核心组件)
- [4.3 一次 HTTP 请求的完整生命周期](#4.3 一次 HTTP 请求的完整生命周期)
- [五、Spring MVC 实战:book-manager 图书管理](#五、Spring MVC 实战:book-manager 图书管理)
- [5.1 目录结构](#5.1 目录结构)
- [5.3 pom.xml(Spring Boot 4.0.8 完整配置)](#5.3 pom.xml(Spring Boot 4.0.8 完整配置))
- [5.4 application.yml(Spring Boot 4.0.8 配置)](#5.4 application.yml(Spring Boot 4.0.8 配置))
- [5.5 启动类](#5.5 启动类)
- [5.6 实体类 Book](#5.6 实体类 Book)
- [5.7 Repository 层(内存模拟,便于学习)](#5.7 Repository 层(内存模拟,便于学习))
- [5.8 Service 层](#5.8 Service 层)
- [5.9 Controller 层(RESTful CRUD)](#5.9 Controller 层(RESTful CRUD))
- [5.10 统一返回结果](#5.10 统一返回结果)
- [5.11 全局异常处理](#5.11 全局异常处理)
- 六、参数接收与数据返回
- [6.1 三种参数来源](#6.1 三种参数来源)
- [6.2 常见的状态码](#6.2 常见的状态码)
- [6.3 用 curl 测试 API](#6.3 用 curl 测试 API)
- 七、常见问题与最佳实践
- [7.1 注解区别](#7.1 注解区别)
- [7.2 分层原则](#7.2 分层原则)
- [7.3 常见错误排查](#7.3 常见错误排查)
- [7.4 初学者易犯错误](#7.4 初学者易犯错误)
- 八、总结与学习路线
- [8.1 本文重点回顾](#8.1 本文重点回顾)
前言
如果你已经会写 Java,但一碰到 Spring 就分不清「容器、Bean、注解、MVC」------这篇文章就是为你准备的。
本文采用费曼学习法:每个概念先用生活类比讲清「它解决什么问题」,再用代码验证「它到底怎么工作」。读完后你将能够:
-
说清楚 Spring Framework、Spring MVC、Spring Boot 三者的关系;
-
理解 IoC / DI 并用构造器注入写出可测试的代码;
-
独立搭建一个基于Spring Boot 4.0.8 的 RESTful 图书管理 API;
-
掌握 Controller → Service → Repository 的分层开发流程。
>版本说明 :你在 IntelliJ IDEA 新建项目时选择的 Spring Boot 4.0.8 基于 Spring Framework 7.x和 Jakarta EE 11(不再是旧的 `javax.*` 包)。下文所有代码均按此版本编写。
。
提示:以下是本篇文章正文内容,下面案例可供参考
一、Spring 简介:它到底解决了什么问题?
1.1 从【自己 new 对象】到【交给 Spring 管】
想象自己开一家餐厅:
-
**传统写法:**每来一位客人,厨师自己跑菜市场买菜、自己洗菜切菜------每个类都`new` 自己的依赖,耦合严重。
-
Spring写法: 餐厅有一个中央调度室(IoC 容器) ,提前把【采购员(Repository)】【主厨(Service)配好岗,谁需要谁,调度室直接注入------这就是控制反转(IOC) 和依赖注入。(后面会详细讲解IoC和DI)。
Spring 要解决的正是 Java 企业开发中的老问题:

1.2 Spring Framework、Spring MVC、Spring Boot 的关系
┌─────────────────────────────────────────────────────────────┐
│ Spring Boot 4.0.8 │
│ (脚手架:自动配置 + 内嵌 Tomcat + 约定优于配置) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Spring Framework 7.x │ │
│ │ ┌─────────────┐ ┌──────────┐ ┌─────────────────┐ │ │
│ │ │ Core/IoC │ │ AOP │ │ Data Access │ │ │
│ │ └─────────────┘ └──────────┘ └─────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ Spring MVC(Web 框架) │ │ │
│ │ │ DispatcherServlet → Controller → View/JSON │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
-
Spring Framework :核心框架,提供 IoC、AOP、事务、Spring MVC 等能力。
-
Spring MVC :Spring 的 Web 模块,负责 HTTP 请求分发与响应。
-
Spring Boot :基于 Spring 的快速启动工具,帮我们配好 Tomcat、Jackson、日志等,让我们专注业务。
> 类比:Framework 是发动机,MVC 是变速箱,Boot 是整辆开箱即用的汽车。
1.3 Spring 主要模块概览

二、Spring 核心概念
2.1 IOC与DI
-
IoC(Inversion of Control,控制反转) :对象的创建权从业务代码转移到 Spring 容器。
-
DI(Dependency Injection,依赖注入):容器把依赖对象「注入」到需要它的类中。
java
//传统写法: 自己 new 紧耦合
public class BookService {
private BookeRepository repo = new BookeRepository();
}
//Spring 写法:构造器注入,松耦合、易测试
@Service
public class BookService {
private final BookRepository repository;
public BookService(BookRepository repository) {
this.repository = repository;
}
}
为什么推荐构造器注入?
1.依赖在对象创建时就绪,`final`字段保证不可变;
2.便与单元测试(直接`new BookService(mockRepo)`);
3.避免`@Autowired` 字段注入带来的空指针和隐藏依赖。
2.2 Bean 与ApplicationContext
- Bean:由Spring容器管理的对象。
-ApplicationContext:Spring的IoC容器,负责Bean的创建、装配、生命周期管理。
2.3 常用注解速查

2.4 Bean 生命周期与作用域(简要)
生命周期(简化版)
实例化 → 属性注入 → 初始化(@PostConstruct)→ 使用中 → 销毁(@PreDestroy)
常见作用域

三、Spring 基础示例:最小 IoC 程序
在深入 MVC之前,先用一个最小例子感受【容器帮我们装配对象】。
3.1 目录结构

3.2 Service 接口与实现
java
package com.review.springreview.service;
public interface GreetingService {
String greet(String name);
}
package com.review.springreview.service.impl;
import com.review.springreview.service.GreetingService;
import org.springframework.stereotype.Service;
@Service
public class GreetingServiceImpl implements GreetingService {
@Override
public String greet(String name) {
return "hello"+name +"!"+"欢迎学习spring ";
}
}
package com.review.springreview.component;
import com.review.springreview.service.GreetingService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
// CommandLineRunnerDemo.java --- 启动后自动执行,验证 DI 是否成功
@Component
public class CommandLineRunnerDemo implements CommandLineRunner {
private final GreetingService greetingService;
public CommandLineRunnerDemo(GreetingService greetingService) {
this.greetingService = greetingService;
}
@Override
public void run(String... args) throws Exception {
System.out.println(greetingService.greet("Spring初学者 xing"));
}
}
运行启动类即可观察到结果。
java
package com.review.springreview;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringReviewApplication {
public static void main(String[] args) {
SpringApplication.run(SpringReviewApplication.class, args);
}
}
代码解读:`CommandLineRunnerDemo` 没有 `new GreetingServiceImpl()`,Spring 发现构造器需要 `GreetingService`,自动找到 `@Service` 标注的实现类并注入。
四、Spring MVC 概述
4.1 什么是MVC?

4.2 Spring MVC 核心组件

4.3 一次HTTP 请求的完整生命周期

小结:
把 DispatcherServlet 想象成快递分拣中心------它不看包裹里是什么,只负责按地址(URL)把包裹送到正确的 Controller 手里。
五、Spring MVC 实战:book-manager 图书管理
5.1目录结构
book-manager/
├── pom.xml
├── src/main/java/com/example/bookmanager/
│ ├── BookManagerApplication.java
│ ├── model/
│ │ └── Book.java
│ ├── repository/
│ │ └── BookRepository.java
│ ├── service/
│ │ ├── BookService.java
│ │ └── BookServiceImpl.java
│ ├── controller/
│ │ └── BookController.java
│ ├── dto/
│ │ └── ApiResponse.java
│ └── exception/
│ ├── BookNotFoundException.java
│ └── GlobalExceptionHandler.java
└── src/main/resources/
└── application.yml
5.3 pom.xml(Spring Boot 4.0.8 完整配置)
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.8</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>book-manager</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>book-manager</name>
<description>book-manager</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5.4 application.yml(Spring Boot 4.0.8 配置)
java
server:
port: 8080
servlet:
context-path: /
spring:
application:
name: book-manager
JSON 序列化配置
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Shanghai
default-property-inclusion: non_null
DevTools 热重载(开发环境)
devtools:
restart:
enabled: true
livereload:
enabled: true
servlet:
encoding:
force: true
日志配置
logging:
level:
root: info
com.example.bookmanager: debug
org.springframework.web: info
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
自定义业务配置(可选)
app:
info:
name: 图书管理系统
version: 1.0.0
description: Spring 系列第 1 篇实战示例
>Spring Boot 4 提示:4.x 采用模块化 Starter 设计,第三方能力通常需要显式引入对应 Starter。例如参数校验需引入 `spring-boot-starter-validation`,不能仅靠传递依赖「碰运气」。
5.5 启动类
java
package org.example.bookmanager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BookManagerApplication {
public static void main(String[] args) {
SpringApplication.run(BookManagerApplication.class, args);
}
}
`@SpringBootApplication` = `@Configuration` + `@EnableAutoConfiguration` + `@ComponentScan`,一行注解完成组件扫描和自动配置。
5.6实体类Book
java
package org.example.bookmanager.model;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Data;
@Data
public class Book {
private Long id;
@NotBlank(message = "书名不能为空")
private String title;
@NotBlank(message = "作者不能为空")
private String author;
@NotNull(message = "价格不能为空")
@PositiveOrZero(message = "价格不能为负数")
private Double price;
}
> 注意:Spring Boot 4 使用 **Jakarta EE 11**,校验注解包名为 `jakarta.validation.*`,不是旧的 `javax.validation.*`。
5.7 Repository 层(内存模拟,便于学习)
java
package org.example.bookmanager.repository;
import org.example.bookmanager.model.Book;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@Repository
public class BookRepository {
private final Map<Long, Book> store =new ConcurrentHashMap<>();
private final AtomicLong idGenerator = new AtomicLong(1);
public BookRepository(){
// 预置测试数据
save(new Book(null, "Spring 实战", "Craig Walls", 89.0));
save(new Book(null, "深入理解 Java 虚拟机", "周志明", 99.0));
}
public List<Book> findAll(){
return new ArrayList<>(store.values());
}
public Optional<Book> finalById(Long id){
return Optional.ofNullable(store.get(id));
}
public Book save(Book book){
if (book.getId() == null){
book.setId(idGenerator.getAndIncrement());
}
store.put(book.getId(),book);
return book;
}
public boolean deleteById(Long id){
return store.remove(id) != null;
}
}
本示例使用内存 Map 模拟数据库,目的是让你专注理解分层与 MVC 流程。生产环境可替换为 Spring Data JPA / MyBatis。
5.8 Service 层
java
package org.example.bookmanager.service;
import org.example.bookmanager.model.Book;
import java.util.List;
public interface BookService {
List<Book> listAll();
Book getById(Long id);
Book create(Book book);
Book update(Long id,Book book);
void delete(Long id);
}
package org.example.bookmanager.service.impl;
import org.example.bookmanager.exception.BookNotFoundException;
import org.example.bookmanager.model.Book;
import org.example.bookmanager.repository.BookRepository;
import org.example.bookmanager.service.BookService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookServiceImpl implements BookService {
private final BookRepository bookRepository;
public BookServiceImpl(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
@Override
public List<Book> listAll() {
return bookRepository.findAll();
}
@Override
public Book getById(Long id) {
return bookRepository.finalById(id)
.orElseThrow(()->new BookNotFoundException(id));
}
@Override
public Book create(Book book) {
book.setId(null);
return bookRepository.save(book);
}
@Override
public Book update(Long id, Book book) {
getById(id);
book.setId(id);
return bookRepository.save(book);
}
@Override
public void delete(Long id) {
if (!bookRepository.deleteById(id)){
throw new BookNotFoundException(id);
}
}
}
5.9 Controller 层(RESTful CRUD)
java
package org.example.bookmanager.controller;
import jakarta.validation.Valid;
import org.example.bookmanager.dto.ApiResponse;
import org.example.bookmanager.model.Book;
import org.example.bookmanager.service.BookService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@RequestMapping("/book")
@RestController
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
/**
* 查询列表
*/
@GetMapping("/list")
public ApiResponse<List<Book>>list(@RequestParam(required = false) String keyword){
List<Book> books = bookService.listAll();
if (keyword !=null && !keyword.isBlank()){
books = books.stream()
.filter(b->b.getTitle().contains(keyword)|| b.getAuthor().contains(keyword)).toList();
}
return ApiResponse.success(books);
}
/**
* 根据id查询
*/
@GetMapping("/getById")
public ApiResponse<Book> getById(@RequestParam Long id){
return ApiResponse.success(bookService.getById(id));
}
/**
* 新增
*/
@PostMapping("/create")
@ResponseStatus(HttpStatus.CREATED)
public ApiResponse<Book> create(@Valid@RequestBody Book book){
return ApiResponse.success(bookService.create(book));
}
/**
* 修改
*/
@PutMapping("/update")
public ApiResponse<Book> update(@RequestParam Long id,
@Valid @RequestBody Book book){
return ApiResponse.success(bookService.update(id,book));
}
/**
* 删除
*/
@GetMapping("/delete")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@RequestParam Long id){
bookService.delete(id);
}
}
注解说明:

5.10 统一返回结果
java
package org.example.bookmanager.dto;
public record ApiResponse<T>(int code,String message,T data) {
public static <T> ApiResponse<T> success(T data){
return new ApiResponse<>(200,"success",data);
}
public static <T> ApiResponse<T> fail(int code,String message){
return new ApiResponse<>(code,message,null);
}
}
5.10 全局异常处理
java
package org.example.bookmanager.exception;
public class BookNotFoundException extends RuntimeException{
public BookNotFoundException(Long id) {
super("图书不存在id="+ id);
}
}
java
package org.example.bookmanager.exception;
import org.springframework.validation.FieldError;
import org.example.bookmanager.dto.ApiResponse;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.stream.Collectors;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BookNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ApiResponse<Void> handleNotFound(BookNotFoundException ex) {
return ApiResponse.fail(404, ex.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ApiResponse<Void> handleValidation(MethodArgumentNotValidException ex) {
String msg = ex.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining("; "));
return ApiResponse.fail(400, msg);
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ApiResponse<Void> handleGeneral(Exception ex) {
return ApiResponse.fail(500, "服务器内部错误:" + ex.getMessage());
}
}
六、参数接收与数据返回
6.1 三种参数来源

6.2常见的状态码

6.3 用 curl 测试 API
查询全部
curl http://localhost:8080/book/list
按关键词搜索
curl "http://localhost:8080/book/list?keyword=Spring"
查询详情
curl http://127.0.0.1:8080/book/getById?id=1
新增
curl -X POST http://localhost:8080/api/book/create
-H "Content-Type: application/json" \
-d "{
"id":3,
"title":"Java并发编程",
"author":"Brian Goetz",
"price":79.0
}"
修改
curl -X PUT http://127.0.0.1:8080/book/update?id=1
-H "Content-Type: application/json" \
-d {
"id":1,
"title":"Spring 实战(第6版)",
"author":"Craig Walls",
"price":99.0
}
删除
curl -X GET http://localhost:8080/book/delete?id=2
七、常见问题与最佳实践
7.1 注解区别

7.2 分层原则
Controller → 只做参数接收、调用 Service、返回响应(薄薄一层)
Service → 业务逻辑、事务控制
Repository → 数据存取,不含业务判断
7.3 常见错误排查

7.4初学者易犯错误
-
在 Controller 里写业务逻辑 → 应下沉到 Service;
-
可以使用Autowired注入,也可以构造器注入(看公司怎么使用);
-
不区分 Spring / Spring Boot → Boot 是快速启动工具,底层仍是 Spring;
-
复制旧教程的 `javax.servlet` → Boot 4 请用 `jakarta.servlet`。
八、总结与学习路线
8.1本文重点回顾
-
Spring 核心 :IoC 容器管理 Bean,DI 解耦对象依赖;
-
三者关系 :Framework 是核心,MVC 处理 Web,Boot 简化配置;
-
MVC 流程 :DispatcherServlet → Controller → Service → Repository → JSON;
-
实战项目 :book-manager 实现了完整 RESTful CRUD;
-
最佳实践:构造器注入、分层架构、统一返回、全局异常处理。
完整运行示例:
1.环境准备 :安装 JDK 17+、Maven 3.9+、IntelliJ IDEA。
-
创建项目 :IDEA → New Project → Spring Initializr → 选择 **Spring Boot 4.0.8** → 勾选 Web、Validation。
-
复制代码 :按上文目录结构创建包和类,粘贴 `pom.xml` 与 `application.yml`。
-
启动项目 :运行 `BookManagerApplication.main()`,看到 `Started BookManagerApplication` 即成功。
-
验证接口 :浏览器访问 `http://localhost:8080/book/list\`,或用 curl / Postman 测试 CRUD。
-
观察日志:`application.yml` 中 `com.example.bookmanager: debug` 可看到详细请求日志。
欢迎收藏本系列,动手跑通 book-manager 后再往下学,印象会更深。