一、Spring Boot 概述
1.1 为什么需要 Spring Boot
传统 Spring 项目存在三大痛点:
依赖版本冲突:手动管理依赖版本,容易出现版本不兼容。
配置繁琐:大量 XML 或 Java 配置类,启动慢、维护成本高。
部署复杂:需要外部 Servlet 容器,打包、部署、运维链路长。
Spring Boot 通过起步依赖、自动配置、内嵌容器三大核心能力,系统性地解决了上述问题。
1.2 核心特性
| 特性 | 说明 |
|---|---|
| 起步依赖(Starter) | 将常用依赖以固定版本打包,引入一个 starter 即可获得一组匹配依赖 |
| 自动配置(Auto Configuration) | 根据类路径和已定义的 Bean,自动完成大量默认配置 |
| 内嵌容器 | 内嵌 Tomcat、Jetty 或 Undertow,可打成 JAR 包独立运行 |
| Actuator | 提供生产可用的监控端点,如健康检查、指标、环境信息等 |
| 外部化配置 | 支持 properties、YAML、环境变量、命令行参数等多种来源,且有明确优先级 |
1.3 Spring Boot 与 Spring、Spring Cloud 的关系
Spring:核心框架,提供 IoC、AOP、数据访问、Web 等基础能力。
Spring Boot:在 Spring 之上的快速开发脚手架,解决配置和部署复杂度。
Spring Cloud:基于 Spring Boot 构建,提供微服务治理能力,如服务注册发现、配置中心、网关、熔断等。
三者是层层递进、相互配合的关系,面试中常常会考察对这一关系的理解。
二、自动配置原理
自动配置是 Spring Boot 面试中的绝对高频考点。要讲清楚自动配置,必须从@SpringBootApplication注解和SpringFactoriesLoader机制入手。
2.1 @SpringBootApplication 背后的三个注解
@SpringBootApplication是一个组合注解,等价于同时声明了以下三个注解:
@SpringBootConfiguration:本质是
@Configuration,标记当前类是配置类。@EnableAutoConfiguration:开启自动配置,是核心所在。
@ComponentScan:开启组件扫描,默认扫描启动类所在包及其子包。
简化源码结构:
java
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { }2.2 自动配置的加载流程
自动配置的入口是@EnableAutoConfiguration,其内部通过@Import(AutoConfigurationImportSelector.class)导入选择器。整体流程如下:
启动类上的
@EnableAutoConfiguration触发AutoConfigurationImportSelector。该选择器调用
SpringFactoriesLoader.loadFactoryNames方法。从类路径下所有
META-INF/spring.factories文件(以及 Spring Boot 2.7 之后推荐的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports)中读取自动配置类全限定名。通过
@Conditional系列条件注解对候选配置类进行过滤。符合条件的配置类被加载为 Bean,完成默认配置。
核心流程伪代码:
java
public class AutoConfigurationImportSelector { protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 从 META-INF/spring.factories 读取 EnableAutoConfiguration 对应的配置类列表 List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); return configurations; } }2.3 条件注解体系
自动配置之所以能“按需生效”,依赖的是 Spring 提供的条件注解:
| 条件注解 | 作用 |
|---|---|
| @ConditionalOnClass | 当类路径下存在指定类时生效 |
| @ConditionalOnMissingClass | 当类路径下不存在指定类时生效 |
| @ConditionalOnBean | 当容器中存在指定 Bean 时生效 |
| @ConditionalOnMissingBean | 当容器中不存在指定 Bean 时生效 |
| @ConditionalOnProperty | 当配置文件中存在指定属性且值为指定值时生效 |
| @ConditionalOnWebApplication | 当当前应用是 Web 应用时生效 |
| @ConditionalOnExpression | 当 SpEL 表达式结果为 true 时生效 |
经典应用场景:引入spring-boot-starter-data-redis且类路径存在RedisTemplate相关类时,RedisAutoConfiguration才会被激活。
2.4 自定义 Starter 的思路
创建配置工程,定义自动配置类,使用
@Configuration和@Bean提供核心对象。在
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports或spring.factories中注册配置类。通过
@ConfigurationProperties提供可配置属性。用
@ConditionalOnMissingBean等注解保证可被用户覆盖。单独拆分
xxx-spring-boot-autoconfigure与xxx-spring-boot-starter两个模块。
三、Spring Boot 启动流程
Spring Boot 应用的启动本质是执行SpringApplication.run方法。
3.1 启动流程图
3.2 关键步骤详解
创建 SpringApplication 实例:推断应用类型(有无 Web 环境),加载
ApplicationContextInitializer和ApplicationListener。准备环境:创建
ConfigurableEnvironment,加载配置文件和环境变量,并发布环境准备事件。创建应用上下文:根据应用类型创建
AnnotationConfigApplicationContext、AnnotationConfigServletWebServerApplicationContext或AnnotationConfigReactiveWebServerApplicationContext。执行刷新:调用经典 Spring 的
refresh方法,完成 Bean 的创建、依赖注入、初始化等过程。执行 Runner:容器刷新完毕后,依次执行
ApplicationRunner和CommandLineRunner接口的实现类。
启动完成后执行初始化逻辑的示例:
java
@Component public class DataInitRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("容器启动完成,执行数据初始化..."); } }四、配置文件与多环境管理
4.1 配置文件格式
Spring Boot 支持application.properties和application.yml。YAML 层级清晰,适合复杂配置;properties 更简单直观。
yaml
server: port: 8080 servlet: context-path: /api spring: application: name: demo-service datasource: url: jdbc:mysql://localhost:3306/demo username: root password: 123456
4.2 多环境配置
使用application-{profile}.yml区分环境,并在主配置中激活:
yaml
# application.yml spring: profiles: active: dev
yaml
# application-dev.yml server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/dev_db
也可以在启动命令中动态指定:
bash
java -jar app.jar --spring.profiles.active=prod
4.3 配置属性优先级
从高到低大致如下:
命令行参数
Java 系统属性
操作系统环境变量
jar 包外部的
application-{profile}.ymljar 包内部的
application-{profile}.ymljar 包外部的
application.ymljar 包内部的
application.yml@PropertySource引入的配置默认配置
面试提示:至少记住“命令行参数优先级最高,其次环境变量,然后是外部配置文件,最后是内部配置文件”这条主线。
4.4 配置绑定
使用@ConfigurationProperties将配置项批量绑定到实体类,比逐个使用@Value更清晰:
java
@Component @ConfigurationProperties(prefix = "app.upload") public class UploadProperties { private String path; private long maxSize; private List<String> allowTypes; // getter / setter 省略 }yaml
app: upload: path: /data/upload max-size: 10485760 allow-types: - jpg - png - pdf
五、常用注解详解
5.1 核心组件类注解
| 注解 | 作用 |
|---|---|
| @Component | 通用组件,将类交给 Spring 容器管理 |
| @Service | 业务层组件,语义上是 @Component 的特化 |
| @Repository | 数据访问层组件,额外支持异常翻译 |
| @Controller | 控制层组件,处理 Web 请求 |
| @RestController | 等于 @Controller 加 @ResponseBody,返回 JSON 数据 |
| @Configuration | 标记配置类,内部 @Bean 方法会被代理增强 |
| @Bean | 在配置类中声明 Bean,方法名默认作为 Bean 名称 |
| @Scope | 指定 Bean 作用域,如 singleton、prototype |
| @Lazy | 延迟初始化 Bean |
5.2 依赖注入类注解
| 注解 | 说明 |
|---|---|
| @Autowired | Spring 提供,默认按类型注入,可结合 @Qualifier 按名称注入 |
| @Resource | JDK 提供,默认按名称注入,找不到名称再按类型注入 |
| @Qualifier | 配合 @Autowired 指定注入 Bean 名称 |
| @Value | 注入简单属性值,支持 SpEL 表达式 |
@Autowired 与 @Resource 的区别:
来源不同:@Autowired 来自 Spring 框架,@Resource 来自 JDK 的 javax.annotation 包。
注入顺序不同:@Autowired 默认按类型注入,@Resource 默认按名称注入。
配合方式不同:@Autowired 配合 @Qualifier 使用,@Resource 通过 name 属性指定。
是否强制不同:@Autowired 支持 required=false,@Resource 没有此参数,但其行为类似默认必须找到。
5.3 Web 相关注解
| 注解 | 作用 |
|---|---|
| @RequestMapping | 类或方法级别的请求映射,可指定路径、方法、参数等 |
| @GetMapping/@PostMapping/@PutMapping/@DeleteMapping | 对应 HTTP 方法的具体映射 |
| @PathVariable | 从 URL 路径中获取参数,如 /user/{id} |
| @RequestParam | 从请求查询参数或表单中获取参数 |
| @RequestBody | 将请求体 JSON 反序列化为对象 |
| @ResponseBody | 将方法返回值序列化为 JSON 写回响应 |
| @RequestHeader | 读取请求头信息 |
| @CookieValue | 获取 Cookie 中指定名称的值 |
| @SessionAttribute | 获取 HttpSession 中保存的属性 |
| @ModelAttribute | 将请求参数绑定到模型对象,常用于表单回显和公共数据初始化 |
| @ExceptionHandler | 处理 Controller 中抛出的指定异常 |
| @ControllerAdvice/@RestControllerAdvice | 全局异常处理、全局数据绑定和全局模型增强 |
| @ResponseStatus | 指定响应状态码,可配合自定义异常返回对应 HTTP 状态 |
| @Valid/@Validated | 开启参数校验,@Valid 是标准 JSR-303,@Validated 是 Spring 扩展,支持分组校验 |
六、参数校验与全局异常处理
6.1 参数校验常用注解
| 注解 | 说明 |
|---|---|
| @NotNull | 不能为 null |
| @NotBlank | 字符串不能为 null 且去除首尾空格后长度大于 0 |
| @NotEmpty | 字符串、集合或数组不能为 null 且长度或大小大于 0 |
| @Size(min, max) | 校验字符串、集合或数组的长度范围 |
| @Min(value) | 数值必须大于或等于指定值 |
| @Max(value) | 数值必须小于或等于指定值 |
| 校验邮箱格式 | |
| @Pattern(regexp) | 按正则表达式校验字符串 |
在 DTO 上添加校验注解,并在 Controller 方法参数前使用@Valid开启校验:
java
public class UserCreateRequest { @NotBlank(message = "用户名不能为空") private String username; @Email(message = "邮箱格式不正确") private String email; @Size(min = 6, max = 20, message = "密码长度必须在 6 到 20 之间") private String password; // getter / setter 省略 } @RestController @RequestMapping("/users") public class UserController { @PostMapping public Result<Long> create(@Valid @RequestBody UserCreateRequest request) { // 校验通过后执行业务逻辑 return Result.success(userService.create(request)); } }6.2 全局异常处理
使用@RestControllerAdvice配合@ExceptionHandler实现全局统一异常处理:
java
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public Result<Void> handleValidException(MethodArgumentNotValidException e) { String message = e.getBindingResult().getFieldErrors() .stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining("; ")); return Result.fail(400, message); } @ExceptionHandler(BusinessException.class) public Result<Void> handleBusinessException(BusinessException e) { return Result.fail(e.getCode(), e.getMessage()); } @ExceptionHandler(Exception.class) public Result<Void> handleException(Exception e) { log.error("系统异常", e); return Result.fail(500, "系统繁忙,请稍后再试"); } }面试中经常被追问的几点:参数校验异常需要放在最后兜底、自定义异常要区分业务错误码、不要直接把异常堆栈返回给客户端。
七、拦截器、过滤器与监听器
7.1 过滤器 Filter
过滤器属于 Servlet 规范,作用于请求进入 Servlet 容器之后、到达 Controller 之前,以及响应返回客户端之前。
java
@Component public class RequestTimeFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { long start = System.currentTimeMillis(); chain.doFilter(request, response); long cost = System.currentTimeMillis() - start; System.out.println("请求耗时:" + cost + " ms"); } }7.2 拦截器 HandlerInterceptor
拦截器属于 Spring MVC 体系,依赖 Spring 容器,常用于权限校验、登录检查、接口统计等:
java
@Component public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (!StringUtils.hasText(token)) { throw new BusinessException(401, "未登录"); } return true; } } @Configuration public class WebConfig implements WebMvcConfigurer { private final AuthInterceptor authInterceptor; public WebConfig(AuthInterceptor authInterceptor) { this.authInterceptor = authInterceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns("/api/**") .excludePathPatterns("/api/login", "/api/register"); } }7.3 过滤器与拦截器的区别
| 对比项 | 过滤器 Filter | 拦截器 HandlerInterceptor |
|---|---|---|
| 所属规范 | Servlet 规范 | Spring MVC 框架 |
| 依赖容器 | Servlet 容器 | Spring 容器 |
| 触发时机 | 进入 DispatcherServlet 之前 | 进入 Controller 方法之前 |
| 应用场景 | 编码、跨域、日志等通用处理 | 登录校验、权限校验等业务处理 |
7.4 监听器
监听器可以监听 Servlet 容器生命周期事件和 Spring 容器事件。常问ApplicationRunner与CommandLineRunner的区别:两者都在应用启动完成后执行,ApplicationRunner参数是ApplicationArguments,CommandLineRunner参数是原始字符串数组。
java
@Component public class ApplicationReadyListener { @EventListener(ApplicationReadyEvent.class) public void onReady() { System.out.println("应用已就绪"); } }八、事务管理
8.1 @Transactional 核心属性
propagation:事务传播行为,默认 REQUIRED。
isolation:事务隔离级别,默认使用数据库默认隔离级别。
readOnly:只读事务,适合查询方法。
rollbackFor:指定哪些异常触发回滚,默认只有 RuntimeException 和 Error 回滚。
timeout:事务超时时间。
java
@Transactional(rollbackFor = Exception.class, timeout = 30) public void createOrder(OrderCreateRequest request) { // 业务代码 }8.2 事务传播行为
| 传播行为 | 含义 |
|---|---|
| REQUIRED | 有事务则加入,没有则新建事务,最常见 |
| REQUIRES_NEW | 总是新建事务,并挂起当前事务 |
| SUPPORTS | 有事务则加入,没有则以非事务方式执行 |
| NOT_SUPPORTED | 总是以非事务方式执行,并挂起当前事务 |
| MANDATORY | 必须在事务中执行,否则抛异常 |
| NEVER | 必须以非事务方式执行,否则抛异常 |
| NESTED | 嵌套事务,内层事务可以独立回滚 |
面试常问「REQUIRED 和 REQUIRES_NEW 的区别」:REQUIRED 跟随外部事务一起提交或回滚;REQUIRES_NEW 独立开启新事务,内层方法提交后不受外层回滚影响。
8.3 事务失效常见场景
自调用:同一个类内部方法直接调用,没有经过代理对象。
方法不是 public:@Transactional 只对 public 方法生效。
异常被捕获:方法内捕获并吞噬异常,框架感知不到异常不会回滚。
rollbackFor 不匹配:抛出受检异常但未配置 rollbackFor。
数据源或事务管理器不匹配:多数据源场景下没有指定正确的事务管理器。
不被 Spring 管理:类没有注册为 Bean,或是在非 Spring 线程中调用。
九、缓存使用
9.1 缓存注解
常用注解包括@Cacheable、@CachePut、@CacheEvict和@Caching。
java
@Cacheable(cacheNames = "user", key = "#id") public User getUserById(Long id) { return userMapper.selectById(id); } @CachePut(cacheNames = "user", key = "#user.id") public User updateUser(User user) { userMapper.updateById(user); return user; } @CacheEvict(cacheNames = "user", key = "#id") public void deleteUser(Long id) { userMapper.deleteById(id); }@Cacheable会在方法执行前先查缓存,命中则直接返回。@CachePut始终执行方法并更新缓存。@CacheEvict执行方法后删除缓存。
9.2 缓存穿透、击穿、雪崩
| 问题 | 含义 | 常见解决思路 |
|---|---|---|
| 缓存穿透 | 请求不存在的数据,每次都绕过缓存直接打到数据库 | 缓存空值、布隆过滤器、参数校验 |
| 缓存击穿 | 热点 key 过期瞬间大量请求同时访问数据库 | 热点不过期、互斥锁重建缓存 |
| 缓存雪崩 | 大量 key 同一时间过期或缓存服务不可用 | 随机过期时间、本地缓存兜底、多级缓存 |
回答时要区分三者的定义、触发条件和应对措施。
十、日志框架
Spring Boot 默认使用 SLF4J 作为日志门面,并引入 Logback 作为默认实现。使用日志门面的好处在于业务代码不直接依赖具体日志实现,后续可无感切换。
java
@Slf4j @RestController public class LogDemoController { @GetMapping("/log") public String logDemo() { log.info("info 级别日志:{}", "示例参数"); return "ok"; } }在application.yml中可通过logging.level、logging.file.name等属性配置日志级别和输出文件。
十一、总结
Spring Boot 的核心可以浓缩为以下几句话:
定位:Spring Boot 不是 Spring 的替代,而是在 Spring 之上的快速开发脚手架。
三大能力:起步依赖解决依赖管理,自动配置解决配置繁琐,内嵌容器解决部署复杂。
自动配置:入口是
@EnableAutoConfiguration→AutoConfigurationImportSelector→SpringFactoriesLoader,通过@Conditional系列注解按需生效。启动流程:
SpringApplication.run→ 准备环境 → 创建上下文 → refresh → 执行 Runner。配置管理:多环境 profile、外部化配置优先级、
@ConfigurationProperties批量绑定。Web 开发:注解体系、参数校验、全局异常处理、Filter/Interceptor/Listener 三者区别。
数据与缓存:声明式事务、事务失效场景、缓存三大问题与应对。