Spring
Spring
Spring 面试八股文
1. 为什么要使用 Spring?
- Spring 是轻量级的框架,提供了 IOC(控制反转) & AOP(面向切面编程) 两大核心功能:
- IOC 容器:管理 Bean 的生命周期,自动注入依赖,降低耦合。
- AOP 机制:解耦业务逻辑 & 系统服务(如事务管理、日志、权限校验等)。
2. Spring 主要模块
| 模块 | 功能 |
|---|---|
| Spring Core | 提供 IoC 控制反转 & 依赖注入 |
| Spring AOP | 提供面向切面编程,解耦业务逻辑 |
| Spring WebMVC | 提供 MVC 结构,支持 SpringMVC |
| Spring ORM | 支持 JPA、Hibernate、MyBatis 等 ORM 框架 |
| Spring Test | 提供测试支持,方便单元测试 |
| Spring WebSocket | 支持 WebSocket 连接 |
3. Spring IoC(控制反转)
什么是 IoC?
控制反转(Inversion of Control,IoC):对象的创建不由程序控制,而是 交给 Spring 容器管理。
DI(Dependency Injection,依赖注入):Spring 自动注入对象的依赖关系,降低耦合性。
IoC 的优点:
降低代码耦合度,符合依赖倒置原则(DIP)。
统一资源管理,Spring 维护对象实例,开发者无需手动创建对象。
DI 的几种实现方式:
构造函数注入:
@Component
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}- Setter 注入:
@Component
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}- 注解方式(
@Autowired、@Resource、@Qualifier):
@Autowired // 默认按类型注入
@Resource // 默认按名称注入,找不到再按类型
@Qualifier("userRepository") // 指定具体 Bean- 为什么官方推荐构造器注入?
@Autowired可能会导致空指针异常(NPE)。- 推荐使用 Lombok 的
@RequiredArgsConstructor自动生成构造方法,简化代码。
4. Spring Bean & 作用域
Spring Bean 是什么?
Spring 管理的 Java 对象,生命周期由 Spring 控制,与普通 JavaBean 不同。
Bean 作用域(
@Scope) | 作用域 | 描述 | |---|---| | singleton | 默认,单例模式,整个应用共享一个 Bean | | prototype | 每次获取 Bean 时,都会创建一个新实例 | | request | HTTP 请求级别的 Bean,每次请求创建新实例 | | session | HTTP 会话级别的 Bean,每个 session 共享 | | global-session | 跨应用 session 共享(一般用于 Portlet) | | websocket | WebSocket 连接级别的 Bean |Bean 生命周期
实例化** → 属性赋值 → 初始化 → **销毁
- 扩展接口:
BeanPostProcessor:初始化前后增强 Bean。InstantiationAwareBeanPostProcessor:实例化前后增强。Aware接口:获取 IoC 资源,如ApplicationContext。- Bean 线程安全问题
单例 Bean 可能存在线程安全问题,特别是有 可变状态 时(如
List)。解决方案:
使用
prototype作用域(但会频繁创建实例)。使用
ThreadLocal维护线程级变量。使用线程安全集合,如
ConcurrentHashMap。
5. Spring AOP(面向切面编程)
AOP 作用:
不修改原代码的情况下增强功能,如 日志、事务管理、权限校验。
Spring AOP 基于动态代理:
JDK 代理(默认,要求类实现接口)。
CGLIB 代理(无接口时,基于字节码生成子类)。
AOP 术语 | 术语 | 作用 | |---|---| | 切点(PointCut) | 需要增强的方法(通过表达式定义) | | 通知(Advice) | 具体增强逻辑 | | 切面(Aspect) | 切点 + 通知 的结合 | | 织入(Weaving) | 将切面与目标对象结合的过程 |
通知类型 | 通知 | 执行时机 | |---|---| |
@Before| 方法执行前 | |@After| 方法执行后 | |@AfterReturning| 方法返回后 | |@AfterThrowing| 方法抛出异常后 | |@Around| 环绕通知,可控制方法执行 |AOP 失效场景
类内部方法调用(
this调用):AOP 依赖动态代理,而this调用不会经过代理对象。private/final方法:不能被代理。
6. Spring 事务管理(@Transactional)
Spring 事务的作用
保证多条 SQL 操作要么全部成功,要么全部失败,防止数据不一致。
事务传播机制 | 传播行为 | 作用 | |---|---| |
REQUIRED| 默认值,如果有事务就加入,没有就新建 | |REQUIRES_NEW| 新建事务,不管是否已有事务 | |NESTED| 嵌套事务,外部回滚,内部回滚 | |SUPPORTS| 支持事务,没有事务就普通执行 | |MANDATORY| 必须在已有事务下运行,否则报错 | |NOT_SUPPORTED| 不支持事务,挂起当前事务 | |NEVER| 不支持事务,已有事务时报错 |事务失效场景
方法不是
public(@Transactional只能作用在public方法上)。方法被
this调用(不会经过代理)。异常被
catch捕获(事务不会回滚)。数据库引擎不支持事务(如
MyISAM)。
7. Spring 循环依赖
- Spring 如何解决循环依赖?
- 三级缓存机制:
- 一级缓存:存完整 Bean(单例池)。
- 二级缓存:存未初始化的 Bean。
- 三级缓存:存 Bean 工厂(解决 AOP 代理问题)。
为什么不用两级缓存?
如果 Bean 需要 AOP 代理,初始化后才能生成代理对象,而 属性注入发生在初始化前,所以用 三级缓存存代理对象的工厂。
无法解决的情况
构造器注入的循环依赖(因为构造器在属性注入前执行)。
8. Spring Boot 自动装配(@EnableAutoConfiguration)
Spring Boot 启动流程
SpringApplication.run() 生成
SpringApplication实例,创建监听器。加载
META-INF/spring.factories自动配置类(@EnableAutoConfiguration)。过滤不满足
@ConditionalOnXxx条件的配置。启动 IoC 容器,完成 Bean 依赖注入。
自定义 Starter
创建
META-INF/spring.factories使用
@ConditionalOnProperty等注解控制自动装配编写自动配置类
@Configuration
总结
- Spring 核心:IoC(依赖注入)+ AOP(切面编程)。
- Bean 作用域、生命周期、线程安全问题。
- Spring 事务管理(
@Transactional)及事务传播机制。 - Spring AOP 代理模式,及拦截失效原因。
- Spring Boot 自动装配原理。