Featured image of post Java 面试合集:Spring 全家桶

Java 面试合集:Spring 全家桶

Java 面试 Spring 框架合集:Spring 7 大组件、IoC / AOP 原理与失效场景、自动配置原理、Spring Boot Starter、Spring Cloud Gateway / Sentinel / Nacos 选型

本文写于 2017 年 3 月——Spring Boot 2.0 即将发布、Spring Cloud Netflix 全家桶正盛。

一、Spring 7 大组件

1.1 核心组件

组件作用
核心容器(Spring Core)提供 IoC 和依赖注入(DI)特性
Spring 上下文(Spring Context)BeanFactory 功能加强的子接口
Spring AOP面向切面编程,AOP 联盟兼容
Spring DAOJDBC 抽象层,简化 JDBC 编码
Spring ORM整合 Hibernate / MyBatis 等 ORM 框架
Spring WebWeb 应用开发支持
Spring MVCMVC 思想的 Web 实现

1.2 面试追问

Q1:Spring 3 大核心思想?

  • IoC(Inversion of Control):控制反转,对象的创建和依赖关系由 Spring 容器管理
  • DI(Dependency Injection):依赖注入,通过构造器 / Setter / 字段注入
  • AOP(Aspect-Oriented Programming):面向切面编程

二、IoC 原理

2.1 IoC 容器核心接口

1
2
3
4
5
BeanFactory(基础容器)
  └─ ApplicationContext(增强容器)
       ├─ ClassPathXmlApplicationContext
       ├─ AnnotationConfigApplicationContext
       └─ WebApplicationContext

2.2 Bean 生命周期

1
实例化(构造器)→ 属性注入 → 初始化前回调 → 初始化方法 → 初始化后回调 → 使用 → 销毁前回调 → 销毁

关键回调接口

接口方法用途
BeanFactoryPostProcessorpostProcessBeanFactory容器刷新前修改 Bean 定义
BeanPostProcessorpostProcessBeforeInitialization初始化前增强
BeanPostProcessorpostProcessAfterInitialization初始化后增强
InitializingBeanafterPropertiesSet初始化回调
DisposableBeandestroy销毁回调

2.3 Bean 作用域

作用域描述适用
singleton单例(默认)无状态 Bean
prototype多例有状态 Bean
request每个 HTTP 请求Web 应用
session每个 HTTP SessionWeb 应用
application整个 ServletContext全局共享

三、AOP 原理

3.1 过滤器 vs 拦截器 vs AOP

维度过滤器(Filter)拦截器(Interceptor)AOP
拦截对象URLURL类的元数据(包/类/方法/参数)
时机请求到达控制器前后方法执行前后
粒度
容器ServletSpring MVCSpring

执行顺序:过滤器 → 拦截器 → AOP

3.2 AOP 失效场景

场景 1:内部方法直接调用

1
2
3
4
5
6
7
8
9
@Component
public class MyBean {
    public void methodA() {
        methodB();  // 这里会绕过代理!
    }

    @Transactional
    public void methodB() { /* 业务 */ }
}

原因:Spring AOP 基于动态代理,内部方法直接调用不走代理

解决方案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 1. AopContext.currentProxy(不推荐,代码侵入)
@EnableAspectJAutoProxy(exposeProxy = true)
public class MyApp { /* ... */ }

// methodA 中:
((MyService) AopContext.currentProxy()).methodB();

// 2. 自我注入(推荐)
@Service
public class MyService {
    @Autowired
    @Lazy
    private MyService self;

    public void methodA() {
        self.methodB();
    }
}

// 3. 提取到新 Bean(最优雅)
@Service
public class OrderTransactionExecutor {
    private final TransactionTemplate transactionTemplate;

    public void executeInTransaction(NonSpringTask task) {
        transactionTemplate.execute(status -> {
            task.doDbWork();
            return null;
        });
    }
}

场景 2:new 出来的对象

1
2
3
// 错误:new 出来的对象,aop 无法代理
NonSpringTask task = new NonSpringTask();
task.doDbWork();  // 事务不生效

