SpringBoot2核心功能面试题库
生成时间:2026-05-21 23:42
【面试题】1. @SpringBootApplication注解由哪几个注解组合而成?分别起什么作用?
【标准答案】 @SpringBootApplication 是一个组合注解,由三个核心注解构成:
- @SpringBootConfiguration - 本质就是@Configuration,标注这是一个配置类,允许在类中定义@Bean方法
- @EnableAutoConfiguration - 开启自动配置的核心,通过@Import导入AutoConfigurationImportSelector,扫描META-INF/spring.factories或AutoConfiguration.imports文件中定义的自动配置类
- @ComponentScan - 开启组件扫描,默认扫描当前包及其子包下所有@Component、@Service、@Repository、@Controller等标注的Bean
实际开发中,这三个注解协同工作:@ComponentScan扫描用户自定义的组件,@EnableAutoConfiguration根据类路径依赖自动配置第三方组件,@SpringBootConfiguration把这些Bean注册到容器。这就是为什么主启动类要放在根包下——让@ComponentScan能扫描到所有子包。
【面试题】2. @ConfigurationProperties和@Value有什么区别?什么场景用哪个?
【标准答案】 两者核心区别:
| 对比维度 | @ConfigurationProperties | @Value |
|---|---|---|
| 绑定方式 | 批量绑定前缀匹配的所有属性 | 单个属性注入 |
| 松散绑定 | 支持(user-name能匹配userName) | 不支持(必须精确匹配) |
| SpEL表达式 | 不支持 | 支持(#{...}) |
| 复杂对象 | 支持嵌套对象、List、Map | 不支持 |
| 校验 | 配合@Validated支持JSR303校验 | 不支持 |
| 刷新 | 配合@ConfigurationPropertiesScan支持动态刷新 | 启动时固定 |
使用场景:
- 配置项多、需要批量绑定时 → @ConfigurationProperties(推荐定义配置类)
- 单个配置值、需要SpEL表达式时 → @Value
- 需要参数校验(如@NotNull、@Min) → @ConfigurationProperties + @Validated
- 需要动态刷新配置(配合Nacos/Apollo) → @ConfigurationProperties
实战中,大部分配置类用@ConfigurationProperties,只有在读取特定单个值且需要SpEL计算时才用@Value。
【面试题】3. BeanPostProcessor在Spring生命周期中的作用是什么?可以用来做什么?
【标准答案】 BeanPostProcessor是Spring预留的Bean后置处理器,在Bean初始化前后提供扩展点:
执行时机:
实例化 → 属性注入 → BeanPostProcessor.postProcessBeforeInitialization()
→ @PostConstruct/InitializingBean → BeanPostProcessor.postProcessAfterInitialization()两个方法:
postProcessBeforeInitialization()- 初始化前执行,在@PostConstruct和InitializingBean.afterPropertiesSet()之前postProcessAfterInitialization()- 初始化后执行,可以包装Bean返回代理对象
典型应用场景:
- AOP代理创建 - AbstractAutoProxyCreator在postProcessAfterInitialization中为Bean创建代理对象
- 注解解析 - CommonAnnotationBeanPostProcessor处理@PostConstruct、@PreDestroy、@Resource
- 属性校验 - BeanValidationPostProcessor校验@Validated标注的Bean
- 自定义逻辑 - 比如对所有特定注解的Bean进行增强、记录日志、添加监控
注意: BeanPostProcessor本身也是Bean,但它执行时其他普通Bean还没初始化完成,所以不能在BeanPostProcessor中依赖其他业务Bean,否则会导致提前初始化问题。
【面试题】4. @Import有哪三种导入方式?各自的应用场景是什么?
【标准答案】 @Import用于向容器中导入组件,有三种方式:
1. 直接导入普通类
@Import(UserService.class)- 最简单方式,直接把UserService注册为Bean
- Bean名称为全类名
2. 导入ImportSelector实现类
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
return new String[]{"com.example.ServiceA", "com.example.ServiceB"};
}
}
@Import(MyImportSelector.class)- 返回需要导入的全类名数组
- 可以根据注解元数据动态决定导入哪些类
- 典型应用: @EnableAutoConfiguration就是通过ImportSelector(AutoConfigurationImportSelector)批量导入自动配置类
3. 导入ImportBeanDefinitionRegistrar实现类
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
RootBeanDefinition bd = new RootBeanDefinition(ServiceC.class);
registry.registerBeanDefinition("serviceC", bd);
}
}
@Import(MyRegistrar.class)- 直接操作BeanDefinitionRegistry,灵活度最高
- 可以自定义Bean名称、作用域、懒加载等属性
- 典型应用: MyBatis的@MapperScan通过Registrar批量注册Mapper接口
选择建议: 简单导入用方式1,批量动态导入用方式2,需要完全控制Bean定义用方式3。
【面试题】5. AOP中五种通知类型的执行顺序是怎样的?Around如何使用?
【标准答案】 五种通知执行顺序(正常情况):
@Around(前) → @Before → 目标方法 → @AfterReturning → @After → @Around(后)异常情况执行顺序:
@Around(前) → @Before → 目标方法抛异常 → @AfterThrowing → @After关键点:
- @After是finally语义,无论正常还是异常都执行
- @AfterReturning只在正常返回时执行,可以获取返回值
- @AfterThrowing只在异常时执行,可以获取异常对象
- @Around最强大,能控制目标方法是否执行、修改参数、修改返回值
@Around正确用法:
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 前置逻辑
Object[] args = pjp.getArgs(); // 获取参数
long start = System.currentTimeMillis();
Object result = pjp.proceed(); // 执行目标方法,必须调用!
// 后置逻辑
long cost = System.currentTimeMillis() - start;
log.info("执行耗时: {}ms", cost);
return result; // 返回结果,可以修改
}注意: @Around必须调用pjp.proceed(),否则目标方法不会执行。忘记调用是最常见错误!
【面试题】6. Spring事件机制由哪些核心组件构成?如何实现异步事件?
【标准答案】 核心组件:
- ApplicationEvent - 事件基类,自定义事件需继承它
- ApplicationListener<E extends ApplicationEvent> - 事件监听器接口,实现onApplicationEvent方法
- ApplicationEventPublisher - 事件发布器,通过ApplicationContext获取
- @EventListener - 注解式监听器,无需实现接口,更简洁
事件发布流程:
// 1. 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
private Order order;
public OrderCreatedEvent(Object source, Order order) {
super(source);
this.order = order;
}
}
// 2. 发布事件
@Autowired
private ApplicationEventPublisher publisher;
publisher.publishEvent(new OrderCreatedEvent(this, order));
// 3. 监听事件
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// 处理逻辑
}实现异步事件:
@EventListener
@Async // 配合@EnableAsync开启异步
public void handleOrderCreated(OrderCreatedEvent event) {
// 异步执行,不阻塞主线程
}注意事项:
- @Async需要配合@EnableAsync和配置线程池
- 异步事件无法获取返回值,异常不会传播到发布方
- 事务内发布事件:如果监听器在事务内,事务回滚会导致监听器执行;需要@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)确保事务提交后执行
【面试题】7. 拦截器(Interceptor)和过滤器(Filter)有什么区别?执行顺序是怎样的?
【标准答案】 核心区别:
| 对比维度 | 过滤器(Filter) | 拦截器(Interceptor) |
|---|---|---|
| 所属规范 | Servlet规范,属于Tomcat | Spring规范,属于SpringMVC |
| 作用范围 | 所有Servlet请求(包括静态资源) | 只拦截Controller请求 |
| 执行时机 | 在DispatcherServlet之前 | 在DispatcherServlet之后、Controller之前 |
| 依赖注入 | 需要额外配置才能注入Bean | 天然支持@Autowired注入 |
| 获取信息 | 只能拿到Request/Response | 可以拿到Handler、Controller信息 |
| 实现方式 | 实现Filter接口 + @Component | 实现HandlerInterceptor + WebMvcConfigurer注册 |
执行顺序:
Filter.doFilter()前 → Interceptor.preHandle() → Controller →
Interceptor.postHandle() → View渲染 → Interceptor.afterCompletion() → Filter.doFilter()后使用场景:
- 过滤器: 编码转换、XSS防护、跨域处理、登录token解析(需要所有请求都处理)
- 拦截器: 权限校验、登录检查、日志记录、性能监控(需要Handler信息或只需要拦截Controller)
配置拦截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login", "/static/**");
}
}【面试题】8. @Transactional的7种传播行为分别是什么?常用的是哪些?
【标准答案】 七种传播行为:
| 传播行为 | 说明 |
|---|---|
| REQUIRED(默认) | 有事务就加入,没有就新建 |
| SUPPORTS | 有事务就加入,没有就非事务执行 |
| MANDATORY | 必须在事务中调用,否则抛异常 |
| REQUIRES_NEW | 总是新建事务,挂起当前事务 |
| NOT_SUPPORTED | 非事务执行,挂起当前事务 |
| NEVER | 非事务执行,如果在事务中调用则抛异常 |
| NESTED | 有事务就创建嵌套事务,没有就新建 |
常用场景:
REQUIRED(最常用,90%场景)
- 默认值,大部分业务方法用这个
- 多个方法在同一个事务中,要么全部成功,要么全部回滚
REQUIRES_NEW(独立事务)
- 日志记录、消息发送,即使主业务回滚也要记录
- 需要独立提交,不受外部事务影响
NESTED(嵌套事务)
- 子事务可以独立回滚,不影响父事务
- 批量操作,部分失败不影响整体
- 注意:需要数据库支持savepoint(如MySQL InnoDB)
常见陷阱:
// ❌ 错误:同类方法调用,事务不生效(没有代理)
public void methodA() {
this.methodB(); // 直接调用,不走代理
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {}
// ✅ 正确:注入自己或AopContext.currentProxy()
@Autowired
private OrderService self;
public void methodA() {
self.methodB(); // 通过代理调用
}【面试题】9. @Cacheable的原理是什么?缓存key如何自定义?
【标准答案】 核心原理: @Cacheable基于Spring Cache抽象,通过AOP实现:
- 方法调用时,先根据key查缓存
- 缓存命中 → 直接返回缓存值,不执行方法
- 缓存未命中 → 执行方法,将返回值存入缓存
底层实现:
- CacheManager接口 - 管理Cache组件
- Cache接口 - 实际缓存操作(get/put/evict)
- CacheInterceptor - AOP拦截器,处理缓存逻辑
自定义缓存key:
// 1. 使用SpEL表达式
@Cacheable(value = "users", key = "#id")
public User getUser(Long id) {}
// 2. 组合key
@Cacheable(value = "orders", key = "#userId + ':' + #orderId")
public Order getOrder(Long userId, Long orderId) {}
// 3. 使用方法名
@Cacheable(value = "users", key = "#root.methodName + #id")
public User getUser(Long id) {}
// 4. 自定义KeyGenerator
@Cacheable(value = "users", keyGenerator = "myKeyGenerator")
public User getUser(Long id) {}
@Bean
public KeyGenerator myKeyGenerator() {
return (target, method, params) -> {
return method.getName() + ":" + Arrays.toString(params);
};
}常用SpEL表达式:
#id- 参数名#p0/#a0- 第一个参数#root.method.name- 方法名#root.target- 目标对象#result- 返回值(仅@CachePut可用)
注意: 默认key生成是所有参数的hashCode,建议明确指定key,避免缓存错乱。
【面试题】10. SpringBoot分层JAR是什么?为什么能加速Docker镜像构建?
【标准答案】 分层JAR原理: 传统Fat-JAR把所有依赖和业务代码打成一个文件,Docker构建时每次修改代码都需要重新上传整个JAR。分层JAR将JAR按变化频率分层:
Jar结构(分层):
├── dependencies/ (第三方依赖,变化最少)
├── snapshot-dependencies/ (快照依赖)
├── resources/ (配置文件、静态资源)
├── application/ (业务代码,变化最多)
└── META-INF/
└── layers.idx (分层索引文件)Docker镜像构建优势:
# 传统方式 - 每次改代码都要复制整个JAR
COPY app.jar /app.jar
# 分层方式 - 利用Docker层缓存
COPY dependencies/ /app/
COPY snapshot-dependencies/ /app/
COPY resources/ /app/
COPY application/ /app/ # 只有这层频繁变化构建过程:
# 1. Maven配置
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>LAYERED_JAR</layout>
</configuration>
</plugin>
# 2. 解压分层
java -Djarmode=layertools -jar app.jar list
java -Djarmode=layertools -jar app.jar extract
# 3. Dockerfile分层构建
FROM eclipse-temurin:17-jre as builder
WORKDIR /builder
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract
FROM eclipse-temurin:17-jre
WORKDIR /application
COPY --from=builder /builder/dependencies/ ./
COPY --from=builder /builder/snapshot-dependencies/ ./
COPY --from=builder /builder/resources/ ./
COPY --from=builder /builder/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]加速效果: 只改业务代码时,只重建application层,其他层复用Docker缓存,构建时间从分钟级降到秒级,推送镜像也只推送变化的层。
本轮题目覆盖考点:
- @SpringBootApplication注解组合(自动配置原理)
- @ConfigurationProperties vs @Value(配置管理)
- BeanPostProcessor(Bean生命周期)
- @Import三种方式(注解驱动开发)
- AOP通知执行顺序(AOP切面编程)
- Spring事件机制(事件机制)
- 拦截器vs过滤器(Web开发)
- @Transactional传播行为(数据访问层)
- @Cacheable原理(缓存与调度)
- 分层JAR(打包部署)