Spring & Spring Boot 高级面试题与底层原理解析
一、Spring Framework 核心原理
1. IoC容器实现原理
Q1:Spring IoC容器的启动过程是怎样的?BeanFactory和ApplicationContext有何区别?
答案:
启动过程关键阶段:
1. 资源定位:
- ResourceLoader加载配置元数据(xml/注解)
- 形成Resource对象
2. BeanDefinition解析:
- BeanDefinitionReader解析配置
- 生成BeanDefinition对象
3. 注册到容器:
- DefaultListableBeanFactory维护bdMap
- 键为beanName,值为BeanDefinition
4. 依赖注入:
- 根据依赖关系图实例化Bean
- 处理属性注入和依赖解决
BeanFactory vs ApplicationContext:
┌──────────────────┬─────────────────────────────┬─────────────────────────────┐
│ 特性 │ BeanFactory │ ApplicationContext │
├──────────────────┼─────────────────────────────┼─────────────────────────────┤
│ 实例化时机 │ 延迟初始化 │ 启动时预初始化单例 │
│ 功能扩展 │ 基础IoC功能 │ 继承多个接口(MessageSource等)│
│ AOP支持 │ 需手动配置 │ 内置支持 │
│ 事件机制 │ 无 │ 提供ApplicationEventPublisher│
│ 国际化 │ 无 │ 支持MessageSource │
│ 资源访问 │ 基本ResourceLoader │ 扩展资源模式匹配 │
└──────────────────┴─────────────────────────────┴─────────────────────────────┘
底层关键类:
- DefaultListableBeanFactory:默认容器实现
- XmlBeanDefinitionReader:XML配置读取
- AnnotatedBeanDefinitionReader:注解配置读取
- AbstractApplicationContext#refresh():容器刷新入口
2. AOP实现机制
Q2:Spring AOP的动态代理如何实现?JDK动态代理和CGLIB有什么区别?
答案:
实现流程:
1. 代理创建时机:
- Bean初始化后通过BeanPostProcessor处理
- AbstractAutoProxyCreator#postProcessAfterInitialization
2. 代理选择逻辑:
- 目标类实现接口 → JDK动态代理
- 目标类无接口 → CGLIB
- 可强制指定proxyTargetClass=true使用CGLIB
JDK动态代理实现:
1. 基于接口:
- Proxy.newProxyInstance()
- 生成$Proxy0实现类
2. 调用流程:
InvocationHandler.invoke()
→ 拦截器链
→ 目标方法
CGLIB实现:
1. 基于继承:
- Enhancer创建子类
- MethodInterceptor处理调用
2. 特点:
- final方法不能被代理
- 需要ASM库支持
性能对比:
┌──────────────────┬──────────────────────┬──────────────────────┐
│ 特性 │ JDK动态代理 │ CGLIB │
├──────────────────┼──────────────────────┼──────────────────────┤
│ 代理方式 │ 接口 │ 子类继承 │
│ 创建速度 │ 快(首次慢) │ 慢 │
│ 执行速度 │ 较慢 │ 较快 │
│ 方法拦截 │ 仅接口方法 │ 除final方法外所有方法 │
│ 依赖 │ 无第三方库 │ 需要ASM │
└──────────────────┴──────────────────────┴──────────────────────┘
Spring优化策略:
1. 缓存生成的代理类
2. 对相同配置复用代理
3. 延迟AOP代理创建
二、Spring Boot 自动配置原理
3. 自动配置机制
Q3:Spring Boot自动配置是如何实现的?条件注解的工作原理是什么?
答案:
自动配置实现机制:
1. 启动流程:
- @SpringBootApplication组合注解包含
@EnableAutoConfiguration
- AutoConfigurationImportSelector选择配置类
2. 加载过程:
- META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
- 过滤掉不满足条件的配置
3. 条件评估:
- ConditionEvaluator评估各种@Conditional
- 过滤掉不匹配的自动配置类
条件注解工作原理:
1. 核心注解:
- @ConditionalOnClass:类路径存在指定类
- @ConditionalOnMissingBean:容器不存在指定Bean
- @ConditionalOnProperty:配置属性匹配
2. 执行流程:
a. 解析条件注解
b. 获取Condition实现类
c. 调用matches()方法评估
d. 根据结果决定是否应用配置
3. 自定义条件:
实现Condition接口:
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 自定义逻辑
}
}
自动配置示例(DataSource):
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource() {
// 根据配置创建数据源
}
}
4. Starter设计原理
Q4:Spring Boot Starter的工作机制是什么?如何自定义一个Starter?
答案:
Starter核心机制:
1. 依赖管理:
- 通过pom.xml聚合相关依赖
- 定义适当的依赖版本
2. 自动配置:
- META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
- 包含配置类全限定名
3. 配置属性:
- @ConfigurationProperties绑定配置
- META-INF/additional-spring-configuration-metadata.json
自定义Starter步骤:
1. 创建工程结构:
my-spring-boot-starter
├── src/main/java
│ └── com/example/autoconfigure
│ ├── MyServiceAutoConfiguration.java
│ └── MyServiceProperties.java
└── src/main/resources
├── META-INF
│ ├── spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
│ └── additional-spring-configuration-metadata.json
2. 关键代码:
// 自动配置类
@Configuration
@EnableConfigurationProperties(MyServiceProperties.class)
@ConditionalOnClass(MyService.class)
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyServiceProperties properties) {
return new MyService(properties);
}
}
// 配置属性类
@ConfigurationProperties("my.service")
public class MyServiceProperties {
private String prefix;
private String suffix;
// getters/setters
}
3. 配置文件:
# AutoConfiguration.imports
com.example.autoconfigure.MyServiceAutoConfiguration
# additional-spring-configuration-metadata.json
{
"properties": [
{
"name": "my.service.prefix",
"type": "java.lang.String",
"defaultValue": "Hello"
}
]
}
最佳实践:
1. 命名规范:
- 官方starter:spring-boot-starter-{name}
- 自定义starter:{name}-spring-boot-starter
2. 明确作用域:
- 只包含必要的依赖
- 提供合理的默认配置
3. 版本兼容:
- 与Spring Boot主版本保持一致
三、Spring Boot 启动过程
5. 启动流程解析
Q5:SpringApplication.run()方法执行了哪些关键步骤?
答案:
启动流程关键阶段:
1. 初始化SpringApplication:
- 推断web应用类型(Servlet/Reactive/None)
- 通过SpringFactoriesLoader加载:
- ApplicationContextInitializer
- ApplicationListener
- 推断主配置类
2. 运行阶段:
a. 准备环境:
- 创建ConfigurableEnvironment
- 配置PropertySources和Profiles
b. 创建应用上下文:
- 根据web类型创建AnnotationConfig...Context
c. 准备上下文:
- 准备Bean生成器和资源加载器
- 执行ApplicationContextInitializer
d. 刷新上下文:
- 调用AbstractApplicationContext#refresh()
- 加载自动配置
e. 后置处理:
- 调用CommandLineRunner/ApplicationRunner
- 发布ApplicationReadyEvent
底层关键方法:
1. SpringApplication构造器:
- deduceWebApplicationType()
- getSpringFactoriesInstances()
2. run()方法:
- prepareEnvironment()
- createApplicationContext()
- refreshContext()
- callRunners()
3. refresh()方法:
- prepareRefresh()
- obtainFreshBeanFactory()
- invokeBeanFactoryPostProcessors()
- registerBeanPostProcessors()
- finishBeanFactoryInitialization()
启动优化点:
1. 延迟初始化:
spring.main.lazy-initialization=true
2. 排除自动配置:
@EnableAutoConfiguration(exclude={...})
3. 组件扫描过滤:
@ComponentScan(excludeFilters={...})
6. 嵌入式容器原理
Q6:Spring Boot嵌入式Tomcat是如何集成和启动的?
答案:
集成实现原理:
1. 自动配置触发:
- ServletWebServerFactoryAutoConfiguration
- @ConditionalOnClass(Servlet.class)
- 导入EmbeddedTomcat等配置类
2. 容器创建:
- TomcatServletWebServerFactory创建实例
- 初始化Connector、Engine等组件
- 配置线程池(默认为200线程)
3. 启动流程:
a. 创建WebServer
b. 初始化ServletContext
c. 注册DispatcherServlet
d. 启动Tomcat线程
关键代码路径:
1. 自动配置类:
- ServletWebServerFactoryConfiguration.EmbeddedTomcat
- ServletWebServerFactoryAutoConfiguration
2. 核心接口:
- ServletWebServerFactory:创建WebServer
- WebServer:控制服务器生命周期
3. 定制扩展点:
- WebServerFactoryCustomizer:修改服务器配置
- ServletContextInitializer:添加Servlet/Filter
配置参数示例:
server.tomcat.max-threads=200
server.tomcat.accept-count=100
server.tomcat.connection-timeout=5s
底层工作原理:
1. 线程模型:
- Acceptor线程接收连接
- Poller线程检测IO事件
- Worker线程池处理请求
2. 与Spring MVC集成:
- 通过DispatcherServletRegistrationBean注册
- 路径映射由HandlerMapping处理
3. 生命周期管理:
- 随ApplicationContext启动/停止
- 优雅停机支持(server.shutdown=graceful)
四、Spring 响应式编程
7. WebFlux核心原理
Q7:Spring WebFlux的反应式编程模型与Servlet模型有什么本质区别?
答案:
架构对比:
┌──────────────────┬──────────────────────────────┬──────────────────────────────┐
│ 特性 │ Servlet模型 │ WebFlux模型 │
├──────────────────┼──────────────────────────────┼──────────────────────────────┤
│ 编程范式 │ 命令式/阻塞式 │ 声明式/非阻塞 │
│ 并发模型 │ 每个请求占用一个线程 │ 少量线程处理所有请求 │
│ IO处理 │ 阻塞IO │ 异步非阻塞IO │
│ 容器支持 │ Tomcat/Jetty等Servlet容器 │ Netty/Undertow等NIO容器 │
│ 吞吐量 │ 线程数受限 │ 更高并发能力 │
│ 延迟 │ 受线程池大小影响 │ 更稳定的延迟表现 │
│ 错误处理 │ try-catch │ 操作符方式 │
└──────────────────┴──────────────────────────────┴──────────────────────────────┘
核心组件实现:
1. 事件循环:
- Reactor Netty使用EventLoopGroup
- 少量IO线程处理所有连接
2. 背压支持:
- 通过Reactive Streams规范实现
- Subscription控制数据流速
3. 请求处理链:
WebHandler → DispatcherHandler → HandlerAdapter → HandlerResultHandler
性能对比场景:
1. WebFlux优势场景:
- 高并发(>5K QPS)
- 长轮询/SSE等场景
- IO密集型服务
2. Servlet优势场景:
- 计算密集型任务
- 需要阻塞操作(如JDBC)
- 已有Servlet组件集成
编程模型差异:
// Servlet (阻塞式)
@GetMapping("/user/{id}")
public User getUser(@PathVariable String id) {
return repository.findById(id); // 阻塞
}
// WebFlux (非阻塞)
@GetMapping("/user/{id}")
public Mono<User> getUser(@PathVariable String id) {
return repository.findById(id); // 非阻塞
}
8. Reactor核心机制
Q8:Project Reactor中的Flux和Mono有什么区别?背压(Backpressure)是如何实现的?
答案:
Flux vs Mono:
┌──────────────────┬──────────────────────────────┬──────────────────────────────┐
│ 特性 │ Flux │ Mono │
├──────────────────┼──────────────────────────────┼──────────────────────────────┤
│ 数据流 │ 0..N个元素 │ 0..1个元素 │
│ 典型场景 │ 集合/流处理 │ 单个结果/空值 │
│ 操作符 │ 丰富的流操作 │ 侧重单值转换 │
│ 订阅行为 │ 可能多次触发onNext │ 最多触发一次onNext │
│ 完成信号 │ onComplete │ onSuccess/onComplete │
└──────────────────┴──────────────────────────────┴──────────────────────────────┘
背压实现机制:
1. 订阅协商:
- Subscriber通过Subscription.request(n)请求数据量
- Publisher根据需求发送数据
2. 处理策略:
- BUFFER:缓冲超出的数据(默认)
- DROP:丢弃无法处理的数据
- LATEST:只保留最新数据
- ERROR:抛出错误
3. 核心接口:
public interface Subscriber<T> {
void onSubscribe(Subscription s);
void onNext(T t);
void onError(Throwable t);
void onComplete();
}
背压示例:
Flux.range(1, 100)
.onBackpressureBuffer(10) // 缓冲10个
.subscribe(new BaseSubscriber<Integer>() {
@Override
protected void hookOnSubscribe(Subscription subscription) {
request(5); // 初始请求5个
}
@Override
protected void hookOnNext(Integer value) {
// 处理元素
if(needMore) {
request(1); // 再请求1个
}
}
});
调度器(Scheduler):
1. Schedulers.immediate():当前线程
2. Schedulers.single():单线程复用
3. Schedulers.parallel():并行线程池
4. Schedulers.elastic():弹性线程池(已弃用)
5. Schedulers.boundedElastic():有界弹性池
调试技巧:
1. 启用调试模式:
Hooks.onOperatorDebug()
2. 日志记录:
.log()
3. 检查装配:
.checkpoint("description")