解决:用 Spring 容器管理,或在 new 出的对象内部用 TransactionTemplate

场景 3:异步方法 @Async

1
2
@Async
public void asyncMethod() { /* 异步执行 */ }

坑点

  • 异步方法不能在同类内部调用(同样不走代理)
  • 异步方法抛异常无法被 Controller 捕获
  • 异步方法需要 public 修饰
  • 异步方法所在的类需要 @EnableAsync 或被扫描

四、Spring Boot 自动配置原理

4.1 启动入口

1
2
3
4
5
6
@SpringBootApplication  // = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

4.2 自动配置机制

SpringBoot 在启动时会扫描外部引用 jar 包中的 META-INF/spring.factories 文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。

自 Spring Boot 3.0 开始,自动配置包的路径从 META-INF/spring.factories 修改为 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

4 大步骤

  1. 启动类加 @SpringBootApplication
  2. 注解触发 @EnableAutoConfiguration
  3. EnableAutoConfiguration 导入 AutoConfigurationImportSelector
  4. 该 Selector 扫描 META-INF/spring.factories 中的 EnableAutoConfiguration 全限定名列表
  5. 加载所有 XxxAutoConfiguration
  6. 每个 AutoConfiguration 类用 @Conditional 系列注解控制是否生效

4.3 关键注解

注解作用
@ConditionalOnClass类路径存在某类时生效
@ConditionalOnMissingBean容器没有某 Bean 时生效
@ConditionalOnProperty配置项满足条件时生效
@ConditionalOnWebApplicationWeb 应用时生效
@ConditionalOnBean容器有某 Bean 时生效

4.4 Starter 原理

在 SpringBoot 启动时由 @SpringBootApplication 注解会自动去 maven 中读取每个 starter 中的 spring.factories 文件,该文件里配置了所有需要被创建 spring 容器中的 bean,并且进行自动配置把 bean 注入 SpringContext 中。

自定义 Starter 4 步

  1. 创建 xxx-spring-boot-starter 工程
  2. 引入 Spring Boot 相关依赖
  3. 创建 XxxAutoConfiguration
  4. META-INF/spring.factories 注册

4.5 启动时执行代码

方式 1ApplicationRunner / CommandLineRunner

1
2
3
4
5
@Bean
@Order(1)
public ApplicationRunner myRunner() {
    return args -> System.out.println("复杂参数:" + Arrays.toString(args.getSourceArgs()));
}

方式 2CommandLineRunner(简单参数)

1
2
3
4
@Bean
public CommandLineRunner myRunner() {
    return args -> System.out.println("简单参数:" + Arrays.toString(args));
}

方式 3ApplicationListener<ApplicationReadyEvent>

1
2
3
4
5
6
7
@Bean
public ApplicationListener<ApplicationReadyEvent> readyListener() {
    return event -> {
        // Spring Boot 完全就绪后
        // 可获取 environment
    };
}

三种方式对比

方式用途时机
CommandLineRunner简单启动逻辑Spring 启动完成
ApplicationRunner复杂启动逻辑Spring 启动完成
ApplicationListener<ApplicationReadyEvent>完全就绪后执行Web 服务器就绪后
@PostConstruct单个 Bean 初始化Bean 注入完成
InitializingBean单个 Bean 初始化(更安全)Bean 注入完成

五、Spring Boot 常用注解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@SpringBootApplication      // 核心注解
@SpringBootConfiguration    // = @Configuration
@EnableAutoConfiguration    // 打开自动配置
@ComponentScan              // 组件扫描

@Repository                 // DAO 组件
@Service                    // Service 组件
@RestController             // = @Controller + @ResponseBody
@ResponseBody               // 返回值写入 response body
@Component                  // 泛指组件
@Bean                       // 产生 Bean
@Autowired                  // byType 自动装配
@Qualifier("name")          // 指定 Bean 名称
@Resource                   // byName 自动装配
@RequestMapping             // URL 映射
@RequestParam               // 请求参数
@Scope                      // Bean 作用域
@Primary                    // 优先选择
@PostConstruct              // 初始化回调
@PreDestroy                 // 销毁回调
@EnableTransactionManagement // 启用注解事务
@Transactional               // 声明事务
@ControllerAdvice           // 全局异常处理
@ExceptionHandler           // 异常处理

