作为 Java 开发者,你一定绕不开 Spring------ 它不是简单的工具类,而是统治 Java 企业级开发近 20 年的 "开发圣经"。
很多人学 Spring,只停留在 "会用 @Autowired、@Service" 的层面,面试被问 "Spring IoC 原理""循环依赖怎么解决""AOP 底层实现",瞬间卡壳;开发时遇到 Bean 注入失败、事务不生效,只能瞎猜乱试。
这篇文章,不跳步、不玄学、不堆砌概念,用 "道法术器" 四层逻辑,从 "为什么要有 Spring" 到 "Spring 生态怎么用",把 Spring 彻底讲透。全程专业准确,新手能入门,老手能查漏补缺,建议立刻收藏,面试、开发、复盘都能直接翻。
第一步:问题的原点 ------ 为什么要有 Spring?(先搞懂 "为什么",再学 "怎么用")
想象你是 Java 开发者,要开发一个电商系统,用户下单时需要扣库存、生成订单、发送短信。按传统方式写代码,会陷入 5 个致命坑:
java
运行
java
public class OrderService {
// 直接new依赖,耦合死死的
private InventoryService inventoryService = new InventoryService();
private SmsService smsService = new SmsService();
public void createOrder(Order order) {
inventoryService.deductStock(order); // 扣库存
// 保存订单到数据库...
smsService.sendNotification(order); // 发短信
}
}
传统开发的 5 大痛点(戳中所有 Java 开发者的泪点)
- 耦合太紧:OrderService 直接依赖 InventoryService、SmsService 的具体实现,想把阿里云短信换成腾讯云,必须修改 OrderService 代码,牵一发而动全身;
- 难以测试:测试 OrderService 时,必须创建真实的 InventoryService(会真扣库存)、SmsService(会真发短信),单元测试变成集成测试,慢且不稳定;
- 对象生命周期混乱:谁创建对象?什么时候创建?OrderService 该是单例还是多例?这些细节散落在代码各处,后期维护一团糟;
- 代码臃肿:每个方法都要写日志、事务管理、权限检查,这些和业务无关的 "横切代码",把核心业务逻辑埋得严严实实;
- 配置分散:数据库连接、线程池大小、缓存策略全硬编码在类里,改一个配置要改多处,部署上线全是坑。
核心结论
Spring 存在的唯一理由:让开发者只专注核心业务逻辑,把 "对象创建、依赖管理、横切关注点处理" 这些基础设施问题,全部交给框架来做。
第二步:道的层面 ------Spring 的根本思想(懂道,才懂 Spring 的设计精髓)
"道" 是 Spring 的灵魂,回答 "为什么这么设计",搞懂这 4 个思想,就抓住了 Spring 的本质。
道 1:控制反转(IoC)------ 把控制权交出去(核心中的核心)
传统编程:对象自己 "掌控一切"------ 自己创建依赖、自己管理生命周期,比如 "我要用到 A,就自己 new A ()"。
java
运行
less
A a = new A(); // 我自己创建,自己控制
a.doSomething();
这叫 "正转"------ 控制权在你手里。
控制反转(IoC):把对象的创建、依赖管理权力,从对象本身转移到 Spring 容器。你只管声明 "我需要什么",容器负责 "给你什么",不用自己动手 new。
java
运行
kotlin
@Service // 告诉Spring:我是一个Bean,交给你管理
public class OrderService {
@Autowired // 告诉Spring:我需要一个InventoryService,你给我注入
private InventoryService inventoryService;
}
✅ 为什么叫 "反转"?因为控制权从 "开发者 / 对象",转移到了 "Spring 容器"。
道 2:依赖注入(DI)------IoC 的具体实现(落地层面)
控制反转是 "思想",依赖注入是 "落地方式":Spring 容器在创建对象时,自动把依赖的对象 "注入" 到当前对象中,不用你手动赋值。
Spring 支持 3 种注入方式(重点记推荐用法):
- 构造器注入(Spring 推荐):通过构造函数传入依赖,依赖不可变(final)、不能为 null,便于单元测试;
- Setter 注入:通过 setter 方法设置依赖,适合可选依赖;
- 字段注入(常用但不推荐):直接给字段赋值,代码简洁,但可读性差、难以测试。
道 3:面向切面编程(AOP)------ 分离横切关注点(解耦关键)
日志、事务、安全、缓存这些 "横切关注点",横切于多个业务模块,若散落在各处,会导致代码重复、维护困难。
AOP 的核心思想:把横切关注点封装成 "切面",在运行时 "织入" 到业务代码中,不修改业务代码本身,实现 "业务代码和非业务代码分离"。
java
运行
typescript
@Transactional // 声明式事务,不用写一行事务管理代码
public void transferMoney(Account from, Account to, BigDecimal amount) {
// 只写核心业务逻辑,事务由Spring自动管理
from.deduct(amount);
to.add(amount);
}
✅ 好处:业务代码更纯粹,横切逻辑可复用、可维护。
道 4:非侵入式设计(Spring 的 "温柔" 之处)
Spring 的核心理念:你的业务类就是普通的 Java 对象(POJO),不需要继承 Spring 的特定类、实现 Spring 的特定接口。
java
运行
arduino
// 普通Java类,没有任何Spring相关代码
public class OrderService {
private InventoryService inventoryService;
// 普通构造器
public OrderService(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
// 普通业务方法
public void createOrder(Order order) {
inventoryService.deductStock(order);
}
}
这个类可以在 Spring 中使用,也可以单独测试、单独使用,完全不受框架约束 ------ 这就是 Spring 的 "非侵入式",不绑架你的代码。
第三步:法的层面 ------Spring 的核心方法论(懂法,才会用对 Spring)
"法" 是实现 "道" 的路径和方法论,回答 "怎么做",这 4 个方法论,是 Spring 的核心骨架。
法 1:Bean 的生命周期管理(Spring 怎么管理对象)
Spring 容器会全程管理 Bean 的生命周期,从创建到销毁,一步不落地:
plaintext
java
实例化(new对象)→ 属性赋值(注入依赖)→ 初始化(@PostConstruct)→ 使用 → 销毁(@PreDestroy)
关键亮点:Spring 提供多个扩展点,让你可以在生命周期的不同阶段插入自定义逻辑:
- BeanPostProcessor:在 Bean 初始化前后做自定义处理(比如修改 Bean 属性);
- @PostConstruct:Bean 初始化完成后执行;
- @PreDestroy:Bean 销毁前执行(比如释放资源)。
法 2:依赖解析与注入机制(Spring 怎么找到要注入的 Bean)
Spring 不是 "瞎注入",而是有一套明确的依赖解析规则:
- 类型匹配:默认按 "字段类型" 或 "构造器参数类型",查找容器中对应的 Bean;
- 名称匹配:通过 @Qualifier 或 @Resource (name=...),指定要注入的 Bean 名称;
- 自动装配:@Autowired 默认按类型装配,找到多个匹配 Bean 时,会按 Bean 名称匹配。
重点:三级缓存解决循环依赖(面试高频)
当 A 依赖 B、B 依赖 A 时(循环依赖),Spring 用 "三级缓存" 完美解决,不用手动处理:
- 一级缓存(singletonObjects):存放 "成品 Bean"(已实例化、已初始化、可直接使用);
- 二级缓存(earlySingletonObjects):存放 "半成品 Bean"(已实例化、未初始化,提前暴露的引用);
- 三级缓存(singletonFactories):存放 "Bean 工厂",用于提前暴露 Bean 的引用。
循环依赖解决流程(极简版):
- 创建 A,将 A 的工厂放入三级缓存;
- A 需要 B,Spring 去创建 B;
- B 需要 A,从三级缓存拿到 A 的工厂,获取 A 的半成品引用(放入二级缓存);
- B 创建完成(成品),放入一级缓存;
- A 拿到 B 的引用,完成初始化,成为成品,放入一级缓存。
法 3:代理机制与 AOP 实现(AOP 的底层原理)
Spring AOP 的底层是 "动态代理",根据目标对象是否实现接口,选择不同的代理方式:
- JDK 动态代理:如果目标对象实现了接口,用 JDK 的 Proxy 类生成代理对象(代理接口);
- CGLIB 代理:如果目标对象没有实现接口,用 CGLIB 生成目标类的子类(代理类)。
核心逻辑(伪代码,一看就懂):
java
运行
scss
public class AopProxyFactory {
public Object createProxy(Object target, Advice advice) {
// 目标对象有接口 → JDK动态代理
if (target.getClass().getInterfaces().length > 0) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
advice.before(); // 切面逻辑(比如日志)
Object result = method.invoke(target, args); // 业务逻辑
advice.after(); // 切面逻辑(比如事务提交)
return result;
}
);
} else {
// 目标对象无接口 → CGLIB代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
advice.before();
Object result = proxy.invokeSuper(obj, args);
advice.after();
return result;
});
return enhancer.create();
}
}
}
法 4:声明式编程范式(Spring 的高效用法)
Spring 鼓励 "声明式编程",通过注解或 XML 声明配置,代替硬编码,让代码更简洁、可维护。
java
运行
kotlin
// 声明配置类
@Configuration // 告诉Spring:这是一个配置类
@ComponentScan("com.example") // 声明扫描哪些包的Bean
public class AppConfig {
@Bean // 声明一个Bean,交给Spring管理
public DataSource dataSource() {
return new HikariDataSource();
}
}
// 声明Service Bean + 事务
@Service // 声明这是一个Service层Bean
@Transactional // 声明该类所有方法都需要事务管理
public class UserService {
// 只写业务逻辑,配置由Spring管理
}
✅ 声明式编程的好处:关注点分离(业务归业务,配置归配置)、可维护性强、修改配置不用改业务代码。
第四步:术的层面 ------Spring 的具体技术实现(懂术,才能解决实际问题)
"术" 是具体的技术技巧,回答 "用什么工具实现",掌握这些,遇到 Spring 相关问题能快速定位、解决。
术 1:ApplicationContext 的 12 步启动流程(Spring 容器怎么启动)
Spring 容器(ApplicationContext)的启动是一个精密的流程,掌握它,能轻松排查 "容器启动失败" 问题:
- 准备上下文:设置环境变量、加载配置文件;
- 读取配置:加载 @Configuration 类或 XML 配置;
- 解析 Bean 定义:将配置解析为 BeanDefinition(Bean 的 "说明书");
- 注册 Bean 后置处理器:准备 BeanFactoryPostProcessor(用于修改 Bean 定义);
- 实例化 Bean:创建非懒加载的单例 Bean 实例;
- 属性填充:为 Bean 注入依赖;
- 初始化:调用 @PostConstruct 或 InitializingBean 的初始化方法;
- 注册销毁回调:为需要销毁的 Bean 注册销毁逻辑;
- 发布事件:通知所有监听器 "容器已刷新";
- 完成刷新:容器启动完成,可正常使用;
- 处理懒加载 Bean:用到时再创建;
- 销毁:容器关闭时,执行 Bean 的销毁方法,释放资源。
术 2:各种注入方式的底层实现(@Autowired 到底怎么用)
1. 构造器注入(推荐)
java
运行
arduino
// 构造器注入
public OrderService(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
底层原理:Spring 通过反射找到该构造器,从容器中获取 InventoryService 实例,传入构造器创建 OrderService。
2. 字段注入(常用)
java
运行
java
@Autowired
private InventoryService inventoryService;
底层原理:Spring 通过反射获取该私有字段,设置 setAccessible (true)(突破私有访问限制),直接将容器中的 Bean 赋值给字段。
术 3:AOP 的切面表达式(精准匹配需要增强的方法)
Spring AOP 使用 AspectJ 的切点表达式,常用 3 种表达式,直接复制可用:
java
运行
java
@Aspect
@Component
public class LoggingAspect {
// 1. execution表达式:匹配指定包下的所有方法(最常用)
@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
System.out.println("方法执行前,打印日志");
}
// 2. @annotation表达式:匹配有特定注解的方法
@Around("@annotation(com.example.annotation.Loggable)")
public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed(); // 执行原方法
long duration = System.currentTimeMillis() - start;
System.out.println("方法执行耗时:" + duration + "ms");
return result;
}
// 3. within表达式:匹配指定包及子包下的所有方法
@AfterReturning("within(com.example.controller..*)")
public void logAfterReturning() {
System.out.println("方法正常返回,打印日志");
}
}
术 4:事务管理机制(@Transactional 为什么能生效)
Spring 事务的核心是 @Transactional 注解和 TransactionInterceptor(事务拦截器),底层流程如下:
- TransactionInterceptor 拦截被 @Transactional 标注的方法;
- 根据 @Transactional 的属性(传播行为、隔离级别等),从 PlatformTransactionManager 获取事务;
- 执行业务方法;
- 若方法正常执行,提交事务;若抛出异常(符合 rollbackFor 配置),回滚事务;
- 清理事务资源。
常用 @Transactional 属性(开发必记):
java
运行
ini
@Transactional(
propagation = Propagation.REQUIRED, // 事务传播行为(默认,有事务则加入,无则新建)
isolation = Isolation.READ_COMMITTED, // 隔离级别(默认,避免脏读)
timeout = 30, // 超时时间(30秒,超时回滚)
rollbackFor = RuntimeException.class // 哪些异常触发回滚(默认只回滚运行时异常)
)
第五步:器的层面 ------Spring 生态的工具箱(懂器,才能高效开发)
"器" 是 Spring 的具体工具和组件,回答 "有哪些东西可以用",Spring 生态极其完善,开箱即用,覆盖开发全场景。
器 1:Spring Framework 核心模块(基础中的基础)
表格
| 模块 | 核心功能 | 核心类 / 接口 |
|---|---|---|
| spring-core | IoC 容器基础,提供核心工具类 | BeanFactory, ApplicationContext |
| spring-beans | Bean 的创建、管理、依赖注入 | BeanDefinition, BeanWrapper |
| spring-aop | 面向切面编程,提供 AOP 实现 | ProxyFactory, AspectJProxyFactory |
| spring-context | 容器上下文、事件机制、资源管理 | ApplicationEvent, ResourceLoader |
| spring-tx | 事务管理,支持声明式 / 编程式事务 | PlatformTransactionManager, @Transactional |
| spring-web | Web 开发基础,整合 Servlet | DispatcherServlet, HandlerMapping |
| spring-webmvc | Spring MVC 实现,用于 Web 开发 | @Controller, @RequestMapping |
| spring-jdbc | JDBC 抽象,简化数据库操作 | JdbcTemplate, NamedParameterJdbcTemplate |
器 2:Spring Boot(简化 Spring 部署,一键启动)
Spring Boot 的核心是 "自动配置",解决了 Spring "配置繁琐" 的痛点,只需一个注解,就能快速启动 Spring 应用:
java
运行
typescript
@SpringBootApplication // 组合注解:@Configuration + @EnableAutoConfiguration + @ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args); // 一键启动
}
}
自动配置原理:
- @EnableAutoConfiguration 导入 AutoConfigurationImportSelector;
- 扫描 META-INF/spring.factories 文件,加载所有 AutoConfiguration 类;
- 根据条件注解(@ConditionalOnClass、@ConditionalOnBean 等),判断配置是否生效。
器 3:Spring Cloud(微服务开发套件)
Spring Cloud 基于 Spring Boot,提供微服务开发的全套组件,解决微服务中的服务发现、网关、链路追踪等问题:
表格
| 组件 | 核心功能 |
|---|---|
| Spring Cloud Config | 分布式配置中心,统一管理配置 |
| Spring Cloud Netflix | 服务发现(Eureka)、断路器(Hystrix) |
| Spring Cloud Gateway | API 网关,统一入口、路由转发 |
| Spring Cloud Sleuth | 链路追踪,排查微服务调用问题 |
| Spring Cloud Stream | 消息驱动,简化消息队列使用 |
器 4:Spring Data(简化数据访问)
Spring Data 统一了各类数据访问框架的 API,只需定义接口,Spring 自动实现 CRUD 方法,不用写 SQL:
java
运行
java
// 继承JpaRepository,自动获得CRUD、分页、排序等方法
public interface UserRepository extends JpaRepository<User, Long> {
// 方法名解析为SQL:SELECT * FROM user WHERE last_name = ?
List<User> findByLastName(String lastName);
// 自定义JPQL查询
@Query("SELECT u FROM User u WHERE u.email = ?1")
User findByEmail(String email);
}
器 5:Spring Security(安全框架)
Spring Security 提供身份认证、授权、防攻击等功能,快速实现系统安全:
java
运行
scala
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll() // 公开接口,无需登录
.antMatchers("/admin/**").hasRole("ADMIN") // 管理员才能访问
.anyRequest().authenticated() // 其他接口需登录
.and()
.formLogin()
.loginPage("/login") // 自定义登录页
.permitAll();
}
}
第六步:用简单比喻理解 Spring(道法术器全打通)
很多人觉得 Spring 复杂,其实用 "一家大型餐厅" 的比喻,就能轻松理解道法术器四层逻辑:
道的层面:餐厅的经营理念
- 控制反转(IoC):你不自己买菜、做饭,坐在餐桌前点菜,厨师(Spring 容器)给你做好端上来;
- 依赖注入(DI):你只需说 "我要一杯水",服务员(Spring)就把水给你倒好(注入),不用自己去后厨找;
- 面向切面(AOP):服务员会在你用餐前铺餐巾、用餐后结账,这些服务(横切关注点)不用你操心;
- 非侵入式:你坐在餐厅里还是你自己,不用穿餐厅制服、不用考厨师证(不用继承 Spring 类)。
法的层面:餐厅的运营流程
- Bean 生命周期:客人进店(实例化)→ 点菜(属性赋值)→ 上菜(初始化)→ 用餐(使用)→ 离店(销毁);
- 依赖解析:服务员根据你点的菜,知道该去哪个窗口取(类型匹配);
- 动态代理:传菜员(代理)把菜从后厨端到桌前,你只管吃(不用关心菜怎么来的)。
术的层面:餐厅的操作技巧
- ApplicationContext 启动:餐厅开业准备 ------ 打扫卫生(准备环境)、摆桌椅(创建 Bean)、检查食材(验证配置)、开门迎客(发布事件);
- 依赖注入方式:构造器注入(进门就给菜单)、Setter 注入(坐下再递菜单)、字段注入(直接上你爱吃的菜);
- AOP 表达式:告诉服务员 "哪些桌需要提前铺餐巾"(切点匹配)。
器的层面:餐厅的工具设备
- Spring Framework:餐厅的基础设施(桌椅、厨房、餐具);
- Spring Boot:连锁餐厅的标准化开店流程,一键开业;
- Spring Cloud:餐饮集团,管理多家分店(微服务);
- Spring Data:食材管理系统,标准化采购(数据访问);
- Spring Security:门禁系统,区分普通客人和 VIP(权限控制)。
第七步:Spring 的本质是什么?(第一性原理总结)
用第一性原理来看,Spring 的本质是:一个通过 IoC 容器和 AOP 机制,实现 Java 应用松耦合、可测试、易维护的企业级应用开发框架。
它的核心哲学,就是 "道法术器" 四层体系:
- 道:指引方向(为什么设计)------ 解决耦合、测试、横切关注点问题;
- 法:提供路径(怎么设计)------ 通过生命周期、依赖解析、代理机制实现;
- 术:给出技巧(怎么实现)------ 启动流程、注入方式、事务拦截等具体技术;
- 器:提供工具(用什么实现)------Spring Boot、Cloud、Data 等生态组件。
这就是 Spring 能统治 Java 近 20 年的根本原因:它不仅是一个框架,更是一套完整的思想体系和开发方法论。
总结:Spring 道法术器速查表(收藏备用)
表格
| 层次 | 核心内容 | 一句话总结 |
|---|---|---|
| 道 | IoC、DI、AOP、非侵入式 | 把控制权交出去,只专注核心业务 |
| 法 | 生命周期、依赖解析、代理机制、声明式编程 | 规范对象管理和依赖注入的流程 |
| 术 | 启动流程、注入技巧、AOP 表达式、事务拦截 | 解决 Spring 开发中的具体技术问题 |
| 器 | Spring Boot、Cloud、Data、Security | 开箱即用的生态工具,提升开发效率 |
🔥 互动话题
学 Spring 时,你最头疼的问题是什么?
- 循环依赖搞不懂
- AOP 底层代理机制绕不明白
- @Transactional 事务不生效
- Spring Boot 自动配置踩坑
- 面试被问 Spring 原理答不上来
评论区留下你的痛点,我会挑高频问题,下期专门出《Spring 常见坑排查实战》,手把手教你解决,还会附赠 Spring 面试高频题合集!