Skip to content

SpringBoot2核心功能面试题库 ​

生成时间:2026-05-21 23:42


【面试题】1. @SpringBootApplication注解由哪几个注解组合而成?分别起什么作用? ​

【标准答案】 @SpringBootApplication 是一个组合注解,由三个核心注解构成:

  1. @SpringBootConfiguration - 本质就是@Configuration,标注这是一个配置类,允许在类中定义@Bean方法
  2. @EnableAutoConfiguration - 开启自动配置的核心,通过@Import导入AutoConfigurationImportSelector,扫描META-INF/spring.factories或AutoConfiguration.imports文件中定义的自动配置类
  3. @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返回代理对象

典型应用场景:

  1. AOP代理创建 - AbstractAutoProxyCreator在postProcessAfterInitialization中为Bean创建代理对象
  2. 注解解析 - CommonAnnotationBeanPostProcessor处理@PostConstruct、@PreDestroy、@Resource
  3. 属性校验 - BeanValidationPostProcessor校验@Validated标注的Bean
  4. 自定义逻辑 - 比如对所有特定注解的Bean进行增强、记录日志、添加监控

注意: BeanPostProcessor本身也是Bean,但它执行时其他普通Bean还没初始化完成,所以不能在BeanPostProcessor中依赖其他业务Bean,否则会导致提前初始化问题。


【面试题】4. @Import有哪三种导入方式?各自的应用场景是什么? ​

【标准答案】 @Import用于向容器中导入组件,有三种方式:

1. 直接导入普通类

java
@Import(UserService.class)
  • 最简单方式,直接把UserService注册为Bean
  • Bean名称为全类名

2. 导入ImportSelector实现类

java
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实现类

java
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

关键点:

  1. @After是finally语义,无论正常还是异常都执行
  2. @AfterReturning只在正常返回时执行,可以获取返回值
  3. @AfterThrowing只在异常时执行,可以获取异常对象
  4. @Around最强大,能控制目标方法是否执行、修改参数、修改返回值

@Around正确用法:

java
@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事件机制由哪些核心组件构成?如何实现异步事件? ​

【标准答案】 核心组件:

  1. ApplicationEvent - 事件基类,自定义事件需继承它
  2. ApplicationListener<E extends ApplicationEvent> - 事件监听器接口,实现onApplicationEvent方法
  3. ApplicationEventPublisher - 事件发布器,通过ApplicationContext获取
  4. @EventListener - 注解式监听器,无需实现接口,更简洁

事件发布流程:

java
// 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) {
    // 处理逻辑
}

实现异步事件:

java
@EventListener
@Async  // 配合@EnableAsync开启异步
public void handleOrderCreated(OrderCreatedEvent event) {
    // 异步执行,不阻塞主线程
}

注意事项:

  1. @Async需要配合@EnableAsync和配置线程池
  2. 异步事件无法获取返回值,异常不会传播到发布方
  3. 事务内发布事件:如果监听器在事务内,事务回滚会导致监听器执行;需要@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)确保事务提交后执行

【面试题】7. 拦截器(Interceptor)和过滤器(Filter)有什么区别?执行顺序是怎样的? ​

【标准答案】 核心区别:

对比维度过滤器(Filter)拦截器(Interceptor)
所属规范Servlet规范,属于TomcatSpring规范,属于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)

配置拦截器:

java
@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有事务就创建嵌套事务,没有就新建

常用场景:

  1. REQUIRED(最常用,90%场景)

    • 默认值,大部分业务方法用这个
    • 多个方法在同一个事务中,要么全部成功,要么全部回滚
  2. REQUIRES_NEW(独立事务)

    • 日志记录、消息发送,即使主业务回滚也要记录
    • 需要独立提交,不受外部事务影响
  3. NESTED(嵌套事务)

    • 子事务可以独立回滚,不影响父事务
    • 批量操作,部分失败不影响整体
    • 注意:需要数据库支持savepoint(如MySQL InnoDB)

常见陷阱:

java
// ❌ 错误:同类方法调用,事务不生效(没有代理)
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实现:

  1. 方法调用时,先根据key查缓存
  2. 缓存命中 → 直接返回缓存值,不执行方法
  3. 缓存未命中 → 执行方法,将返回值存入缓存

底层实现:

  • CacheManager接口 - 管理Cache组件
  • Cache接口 - 实际缓存操作(get/put/evict)
  • CacheInterceptor - AOP拦截器,处理缓存逻辑

自定义缓存key:

java
// 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镜像构建优势:

dockerfile
# 传统方式 - 每次改代码都要复制整个JAR
COPY app.jar /app.jar

# 分层方式 - 利用Docker层缓存
COPY dependencies/ /app/
COPY snapshot-dependencies/ /app/
COPY resources/ /app/
COPY application/ /app/  # 只有这层频繁变化

构建过程:

bash
# 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缓存,构建时间从分钟级降到秒级,推送镜像也只推送变化的层。


本轮题目覆盖考点:

  1. @SpringBootApplication注解组合(自动配置原理)
  2. @ConfigurationProperties vs @Value(配置管理)
  3. BeanPostProcessor(Bean生命周期)
  4. @Import三种方式(注解驱动开发)
  5. AOP通知执行顺序(AOP切面编程)
  6. Spring事件机制(事件机制)
  7. 拦截器vs过滤器(Web开发)
  8. @Transactional传播行为(数据访问层)
  9. @Cacheable原理(缓存与调度)
  10. 分层JAR(打包部署)