六、Spring Cloud Gateway

6.1 简介

Spring Cloud Gateway 是一个构建于 Spring Framework 5、Project Reactor 和 Spring Boot 2 之上的动态、可编程的 API 网关

6.2 核心特点

  1. 基于路由的 API 网关:根据预定义路由转发请求
  2. 集成断言和过滤器:声明式路由控制
  3. 响应式编程支持:内部使用 WebFlux
  4. 服务发现集成:Eureka / Consul / Nacos
  5. 断路器集成:Hystrix / Sentinel / Resilience4J
  6. 安全集成:Spring Security + OAuth2 + JWT
  7. CORS 支持
  8. 全局过滤器:日志、限流
  9. 动态路由:不重启更新路由

6.3 三大组成

组成描述
Route(路由)路由的基本模块,由 ID、目标 URI、断言集、过滤器集组成
Predicate(断言)访问该路由的访问规则,匹配 headers / 参数等
Filter(过滤器)在请求/响应前后修改内容

6.4 优点

  • 基于非阻塞和异步的 Netty + Spring WebFlux
  • 高吞吐量 + 高并发
  • 声明式路由规则
  • 与 Spring 生态无缝集成
  • 原生 WebSocket 支持
  • 易于扩展

七、Spring Cloud 组件对比

组件Spring Cloud NetflixDubboSpring Cloud Alibaba
服务中心Eureka / ConsulZKNacos
配置中心Spring Cloud Config-Nacos
远程调用FeigngRPCFeign
API 网关Zuul / Gateway-Gateway / Apisix
分布式事务--Seata
熔断器Hystrix-Sentinel / Resilience4j
限流降级Hystrix-Sentinel
分布式追踪Sleuth + Zipkin-SkyWalking

7.1 三大断路器对比

维度SentinelHystrix(维护状态)Resilience4j(Spring 推荐)
开发者AlibabaNetflix独立
隔离策略信号量隔离线程池/信号量信号量
熔断策略响应时间 / 异常比率 / 异常数异常比率异常比率 / 响应时间
实时统计滑动窗口(LeapArray)滑动窗口(RxJava)Ring Bit Buffer
限流基于 QPS + 调用关系有限Rate Limiter
控制台开箱即用简单需对接

Sentinel 核心特性

  • 流量控制(QPS / 并发线程数)
  • 熔断降级(响应时间 / 异常数)
  • 系统负载保护
  • 实时监控
  • 完善 SPI 扩展点

八、CAP 原则

CAP 原则:Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼

维度含义
一致性所有数据备份在同一时刻是否同样的值
可用性集群一部分节点故障后,集群整体还能响应
分区容忍实际效果而言,分区相当于对通信的时限要求

8.1 注册中心 CAP 选择

注册中心CAP
EurekaAP(可用性 + 分区容忍)
ZooKeeperCP(一致性 + 分区容忍)
NacosAP + CP(可切换)

Nacos 配置

1
2
3
4
5
spring:
  cloud:
    nacos:
      discovery:
        ephemeral: false  # false: 持久化实例,使用 CP 架构;true: 临时实例,使用 AP 架构

九、写在最后

Spring 面试要点

  1. 基础:IoC 原理、Bean 生命周期、Bean 作用域
  2. AOP:失效场景(内部调用 / new 对象 / 异步方法)
  3. Spring Boot:自动配置原理、Starter、自定义 Starter
  4. Spring Cloud:Gateway 三大组成、Sentinel / Nacos 选型、CAP 原则
  5. 高频追问:“AOP 失效怎么办”、“Starter 怎么写”、“服务挂了怎么限流”

参考资料

使用 Hugo 构建
主题 StackJimmy 设计