Spring 框架全家桶详解:从入门到实战 🌱
Spring 是 Java 领域最强大的企业级应用开发框架,它的出现彻底改变了 Java 企业级开发的格局。从最初的 Spring Framework 到现在的 Spring Boot、Spring Cloud,Spring 生态已经成为 Java 开发者必须掌握的核心技术。本文将带你全面理解 Spring 的核心概念、核心功能以及实战应用!💪
📚 目录导航
一、Spring 概述:为什么选择 Spring? 1.1 Spring 的诞生背景 2002 年,Rod Johnson 在其著作《Expert One-on-One J2EE Design and Development》中首次提出了 Spring 的概念。此时的 Java 企业级开发主要依赖 EJB(Enterprise JavaBeans)框架,但 EJB 存在以下问题:
EJB 的痛点:
📦 重量级 :每个 Bean 都需要实现特定的接口(如 SessionBean、EntityBean)
🔧 配置复杂 :大量的 XML 配置,部署描述符(DD)冗长
🐢 性能低下 :EJB 容器启动慢,Bean 实例化消耗资源多
🧪 难以测试 :业务逻辑与容器紧耦合,单元测试困难
Spring 的创新: Rod Johnson 认为,我们可以采用简单的 JavaBean(Plain Old Java Object,简称 POJO) 来代替 EJB,实现轻量级的企业级开发。Spring 通过依赖注入(DI)和面向切面编程(AOP)等核心功能,让 Java 开发变得简单、灵活、可测试。
1.2 什么是 Spring? 简单来说,Spring 是一个轻量级的 Java 开发框架 ,它提供了从基础配置到企业级应用的完整解决方案。
我们可以把 Spring 比喻成一个大型工具箱 :
🔨 它提供了各种工具(模块),你可以根据需要选择使用
📦 这些工具都是精心设计、互相配合的
🔧 你不需要自己制造轮子,Spring 已经帮你做好了一切准备
1.3 Spring 的核心优势
flowchart TD
A["☕️ Spring 框架优势"] --> B["🔓 低侵入性"]
A --> C["🧩 组件化设计"]
A --> D["📦 强大生态"]
A --> E["🧪 易于测试"]
A --> F["🔄 灵活扩展"]
B --> B1["不强制实现接口\n保持代码纯净"]
C --> C1["核心模块独立\n按需引入"]
D --> D1["Spring Boot\nSpring Cloud\nSpring Security"]
E --> E1["依赖注入\nMock 测试"]
F --> F1["第三方框架\n无缝集成"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#e3f2fd
style E fill:#f8bbd0
style F fill:#fff3e0
让我详细解释 Spring 的四大核心优势:
1️⃣ 低侵入性 —— 保持代码纯净
传统的 EJB 框架要求开发者实现特定的接口(如 SessionBean),这会让你的业务代码充满框架特定的代码。而 Spring 不要求你实现任何特定接口,你的类可以保持 Plain Old Java Object(POJO)的形态。这意味着:
你的代码更容易理解
你的代码更容易在其他项目中使用
你可以在任何时候去掉 Spring,而不改变业务代码
2️⃣ 组件化设计 —— 按需引入
Spring 采用了模块化的设计理念,主要模块包括:
spring-core:核心工具类
spring-beans:Bean 的创建和管理
spring-context:应用上下文
spring-aop:面向切面编程
spring-web:Web 开发支持
spring-tx:事务管理
你可以根据项目需要,只引入需要的模块,而不需要加载整个框架。
3️⃣ 强大生态 —— 一站式解决方案
Spring 生态非常丰富,包括:
Spring Boot :简化 Spring 应用开发
Spring Cloud :微服务架构解决方案
Spring Security :安全认证授权
Spring Data :数据访问统一抽象
Spring Batch :批处理框架
Spring Integration :消息集成
4️⃣ 易于测试 —— 可维护性高
Spring 的依赖注入让对象之间的依赖关系变得清晰,你可以轻松使用 Mock 对象进行单元测试,而不需要启动整个容器。
1.4 Spring 框架模块全景
flowchart TD
A["📦 Spring Framework"] --> B["🌱 Spring Core\n核心容器"]
A --> C["🧩 Spring AOP\n面向切面"]
A --> D["📝 Spring DAO\n数据访问"]
A --> E["🌐 Spring Web\nWeb 开发"]
A --> F["🧪 Spring Test\n测试框架"]
B --> B1["IoC/DI"]
B --> B2["BeanFactory"]
B --> B3["ApplicationContext"]
D --> D1["JDBC Template"]
D --> D2["事务管理"]
E --> E1["Spring MVC"]
E --> E2["WebSocket"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#e3f2fd
style D fill:#fff3e0
style E fill:#f8bbd0
style F fill:#e3f2fd
各模块详细介绍:
模块
功能
核心类/接口
Spring Core
核心容器,提供 IoC 和 DI 功能
BeanFactory, ApplicationContext
Spring AOP
面向切面编程,支持事务、日志等功能
Aspect, JoinPoint, Advice
Spring DAO
数据访问,简化 JDBC 操作
JdbcTemplate, TransactionManager
Spring Web
Web 开发支持
DispatcherServlet, Controller
Spring Test
单元测试支持
SpringRunner, MockMvc
1.5 Spring vs Java EE 对比
对比维度
Spring
Java EE (Jakarta EE)
学习曲线
平缓,容易上手
陡峭,需要学习大量规范
配置方式
灵活(注解/Java Config/XML)
规范但复杂
侵入性
低侵入,保持 POJO
较高侵入
轻量级
✅ 轻量级容器
重量级容器
灵活性
高度灵活
规范性更强
生态
极其丰富
逐渐追赶
社区活跃度
非常活跃
一般
二、Spring IoC 容器与依赖注入 2.1 什么是 IoC? IoC(Inversion of Control)控制反转 ,是 Spring 的核心设计思想,也是面向对象设计原则中依赖倒置原则 的具体实现。
要理解 IoC,我们需要先了解传统编程方式的问题 :
flowchart LR
A["❌ 传统方式"] --> A1["程序主动\nnew 对象"]
A1 --> A2["对象间耦合\n难以测试"]
B["✅ IoC 方式"] --> B1["容器创建\n管理对象"]
B1 --> B2["松耦合\n易测试"]
style A fill:#ffcdd2
style B fill:#c8e6c9
传统方式的问题举例:
假设我们有一个 UserService 类需要使用 UserDao 来操作数据库:
1 2 3 4 5 6 7 8 public class UserService { private UserDao userDao = new UserDao (); public void save (User user) { userDao.save(user); } }
这种方式的问题 是:
UserService 和 UserDao 紧耦合,无法替换成其他实现
如果 UserDao 的构造函数变了(比如需要参数),UserService 也需要改
单元测试时,无法用 Mock 对象替换真实的 UserDao
IoC 的解决思路:
IoC 的核心思想是**”不要让类自己创建依赖,而是让外部(容器)把依赖传进来”**。
打个比方:
🎬 传统方式 :你(在餐厅)自己做饭
🍽️ IoC 方式 :你(在餐厅)点菜,由厨房(容器)做好端给你
2.2 什么是 DI? DI(Dependency Injection)依赖注入 ,是 IoC 的一种具体实现方式。容器在创建 Bean 时,自动将依赖的对象注入到目标 Bean 中。
我们可以把 DI 想象成医院的输液系统 :
💉 病人(目标对象)需要药物(依赖)
💉 不是病人自己去找药,而是护士(容器)把药接上
💉 药瓶空了?护士会自动换一个新的(容器管理对象生命周期)
2.3 Spring IoC 容器详解
flowchart TD
A["🏭 Spring IoC 容器"] --> B["📦 BeanFactory\n(基础容器)"]
A --> C["📋 ApplicationContext\n(高级容器)"]
B --> B1["延迟加载\n轻量级"]
C --> C1["启动即加载\n事件机制\n资源国际化"]
C1 --> C2["ClassPathXmlApplicationContext"]
C1 --> C3["FileSystemXmlApplicationContext"]
C1 --> C4["AnnotationConfigApplicationContext"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#e3f2fd
Spring IoC 容器的核心接口:
BeanFactory :最基础的 IoC 容器接口
采用延迟加载策略,只有当调用 getBean() 时才创建 Bean
适合资源敏感的场景
ApplicationContext :功能更强大的高级容器
启动时即加载所有 Bean
提供国际化(i18n)支持
提供事件发布机制
是我们在实际开发中最常用的接口
ApplicationContext 的三个实现类:
实现类
适用场景
配置文件位置
ClassPathXmlApplicationContext
XML 配置方式
类路径下(src)
FileSystemXmlApplicationContext
XML 配置方式
文件系统路径
AnnotationConfigApplicationContext
注解配置方式
Java 代码中指定
2.4 三种依赖注入方式详解
flowchart TD
A["💉 依赖注入方式"] --> B["🔨 构造器注入"]
A --> C["📝 Setter 注入"]
A --> D["🔍 字段注入"]
B --> B1["✅ 推荐\n强制依赖\n不可变对象"]
C --> C1["⚠️ 可选依赖\nSetter 多次调用"]
D --> D2["❌ 不推荐\n难以测试\n隐蔽依赖"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#fff3e0
style D fill:#ffcdd2
方式一:构造器注入(推荐使用)
构造器注入是 Spring 官方推荐的注入方式,特别适合处理必选依赖 。
优点:
✅ 依赖对象在构造时就完全初始化,不会出现空指针
✅ 构造器参数可以声明为 final,保证不可变性
✅ 通过构造器可以明确看出这个类需要哪些依赖
✅ 有利于单元测试,直接传 Mock 对象即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Component public class UserService { private final UserDao userDao; private final EmailService emailService; @Autowired public UserService (UserDao userDao, EmailService emailService) { this .userDao = userDao; this .emailService = emailService; } public void register (User user) { userDao.save(user); emailService.sendWelcomeEmail(user.getEmail()); } }
方式二:Setter 注入
Setter 注入通过 setter 方法注入依赖,适合处理可选依赖 。
使用场景:
依赖可能在后续操作中才设置
需要在运行时更换依赖实现
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 @Component public class UserService { private UserDao userDao; private NotificationService notificationService; @Autowired public void setUserDao (UserDao userDao) { this .userDao = userDao; } @Autowired public void setNotificationService (NotificationService notificationService) { this .notificationService = notificationService; } public void notifyUser (Long userId, String message) { if (notificationService != null ) { notificationService.send(userId, message); } } }
方式三:字段注入(不推荐)
字段注入直接在字段上加 @Autowired,看起来最简洁,但不推荐使用。
为什么不推荐?
❌ 依赖隐蔽,构造器中看不到有哪些依赖
❌ 难以进行单元测试,无法手动传入 Mock 对象
❌ 容易导致空指针异常(忘记注入)
❌ 违反单一职责原则,类职责不清晰
1 2 3 4 5 6 7 8 9 10 11 12 @Component public class UserService { @Autowired private UserDao userDao; @Autowired private EmailService emailService; }
2.5 @Autowired 注解详解 @Autowired 是 Spring 中最常用的依赖注入注解,它有几个关键用法:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 @Component public class UserService { private UserDao userDao; public UserService (UserDao userDao) { this .userDao = userDao; } } @Component public class UserService { private UserDao userDao; @Autowired public void setUserDao (UserDao userDao) { this .userDao = userDao; } } @Component public class UserService { @Autowired private UserDao userDao; } @Component public class UserService { @Autowired @Qualifier("mysqlUserDao") private UserDao userDao; } @Repository @Primary public class MysqlUserDao implements UserDao { } @Repository public class OracleUserDao implements UserDao { }
三、Spring Bean 管理详解 3.1 Bean 的作用域 Spring 根据作用域将 Bean 分为以下几类:
flowchart TD
A["📋 Bean 作用域"] --> B["singleton\n单例模式"]
A --> C["prototype\n原型模式"]
A --> D["request\n请求级别"]
A --> E["session\n会话级别"]
B --> B1["整个应用只有一个\n(默认)"]
C --> C1["每次获取\n创建新实例"]
D --> D1["同一 HTTP 请求\n共享实例"]
E --> E1["同一 HTTP 会话\n共享实例"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#e3f2fd
style D fill:#fff3e0
style E fill:#f8bbd0
详解五种作用域:
1️⃣ singleton(单例模式)—— 默认作用域
在 Spring IoC 容器中,每个 Bean 只会有一个共享实例 。无论你获取多少次这个 Bean,得到的都是同一个对象。
使用场景:
无状态的 Bean(不保存实例状态,如 Service、Repository)
性能优化,减少对象创建开销
1 2 3 4 5 6 7 8 9 @Component @Scope("singleton") public class UserService { private int requestCount = 0 ; public void addRequest () { requestCount++; } }
2️⃣ prototype(原型模式)
每次从容器中获取 Bean 时,都会创建一个新的实例 。
使用场景:
有状态的 Bean,需要保持每次请求的独立性
对象创建成本较高,且不需要共享
1 2 3 4 5 6 7 8 9 @Component @Scope("prototype") public class OrderService { private List<OrderItem> items = new ArrayList <>(); public void addItem (OrderItem item) { items.add(item); } }
3️⃣ request(请求级别)—— Web 应用专用
同一个 HTTP 请求内,所有获取的 Bean 都是同一个实例;不同请求则创建不同实例。
1 2 3 4 5 6 7 8 9 10 @Component @Scope("request") public class RequestScopeService { private String requestId; @PostConstruct public void init () { this .requestId = UUID.randomUUID().toString(); } }
4️⃣ session(会话级别)—— Web 应用专用
同一用户会话内获取的 Bean 是同一个实例;不同用户会话则创建不同实例。
1 2 3 4 5 6 7 8 9 @Component @Scope("session") public class UserPreferencesService { private UserPreferences preferences; public void setTheme (String theme) { preferences.setTheme(theme); } }
5️⃣ application(应用级别)
整个 Web 应用只有一个实例,类似于单例但属于 ServletContext 范围。
1 2 3 4 5 6 7 8 9 @Component @Scope("application") public class AppCounterService { private int totalUsers; public void increment () { totalUsers++; } }
3.2 Bean 的生命周期详解 理解 Bean 的生命周期,有助于我们在正确的时机执行初始化和清理逻辑:
flowchart TD
A["🔄 Bean 生命周期"] --> B["1️⃣ 实例化"]
B --> C["2️⃣ 属性填充\n(依赖注入)"]
C --> D["3️⃣ BeanNameAware"]
D --> E["4️⃣ BeanFactoryAware"]
E --> F["5️⃣ ApplicationContextAware"]
F --> G["6️⃣ BeanPostProcessor\n前置处理"]
G --> H["7️⃣ InitializingBean\nafterPropertiesSet"]
H --> I["8️⃣ 自定义 init-method"]
I --> J["9️⃣ BeanPostProcessor\n后置处理"]
J --> K["🔟 Bean 就绪状态"]
K --> L["🛑 销毁\nDisposableBean\ndestroy-method"]
style A fill:#fff3e0
style K fill:#c8e6c9
style L fill:#f8bbd0
每个阶段详解:
阶段
说明
可实现接口/注解
1. 实例化
调用构造器创建 Bean 实例
无
2. 属性填充
Spring 注入配置的属性和依赖
@Autowired, @Value
3. BeanNameAware
将 Bean 的名称注入到 Bean 中
BeanNameAware
4. BeanFactoryAware
将 BeanFactory 注入到 Bean 中
BeanFactoryAware
5. ApplicationContextAware
将 ApplicationContext 注入
ApplicationContextAware
6. BeanPostProcessor 前置
对 Bean 进行预处理
BeanPostProcessor
7. InitializingBean
初始化逻辑
InitializingBean.afterPropertiesSet()
8. 自定义 init-method
自定义初始化方法
@PostConstruct 或 @Bean(initMethod)
9. BeanPostProcessor 后置
对 Bean 进行后处理
BeanPostProcessor
10. Bean 就绪
Bean 可以正常使用了
-
11. 销毁
清理资源,关闭连接
DisposableBean.destroy()
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 @Component public class UserService implements InitializingBean , DisposableBean { private String name; private UserDao userDao; public UserService () { System.out.println("1️⃣ UserService 构造器被调用" ); } @Autowired public void setName (String name) { System.out.println("2️⃣ 属性注入:" + name); this .name = name; } @Override public void setBeanName (String name) { System.out.println("3️⃣ Bean 名称:" + name); } @Override public void afterPropertiesSet () throws Exception { System.out.println("7️⃣ InitializingBean.afterPropertiesSet() 被调用" ); } @PostConstruct public void init () { System.out.println("8️⃣ @PostConstruct 初始化方法被调用" ); } @Override public void destroy () throws Exception { System.out.println("🛑 DisposableBean.destroy() 被调用" ); } @PreDestroy public void cleanup () { System.out.println("🗑️ @PreDestroy 销毁方法被调用" ); } }
3.3 组件扫描与注册 Spring 通过组件扫描 (Component Scanning)自动发现和注册带有 @Component 及其衍生注解(如 @Service、@Repository、@Controller)的类。
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 34 35 36 37 38 39 40 41 42 @Configuration @ComponentScan(basePackages = "com.example") public class AppConfig {} @Configuration @ComponentScan( basePackages = "com.example", excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Dao") ) public class AppConfig {} @Configuration @ComponentScan( basePackages = "com.example", includeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = {Controller.class, Service.class} ) ) public class AppConfig {} @Configuration @ComponentScan( basePackages = "com.example", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class // 排除所有 Controller ) ) public class AppConfig {}
3.4 @Conditional 条件注册 @Conditional 注解允许我们根据特定条件决定是否注册某个 Bean,这在多环境配置和功能开关场景中非常有用。
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 34 35 36 37 38 39 40 41 42 43 44 45 46 @Configuration public class AppConfig { @Bean @ConditionalOnClass(name = "com.example.thirdparty.ThirdPartyService") public ThirdPartyService thirdPartyService () { return new ThirdPartyService (); } } @Configuration @ConditionalOnProperty(name = "feature.enabled", havingValue = "true") public class FeatureConfig { @Bean public FeatureService featureService () { return new FeatureService (); } } @Configuration @Profile("prod") public class ProdConfig { @Bean public AlertService alertService () { return new EmailAlertService (); } } @Configuration @Profile("dev") public class DevConfig { @Bean public AlertService alertService () { return new ConsoleAlertService (); } }
四、Spring AOP 面向切面编程 4.1 为什么需要 AOP? 在软件开发中,有一些横切关注点 (Cross-cutting Concerns)会分散在多个模块中,例如:
📝 日志记录 :在每个方法开始和结束时记录日志
🔒 安全控制 :在方法执行前检查用户权限
🔄 事务管理 :在方法开始时开启事务,结束时提交/回滚
📊 性能监控 :记录每个方法的执行时间
这些功能如果每个方法都手动编写,会导致:
代码重复:同样的逻辑在多个地方重复
业务逻辑不清晰:核心业务被横切逻辑污染
修改成本高:横切逻辑改变需要修改所有模块
AOP 的解决方案是 :将这些横切关注点封装成独立的模块(切面),然后声明式地将它们”织入”到需要的方法中。
4.2 AOP 核心概念详解
flowchart TD
A["🧩 AOP 核心概念"] --> B["🎯 Aspect\n切面"]
A --> C["📍 Pointcut\n切入点"]
A --> D["➡️ Join Point\n连接点"]
A --> E["🔔 Advice\n通知"]
A --> F["🔄 Weaving\n织入"]
B --> B1["封装横切逻辑\n的模块"]
C --> C1["定义哪些\nJoin Point\n需要拦截"]
D --> D1["可被拦截\n的方法调用"]
E --> E1["拦截后\n执行的逻辑"]
E1 --> E2["前置通知"]
E1 --> E3["后置通知"]
E1 --> E4["返回通知"]
E1 --> E5["异常通知"]
E1 --> E6["环绕通知"]
style A fill:#fff3e0
核心概念详解:
🎯 Aspect(切面): 切面是横切关注点的模块化封装 。你可以把切面想象成一个”插件”,它定义了在哪些地方(切入点)做什么(通知)。
📍 Pointcut(切入点): 切入点用于定义哪些连接点需要被拦截 。你可以理解为”在哪些方法上动手脚”的规则。
➡️ Join Point(连接点): 连接点是程序执行的某个位置 ,可以被切面拦截。在 Spring AOP 中,连接点特指方法调用 。
🔔 Advice(通知/增强): 通知是拦截到连接点后执行的逻辑 ,分为五种类型:
@Before:前置通知,在方法执行前执行
@After:后置通知,在方法执行后执行(无论成功或失败)
@AfterReturning:返回通知,在方法成功返回后执行
@AfterThrowing:异常通知,在方法抛出异常时执行
@Around:环绕通知,可以控制方法是否执行以及执行时机
🔄 Weaving(织入): 织入是把切面应用到目标对象的过程。Spring AOP 默认采用动态代理 方式进行织入。
4.3 AOP 工作流程图解
flowchart LR
A["📋 AOP 工作流程"] --> B["客户端请求"]
B --> C["目标方法调用"]
C --> D{"切点匹配?"}
D -->|"是"| E["前置通知\n@Before"]
E --> F["目标方法执行"]
F --> G["后置通知\n@AfterReturning"]
G --> H["返回客户端"]
D -->|"否"| H
F -.->|"异常时"| I["异常通知\n@AfterThrowing"]
I --> H
style E fill:#c8e6c9
style G fill:#c8e6c9
style I fill:#ffcdd2
4.4 AOP 示例代码详解 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 @Aspect @Component public class LoggingAspect { @Pointcut("execution(* com.example.service.*.*(..))") public void servicePointcut () {} @Pointcut("@annotation(com.example.annotation.Loggable)") public void loggablePointcut () {} @Before("servicePointcut()") public void before (JoinPoint joinPoint) { String methodName = joinPoint.getSignature().toShortString(); Object[] args = joinPoint.getArgs(); System.out.println("🔔 [前置通知] 方法 " + methodName + " 即将执行" ); System.out.println(" 参数:" + Arrays.toString(args)); } @After("servicePointcut()") public void after (JoinPoint joinPoint) { String methodName = joinPoint.getSignature().toShortString(); System.out.println("🔔 [后置通知] 方法 " + methodName + " 执行完成" ); } @AfterReturning(pointcut = "servicePointcut()", returning = "result") public void afterReturning (JoinPoint joinPoint, Object result) { String methodName = joinPoint.getSignature().toShortString(); System.out.println("✅ [返回通知] 方法 " + methodName + " 返回结果:" + result); } @AfterThrowing(pointcut = "servicePointcut()", throwing = "exception") public void afterThrowing (JoinPoint joinPoint, Throwable exception) { String methodName = joinPoint.getSignature().toShortString(); System.out.println("❌ [异常通知] 方法 " + methodName + " 抛出异常:" + exception.getMessage()); } @Around("servicePointcut()") public Object around (ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().toShortString(); long startTime = System.currentTimeMillis(); System.out.println("⏰ [环绕通知] 方法 " + methodName + " 开始执行" ); try { Object result = joinPoint.proceed(); long duration = System.currentTimeMillis() - startTime; System.out.println("⏱️ [环绕通知] 方法 " + methodName + " 执行成功,耗时:" + duration + "ms" ); return result; } catch (Exception e) { long duration = System.currentTimeMillis() - startTime; System.out.println("❌ [环绕通知] 方法 " + methodName + " 执行失败,耗时:" + duration + "ms" ); throw e; } } }
4.5 常用切入点表达式详解 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 @Aspect @Component public class PointcutExpressions { @Pointcut("execution(* com.example.service.UserService.*(..))") public void userServicePointcut () {} @Pointcut("execution(* com.example.service.UserService.findById(Long))") public void findByIdPointcut () {} @Pointcut("execution(com.example.entity.User com.example.service.*.*(..))") public void userReturnPointcut () {} @Pointcut("execution(* com.example.service..*.*(..))") public void servicePackagePointcut () {} @Pointcut("execution(public * *(..))") public void publicMethodPointcut () {} @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)") public void transactionalPointcut () {} @Pointcut("execution(* com.example.service..*.*(..)) && !execution(* com.example.service..save*(..))") public void serviceExceptSavePointcut () {} @Pointcut("within(com.example.service.UserService)") public void withinPointcut () {} @Pointcut("this(com.example.service.UserService)") public void thisPointcut () {} @Pointcut("execution(* com.example.service.UserService.findById(Long))") public void argsPointcut () {} }
4.6 自定义注解 + AOP 实战 通过结合自定义注解和 AOP,我们可以实现功能更明确、使用更便捷的切面。
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Loggable { String value () default "" ; LogLevel level () default LogLevel.INFO; } enum LogLevel { DEBUG, INFO, WARN, ERROR } @Aspect @Component public class LoggableAspect { @Around("@annotation(Loggable)") public Object around (ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable { String methodName = joinPoint.getSignature().toShortString(); String annotationValue = loggable.value(); LogLevel level = loggable.level(); log(level, "📝 [" + level + "] 方法 " + methodName + " 注解值:" + annotationValue); long startTime = System.currentTimeMillis(); try { Object result = joinPoint.proceed(); long duration = System.currentTimeMillis() - startTime; log(level, "✅ 方法执行成功,耗时:" + duration + "ms" ); return result; } catch (Exception e) { long duration = System.currentTimeMillis() - startTime; log(LogLevel.ERROR, "❌ 方法执行异常:" + e.getMessage()); throw e; } } private void log (LogLevel level, String message) { switch (level) { case DEBUG: System.out.println("[DEBUG] " + message); break ; case INFO: System.out.println("[INFO] " + message); break ; case WARN: System.out.println("[WARN] " + message); break ; case ERROR: System.err.println("[ERROR] " + message); break ; } } } @Service public class UserService { @Loggable(value = "查询用户详情", level = LogLevel.INFO) public User findById (Long id) { return userDao.findById(id); } @Loggable(value = "保存用户", level = LogLevel.WARN) public User save (User user) { return userDao.save(user); } }
五、Spring 事务管理 5.1 为什么需要事务? 事务是一组操作,要么全部成功,要么全部失败 的机制。典型场景是银行转账:
假设 A 账户给 B 账户转账 1000 元,操作包括:
从 A 账户扣除 1000 元
向 B 账户增加 1000 元
如果第一步成功但第二步失败(比如数据库崩溃),A 账户的钱就莫名其妙少了!这就需要事务来保证:
flowchart TD
A["🔒 事务 ACID 特性"] --> B["A - Atomicity\n原子性"]
A --> C["C - Consistency\n一致性"]
A --> D["I - Isolation\n隔离性"]
A --> E["D - Durability\n持久性"]
B --> B1["全部成功\n或全部失败"]
C --> C1["事务前后\n数据合法一致"]
D --> D1["并发控制\n隔离级别"]
E --> E1["提交后\n永久保存"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#e3f2fd
style E fill:#f8bbd0
ACID 特性详解:
A - Atomicity(原子性): 事务中的所有操作要么全部完成,要么全部不执行。如果事务中途失败,已经执行的操作会被撤销。
C - Consistency(一致性): 事务执行前后,数据库的状态保持一致。例如转账前后,总金额不变。
I - Isolation(隔离性): 并发执行的事务之间互不干扰。数据库通过隔离级别来控制并发程度。
D - Durability(持久性): 事务一旦提交,其对数据库的修改就是永久性的,即使系统崩溃也不会丢失。
5.2 Spring 事务管理方式 Spring 提供两种事务管理方式:
方式一:编程式事务(了解即可,不推荐使用)
通过编写代码手动管理事务,可以精确控制,但代码侵入性高。
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 @Service public class UserService { @Autowired private PlatformTransactionManager transactionManager; public void transfer (Long fromId, Long toId, BigDecimal amount) { DefaultTransactionDefinition definition = new DefaultTransactionDefinition (); definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); TransactionStatus status = transactionManager.getTransaction(definition); try { accountDao.deduct(fromId, amount); accountDao.add(toId, amount); transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); throw e; } } }
方式二:声明式事务(推荐使用)
通过 @Transactional 注解声明式地管理事务,代码侵入性低,是实际开发中的主要方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Service public class UserService { @Transactional(rollbackFor = Exception.class) public void transfer (Long fromId, Long toId, BigDecimal amount) { accountDao.deduct(fromId, amount); accountDao.add(toId, amount); } @Transactional public class UserService { public void saveUser (User user) { } public void updateUser (User user) { } public void deleteUser (Long id) { } } }
5.3 @Transactional 注解详解 @Transactional 注解有多个参数,每个参数控制事务的不同方面:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 @Service public class UserService { @Transactional(rollbackFor = Exception.class) public void method1 () { } @Transactional(noRollbackFor = BusinessException.class) public void method2 () { } @Transactional(propagation = Propagation.REQUIRED) public void method3 () { } @Transactional(isolation = Isolation.REPEATABLE_READ) public void method4 () { } @Transactional(timeout = 30) public void method5 () { } @Transactional(readOnly = true) public void method6 () { } }
5.4 事务传播行为详解 事务传播行为定义了当一个事务方法被另一个事务方法调用时,事务如何处理 。
flowchart LR
A["🔄 事务传播行为"] --> B["REQUIRED\n(默认)"]
A --> C["REQUIRES_NEW"]
A --> D["NESTED"]
A --> E["SUPPORTS"]
B --> B1["加入已有事务\n或创建新事务"]
C --> C1["挂起当前事务\n创建全新事务"]
D --> D1["嵌套执行\n使用保存点"]
E --> E1["有则加入\n无则非事务"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#e3f2fd
style D fill:#fff3e0
style E fill:#f8bbd0
常见传播行为详解:
传播行为
说明
适用场景
REQUIRED
如果当前有事务,加入;否则创建新事务
大多数业务操作
REQUIRES_NEW
挂起当前事务,创建全新事务
独立记录日志
NESTED
嵌套事务(使用保存点)
部分操作需要独立回滚
SUPPORTS
如果当前有事务加入;否则非事务执行
查询为主的操作
MANDATORY
必须在事务中执行
强制要求事务的方法
NOT_SUPPORTED
挂起事务,非事务执行
不需要事务的操作
5.5 Spring 事务原理 Spring 事务的原理基于 AOP 动态代理 。当你使用 @Transactional 注解时:
flowchart TD
A["🔄 Spring 事务原理"] --> B["1️⃣ Spring 为目标类\n创建代理对象"]
B --> C["2️⃣ 代理对象在调用\n目标方法前\n检查 @Transactional"]
C --> D["3️⃣ 如果有注解\n开启事务\n获取数据库连接"]
D --> E["4️⃣ 执行目标方法"]
E --> F{"执行结果?"}
F -->|"正常"| G["提交事务"]
F -->|"异常"| H["回滚事务"]
G --> I["返回结果"]
H --> I
style A fill:#fff3e0
style C fill:#c8e6c9
style G fill:#e3f2fd
style H fill:#ffcdd2
⚠️ 重要注意事项:@Transactional 在内部方法调用时不生效
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 34 35 36 37 38 @Service public class UserService { @Transactional public void outerMethod () { innerMethod(); } @Transactional public void innerMethod () { } } @Service public class UserService { @Autowired private UserService self; @Transactional public void outerMethod () { self.innerMethod(); } @Transactional public void innerMethod () { } }
六、Spring MVC 开发 Web 应用 6.1 Spring MVC 概述 Spring MVC 是 Spring 框架的 Web 模块,专门用于开发 Web 应用。它遵循 MVC(Model-View-Controller) 设计模式,将数据处理、页面展示、业务控制分离。
MVC 职责分工:
角色
职责
Spring MVC 对应
Model(模型)
业务数据处理
Service、Repository、Entity
View(视图)
页面展示
JSP、Thymeleaf、JSON
Controller(控制器)
接收请求、调用服务、返回响应
@Controller、@RestController
6.2 Spring MVC 工作流程
flowchart TD
A["🔄 Spring MVC 工作流程"] --> B["1️⃣ 请求到达\nDispatchServlet"]
B --> C["2️⃣ 处理器映射\nHandlerMapping\n根据 URL 找到\n对应的 Controller"]
C --> D["3️⃣ 处理器适配器\nHandlerAdapter\n执行 Controller\n的方法"]
D --> E["4️⃣ Controller\n执行业务逻辑\n返回 ModelAndView"]
E --> F["5️⃣ 视图解析器\nViewResolver\n根据视图名\n找到对应视图"]
F --> G["6️⃣ 视图渲染\nView\n将 Model 数据\n填充到视图模板"]
G --> H["7️⃣ 返回响应\n给浏览器"]
style A fill:#fff3e0
style E fill:#c8e6c9
流程详解:
DispatchServlet 接收请求 :作为前端控制器,所有请求都先到达这里
HandlerMapping 查找 Controller :根据 URL 找到对应的处理方法
HandlerAdapter 执行 Controller :调用具体的处理方法
Controller 返回 ModelAndView :包含模型数据和视图名
ViewResolver 解析视图 :根据视图名找到实际的视图文件
View 渲染视图 :将数据填充到模板中
返回响应 :生成 HTML 或 JSON 返回给浏览器
6.3 @Controller vs @RestController 这两个注解用于不同的场景:
@Controller:返回视图(JSP、Thymeleaf 等模板)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Controller @RequestMapping("/users") public class UserController { @RequestMapping("/list") public String list (Model model) { List<User> users = userService.findAll(); model.addAttribute("users" , users); return "user/list" ; } @RequestMapping("/detail/{id}") public String detail (@PathVariable Long id, Model model) { User user = userService.findById(id); model.addAttribute("user" , user); return "user/detail" ; } }
@RestController:返回 JSON 数据(前后端分离)
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 34 35 36 37 38 39 40 41 42 @RestController @RequestMapping("/api/users") public class UserApiController { @Autowired private UserService userService; @GetMapping public List<User> list () { return userService.findAll(); } @GetMapping("/{id}") public User getById (@PathVariable Long id) { return userService.findById(id); } @PostMapping public Result<User> create (@RequestBody @Valid User user) { User saved = userService.save(user); return Result.success(saved); } @PutMapping("/{id}") public Result<Void> update (@PathVariable Long id, @RequestBody @Valid User user) { user.setId(id); userService.update(user); return Result.success(); } @DeleteMapping("/{id}") public Result<Void> delete (@PathVariable Long id) { userService.deleteById(id); return Result.success(); } }
6.4 请求参数绑定详解 Spring MVC 提供了丰富的参数绑定方式:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 @RestController @RequestMapping("/api/products") public class ProductController { @GetMapping("/{category}/{id}") public Product getById (@PathVariable Long id, @PathVariable String category) { return productService.findById(id); } @GetMapping("/list") public PageResult<Product> list ( @RequestParam(defaultValue = "1") int page, // 默认值 @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String keyword, // 可选参数 @RequestParam(required = false) String sort) { return productService.findByPage(page, size, keyword, sort); } @PostMapping public Result<Product> create (@RequestBody @Valid Product product) { return Result.success(productService.save(product)); } @PostMapping("/form") public Result<Void> submitForm (@RequestParam String name, @RequestParam String email) { return Result.success(); } @GetMapping("/header") public String getHeader ( @RequestHeader("Authorization") String auth, @RequestHeader(value = "X-Custom-Header", required = false) String custom) { return "Auth: " + auth + ", Custom: " + custom; } @GetMapping("/cookie") public String getCookie (@CookieValue("JSESSIONID") String sessionId) { return "Session ID: " + sessionId; } @GetMapping("/search") public List<Product> search (ProductQuery query) { return productService.search(query); } @GetMapping("/raw") public String raw (HttpServletRequest request, HttpServletResponse response) { String method = request.getMethod(); String remoteAddr = request.getRemoteAddr(); return "Method: " + method + ", IP: " + remoteAddr; } } @Data public class ProductQuery { private String keyword; private String category; private BigDecimal minPrice; private BigDecimal maxPrice; @Min(1) private Integer page = 1 ; @Max(100) private Integer size = 10 ; }
6.5 响应处理与数据封装 为了保持 API 响应格式的一致性,我们通常会定义统一的响应结构:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 @Data public class Result <T> { private int code; private String message; private T data; public static <T> Result<T> success (T data) { Result<T> result = new Result <>(); result.setCode(200 ); result.setMessage("success" ); result.setData(data); return result; } public static <T> Result<T> success () { return success(null ); } public static <T> Result<T> error (String message) { return error(500 , message); } public static <T> Result<T> error (int code, String message) { Result<T> result = new Result <>(); result.setCode(code); result.setMessage(message); return result; } } @Data public class PageResult <T> { private long total; private int page; private int size; private int totalPages; private List<T> data; public static <T> PageResult<T> of (Page<T> page) { PageResult<T> result = new PageResult <>(); result.setTotal(page.getTotalElements()); result.setPage(page.getNumber() + 1 ); result.setSize(page.getSize()); result.setTotalPages(page.getTotalPages()); result.setData(page.getContent()); return result; } }
6.6 统一异常处理 为了给用户友好的错误提示,我们需要统一处理各种异常:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public Result<Void> handleBusinessException (BusinessException e) { System.out.println("业务异常:" + e.getMessage()); return Result.error(400 , e.getMessage()); } @ExceptionHandler(MethodArgumentNotValidException.class) public Result<Void> handleValidationException ( MethodArgumentNotValidException e) { String message = e.getBindingResult().getFieldError() .getDefaultMessage(); return Result.error(400 , "参数校验失败:" + message); } @ExceptionHandler(EntityNotFoundException.class) public Result<Void> handleNotFound (EntityNotFoundException e) { return Result.error(404 , e.getMessage()); } @ExceptionHandler(DataIntegrityViolationException.class) public Result<Void> handleDataIntegrityViolation ( DataIntegrityViolationException e) { return Result.error(400 , "数据重复,请检查后重试" ); } @ExceptionHandler(Exception.class) public Result<Void> handleException (Exception e) { e.printStackTrace(); return Result.error(500 , "系统异常,请稍后重试" ); } } public class BusinessException extends RuntimeException { public BusinessException (String message) { super (message); } } public class EntityNotFoundException extends RuntimeException { public EntityNotFoundException (String entity, Long id) { super (entity + " 不存在,ID:" + id); } }
七、Spring Boot 快速开发 7.1 什么是 Spring Boot? Spring Boot 是 Spring 的子项目 ,它的目标是简化 Spring 应用的创建和开发过程 。你可以把 Spring Boot 理解为”Spring 的一键启动器”。
Spring Boot 的核心特性:
flowchart TD
A["🚀 Spring Boot 特性"] --> B["☕️ 开箱即用"]
A --> C["🎯 习惯优于配置"]
A --> D["🛠️ 内嵌服务器"]
A --> E["📊 自动配置"]
A --> F["🧪 内置测试"]
B --> B1["引入依赖\n直接运行"]
C --> C1["无需 XML 配置\nJava Config 优先"]
D --> D1["Tomcat/Jetty\n无需部署 WAR"]
E --> E1["自动配置\nSpring/Spring MVC"]
F --> F1["JUnit/MockMvc\n自动化测试"]
style A fill:#fff3e0
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#e3f2fd
style E fill:#fff3e0
style F fill:#f8bbd0
1️⃣ 开箱即用: Spring Boot 提供了大量的”起步依赖”(Starter Dependencies),你只需要引入一个依赖,就能获得完整的模块功能。
2️⃣ 习惯优于配置: Spring Boot 会根据你引入的依赖和类路径,自动配置应用程序。你不需要手动配置每一个 Bean。
3️⃣ 内嵌服务器: Spring Boot 内嵌了 Tomcat、Jetty 等 Web 服务器,可以直接打包成可执行的 JAR 文件运行,无需部署到外部服务器。
4️⃣ 自动配置: Spring Boot 会根据你的环境自动配置 Bean,例如:
如果类路径中有 MySQL 驱动,自动配置数据源
如果类路径中有 Spring MVC,自动配置 DispatcherServlet
5️⃣ 内置测试: Spring Boot 提供了完整的测试支持,可以方便地编写集成测试和单元测试。
7.2 Spring Boot 项目结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 my-spring-boot-project/ ├── src/ │ ├── main/ │ │ ├── java/com/example/ │ │ │ ├── Application.java # 启动类 │ │ │ ├── controller/ # 控制器层(接收请求) │ │ │ ├── service/ # 服务层(业务逻辑) │ │ │ ├── repository/ # 数据访问层(操作数据库) │ │ │ ├── entity/ # 实体类(数据库表映射) │ │ │ ├── dto/ # 数据传输对象 │ │ │ ├── config/ # 配置类 │ │ │ └── exception/ # 异常处理 │ │ └── resources/ │ │ ├── application.yml # 配置文件 │ │ ├── static/ # 静态资源(CSS/JS/图片) │ │ └── templates/ # 模板文件 │ └── test/ # 测试代码 ├── pom.xml # Maven 配置 └── README.md # 项目说明
分层职责说明:
层级
职责
包含内容
Controller
接收请求、参数校验、调用服务
@RestController、@GetMapping 等
Service
业务逻辑处理、事务管理
@Service、@Transactional
Repository
数据访问、操作数据库
JpaRepository、MyBatis Mapper
Entity
数据模型、ORM 映射
@Entity、@Table、@Column
7.3 Spring Boot 启动类 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 34 35 36 37 38 39 40 41 42 43 44 45 @SpringBootApplication public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } } public class CustomApplication { public static void main (String[] args) { SpringApplication app = new SpringApplication (Application.class); app.setBannerMode(Banner.Mode.OFF); app.setBanner(new Banner () { @Override public void printBanner (Environment environment, Class<?> sourceClass, PrintStream out) { out.println("========================================" ); out.println(" 🚀 我的 Spring Boot 应用 🚀" ); out.println("========================================" ); } }); app.run(args); } }
7.4 Spring Boot Maven 配置 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 <?xml version="1.0" encoding="UTF-8" ?> <project > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 3.2.0</version > <relativePath /> </parent > <groupId > com.example</groupId > <artifactId > my-spring-boot-demo</artifactId > <version > 1.0.0</version > <properties > <java.version > 17</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-jpa</artifactId > </dependency > <dependency > <groupId > com.mysql</groupId > <artifactId > mysql-connector-j</artifactId > <scope > runtime</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-thymeleaf</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-validation</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-devtools</artifactId > <scope > runtime</scope > <optional > true</optional > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <excludes > <exclude > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </exclude > </excludes > </configuration > </plugin > </plugins > </build > </project >
八、Spring Boot 核心配置 8.1 application.yml 配置详解 Spring Boot 使用 application.yml(或 application.properties)作为配置文件:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 server: port: 8080 servlet: context-path: /api tomcat: threads: max: 200 min-spare: 10 connection-timeout: 20000 spring: application: name: my-spring-boot-demo datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=Asia/Shanghai username: root password: your_password hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: format_sql: true data: redis: host: localhost port: 6379 password: your_password lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 logging: level: root: INFO com.example: DEBUG org.hibernate.SQL: DEBUG pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" file: name: logs/app.log app: version: 1.0 .0 author: 旅人 features: email-enabled: true sms-enabled: false
8.2 多环境配置 在真实开发中,我们通常有多个环境(开发、测试、生产),每个环境的配置可能不同:
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 spring: application: name: my-spring-boot-demo
激活特定环境的方式:
1 2 3 4 spring: profiles: active: dev
1 2 java -jar myapp.jar --spring.profiles.active=prod
1 2 3 4 5 6 7 8 @SpringBootApplication @ActiveProfiles("dev") public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } }
8.3 @ConfigurationProperties 配置绑定 将配置文件中的属性绑定到 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 @Data @Component @ConfigurationProperties(prefix = "app") public class AppProperties { private String version; private String author; private Features features = new Features (); @Data public static class Features { private boolean emailEnabled; private boolean smsEnabled; } } @Configuration @EnableConfigurationProperties(AppProperties.class) public class AppConfig { } @Service public class UserService { @Autowired private AppProperties appProperties; public void sendNotification () { if (appProperties.getFeatures().isEmailEnabled()) { emailService.send(); } if (appProperties.getFeatures().isSmsEnabled()) { smsService.send(); } } }
九、Spring Data 简化数据访问 9.1 Spring Data JPA 详解 Spring Data JPA 是 Spring Data 项目的一部分,它极大简化了数据访问层的开发 。通过继承 JpaRepository,你无需编写实现类,就能获得完整的 CRUD 功能。
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 public interface UserRepository extends JpaRepository <User, Long> { } public interface UserRepository extends JpaRepository <User, Long> { List<User> findByUsername (String username) ; List<User> findByStatus (Integer status) ; User findByEmail (String email) ; List<User> findByUsernameContaining (String keyword) ; List<User> findByStatusAndUsernameContaining (Integer status, String keyword) ; Page<User> findByStatus (Integer status, Pageable pageable) ; List<User> findByStatusOrderByCreatedAtDesc (Integer status) ; long countByStatus (Integer status) ; boolean existsByEmail (String email) ; } public interface UserRepository extends JpaRepository <User, Long> { @Query("SELECT u FROM User u WHERE u.status = :status ORDER BY u.createdAt DESC") List<User> findUsersByStatus (@Param("status") Integer status) ; @Query("SELECT COUNT(u) FROM User u WHERE u.status = :status") long countByStatusQuery (@Param("status") Integer status) ; @Query(value = "SELECT * FROM users WHERE status = :status", nativeQuery = true) List<User> findUsersByStatusNative (@Param("status") Integer status) ; } public interface UserRepository extends JpaRepository <User, Long>, JpaSpecificationExecutor<User> { } @Service public class UserService { @Autowired private UserRepository userRepository; public List<User> search (UserSearchCriteria criteria) { Specification<User> spec = (root, query, cb) -> { List<Predicate> predicates = new ArrayList <>(); if (criteria.getKeyword() != null ) { predicates.add(cb.like(root.get("username" ), "%" + criteria.getKeyword() + "%" )); } if (criteria.getStatus() != null ) { predicates.add(cb.equal(root.get("status" ), criteria.getStatus())); } if (criteria.getStartDate() != null ) { predicates.add(cb.greaterThanOrEqualTo(root.get("createdAt" ), criteria.getStartDate())); } return cb.and(predicates.toArray(new Predicate [0 ])); }; return userRepository.findAll(spec); } }
9.2 Spring Data Redis 缓存操作 Spring Data Redis 提供了简洁的 API 来操作 Redis:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 @Service public class RedisService { @Autowired private StringRedisTemplate redisTemplate; public void setString (String key, String value) { redisTemplate.opsForValue().set(key, value); redisTemplate.opsForValue().set(key, value, Duration.ofHours(1 )); } public String getString (String key) { return redisTemplate.opsForValue().get(key); } public void setHash (String key, String field, String value) { redisTemplate.opsForHash().put(key, field, value); } public Object getHash (String key, String field) { return redisTemplate.opsForHash().get(key, field); } public void leftPush (String key, String value) { redisTemplate.opsForList().leftPush(key, value); } public String rightPop (String key) { return redisTemplate.opsForList().rightPop(key); } public void addToSet (String key, String... values) { redisTemplate.opsForSet().add(key, values); } public Set<String> getSetMembers (String key) { return redisTemplate.opsForSet().members(key); } @Cacheable(value = "users", key = "#id") public User getUserById (Long id) { return userRepository.findById(id).orElse(null ); } @CachePut(value = "users", key = "#result.id") public User saveUser (User user) { return userRepository.save(user); } @CacheEvict(value = "users", key = "#id") public void deleteUser (Long id) { userRepository.deleteById(id); } @CacheEvict(value = "users", allEntries = true) public void clearUserCache () { } }
十、Spring Boot 日志与监控 10.1 日志配置 Spring Boot 默认使用 Logback 作为日志框架,我们可以通过 application.yml 进行配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 logging: level: root: INFO com.example: DEBUG org.springframework.web: DEBUG org.hibernate.SQL: DEBUG org.hibernate.type.descriptor.sql.BasicBinder: TRACE pattern: console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" file: name: logs/application.log max-size: 10MB max-history: 30
10.2 Spring Boot Actuator 监控端点 Spring Boot Actuator 提供了生产级别的监控功能:
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-actuator</artifactId > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 management: endpoints: web: exposure: include: health,info,metrics,env,beans,caches,conditions endpoint: health: show-details: when_authorized health: redis: enabled: true db: enabled: true
常用 Actuator 端点:
端点
说明
示例
/actuator/health
应用健康状态
查看应用是否正常运行
/actuator/info
应用信息
查看应用基本信息
/actuator/metrics
性能指标
查看内存、CPU、请求数等
/actuator/env
环境变量
查看所有配置的环境变量
/actuator/beans
所有 Bean
查看容器中所有 Bean
/actuator/mappings
所有 URL 映射
查看所有接口路径
/actuator/conditions
配置条件
查看自动配置结果
十一、常见问题与最佳实践 11.1 常见问题与解决方案
flowchart TD
A["❓ 常见问题"] --> B["⚠️ 循环依赖"]
A --> C["⚠️ 事务不生效"]
A --> D["⚠️ 启动失败"]
A --> E["⚠️ 中文乱码"]
B --> B1["A 注入 B\nB 注入 A"]
B1 --> B2["解决:\n@Lazy 延迟加载\n或重构设计"]
C --> C1["内部方法调用\n使用代理对象"]
C1 --> C2["解决方案:\n注入自身代理对象\n或使用 AopContext"]
D --> D1["Bean 注入失败\n配置错误"]
D1 --> D2["解决方案:\n查看启动日志\n使用 --debug 启动"]
E --> E1["编码不一致"]
E1 --> E2["解决方案:\n配置 UTF-8\napplication.yml"]
style A fill:#fff3e0
问题一:循环依赖(Circular Dependency)
当两个 Bean 互相依赖时会形成循环:
A Bean 构造器需要注入 B
B Bean 构造器需要注入 A
解决方案:
使用 @Lazy 延迟加载其中一个依赖
重构代码,消除循环依赖
使用 Setter 注入代替构造器注入
1 2 3 4 5 6 7 8 9 @Component public class A { private B b; public A (@Lazy B b) { this .b = b; } }
问题二:事务不生效
常见原因是内部方法调用(this.method())不经过代理对象。
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Service public class UserService { @Autowired private UserService self; @Transactional public void outerMethod () { self.innerMethod(); } @Transactional public void innerMethod () { } }
11.2 Spring Boot 开发建议
实践
说明
推荐程度
分层清晰
Controller → Service → Repository
✅✅✅
统一响应格式
所有接口返回 Result
✅✅✅
全局异常处理
@ControllerAdvice 统一处理异常
✅✅✅
参数校验
@Valid + BindingResult
✅✅✅
事务合理使用
不要在事务中做耗时操作
✅✅✅
日志规范
使用 SLF4J,合理配置日志级别
✅✅
配置文件分离
多环境配置,敏感信息加密
✅✅
十二、总结 12.1 核心知识点回顾
mindmap
root((Spring Framework))
Spring Core
IoC 容器
DI 依赖注入
Bean 生命周期
作用域
Spring AOP
切面 Aspect
切入点 Pointcut
通知 Advice
织入 Weaving
Spring MVC
DispatchServlet
HandlerMapping
Controller
ViewResolver
Spring Boot
自动配置
起步依赖
内嵌服务器
Spring Data
JPA Repository
Redis 操作
查询方法
事务管理
@Transactional
传播行为
隔离级别
12.2 Spring 学习路线
flowchart LR
A["Spring 学习路线"] --> B["第一阶段\nSpring Core"]
B --> C["第二阶段\nSpring MVC"]
C --> D["第三阶段\nSpring Boot"]
D --> E["第四阶段\nSpring Cloud"]
B --> B1["IoC/DI"]
B --> B2["Bean 管理"]
B --> B3["AOP"]
C --> C1["请求映射"]
C --> C2["参数绑定"]
C --> C3["视图解析"]
D --> D1["自动配置"]
D --> D2["数据访问"]
D --> D3["Web 开发"]
E --> E1["微服务架构"]
E --> E2["服务治理"]
E --> E3["分布式事务"]
style A fill:#fff3e0
style B fill:#e3f2fd
style C fill:#c8e6c9
style D fill:#fff3e0
style E fill:#f8bbd0
12.3 下一步推荐学习
📖 Spring Cloud 微服务 :服务注册、配置中心、熔断器、网关
📖 Spring Security 安全 :认证授权、OAuth2、JWT
📖 Spring Batch 大数据 :批处理框架
📖 Spring Integration :消息集成
📖 Spring 源码解读 :深入理解框架设计思想
💡 写给读者的话 :Spring 是 Java 开发者的必备技能,它不仅简化了企业级开发,更提供了一种优秀的软件设计思想。从 Spring Framework 到 Spring Boot,再到 Spring Cloud,Spring 生态已经成为了 Java 后端开发的事实标准。希望本文能帮助你建立完整的 Spring 技术体系,为今后的深入学习打下坚实基础!💪
📅 本文首次发布于 2026 年 5 月 24 日