Spring Boot 面试八股文(核心考点汇总)

一、Spring Boot 基础概念

1. 什么是 Spring Boot?它的核心优势是什么?

Spring Boot 是 Spring 生态的“脚手架”,基于 Spring 框架,通过自动配置、约定优于配置(Convention Over Configuration) 简化 Spring 应用的搭建与开发,核心目标是“让开发人员快速启动一个独立、生产级别的 Spring 应用”。

核心优势:

  • 简化配置:无需手动编写大量 XML 配置,通过 application.properties/yaml 或注解即可完成配置。
  • 自动配置:根据依赖的 Jar 包(如引入 spring-boot-starter-web 则自动配置 Tomcat、Spring MVC),自动初始化相关 Bean 到 Spring 容器。
  • 嵌入式容器:内置 Tomcat、Jetty、Undertow 等 Servlet 容器,无需单独部署 WAR 包,直接运行 Jar 包即可。
  • starters 依赖:提供标准化的 starter 依赖(如 spring-boot-starter-data-jpa),一键引入某功能所需的所有依赖,避免依赖冲突。
  • 生产级特性:内置健康检查(Actuator)、 metrics 监控、外部化配置等,简化生产环境部署与维护。
  • 无代码生成/XML 配置:基于注解驱动,无需手动生成代码或编写 XML。

2. Spring Boot 与 Spring、Spring MVC 的关系?

三者是“包含与扩展”的关系,核心目标都是简化 Java 开发,但定位不同:

  • Spring:核心是“依赖注入(DI)”和“面向切面(AOP)”,是整个生态的基础,负责管理 Bean 的生命周期、解耦组件。
  • Spring MVC:是 Spring 生态的“Web 模块”,基于 MVC 架构(Model-View-Controller),负责处理 HTTP 请求、路由分发、视图渲染等 Web 开发功能。
  • Spring Boot:不是替换 Spring/Spring MVC,而是整合并简化两者的使用——通过自动配置将 Spring MVC 的 DispatcherServlet、Tomcat 等组件自动初始化,开发者无需手动配置即可快速开发 Web 应用。

简单总结:Spring Boot = Spring + Spring MVC + 自动配置 + 嵌入式容器 + Starter 机制

3. Spring Boot 的核心注解有哪些?各自作用是什么?

Spring Boot 核心注解集中在启动类和配置类,关键注解如下:

注解 作用
@SpringBootApplication 启动类的“组合注解”,包含 3 个核心注解,是 Spring Boot 应用的入口。
@SpringBootConfiguration 等同于 @Configuration,标记当前类是“配置类”,允许通过 @Bean 注册 Bean。
@EnableAutoConfiguration 开启“自动配置”功能,扫描并加载 META-INF/spring.factories 中的自动配置类。
@ComponentScan 扫描当前包及其子包下的 @Component@Service@Controller 等注解,将类注册为 Bean。
@Configuration 标记类为配置类,替代传统 XML 配置文件,内部可通过 @Bean 定义 Bean。
@Bean 在配置类中定义 Bean,等同于 XML 中的 <bean> 标签,默认 Bean 名称为方法名。
@Value 注入外部配置(如 application.yml 中的值),支持 SpEL 表达式(如 ${server.port})。
@ConfigurationProperties 将外部配置(如 application.yml)批量绑定到 Java 类中(需配合 @Component@EnableConfigurationProperties)。

二、核心原理:自动配置

自动配置是 Spring Boot 的“灵魂”,面试中高频考察“如何实现自动配置”“如何自定义自动配置”。

1. Spring Boot 自动配置的核心流程是什么?

核心逻辑:根据 classpath 下的依赖(Jar 包),自动加载并初始化对应的配置类,注册所需 Bean 到 Spring 容器,流程如下:

  1. 启动触发:启动类标注 @SpringBootApplication,其中 @EnableAutoConfiguration 是自动配置的入口。
  2. 加载配置类@EnableAutoConfiguration 会通过 SpringFactoriesLoader 扫描所有 Jar 包下的 META-INF/spring.factories 文件,读取其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置类(如 WebMvcAutoConfigurationDataSourceAutoConfiguration)。
  3. 条件筛选:自动配置类上通常标注 @Conditional 系列注解(如 @ConditionalOnClass@ConditionalOnMissingBean),只有满足条件(如 classpath 存在某类、容器中不存在某 Bean)时,该配置类才会生效。
  4. 注册 Bean:生效的配置类通过 @Bean 注册所需的 Bean 到 Spring 容器(如 WebMvcAutoConfiguration 注册 DispatcherServlet)。
  5. 配置绑定:通过 @ConfigurationPropertiesapplication.yml 中的配置(如 spring.mvc 前缀的配置)绑定到自动配置类的属性上,实现“配置自定义”。

2. @Conditional 系列注解有哪些?作用是什么?

@Conditional 是“条件注解”,用于控制配置类/Bean 的生效条件,是自动配置的“开关”,常用子类:

注解 生效条件
@ConditionalOnClass 当 classpath 中存在指定类时生效(如 @ConditionalOnClass(Servlet.class))。
@ConditionalOnMissingClass 当 classpath 中不存在指定类时生效。
@ConditionalOnBean 当 Spring 容器中存在指定 Bean 时生效(如 @ConditionalOnBean(DataSource.class))。
@ConditionalOnMissingBean 当 Spring 容器中不存在指定 Bean 时生效(允许用户自定义 Bean 覆盖默认配置)。
@ConditionalOnProperty 当外部配置(如 application.yml)中存在指定属性,且值匹配时生效(如 @ConditionalOnProperty(prefix = "spring.datasource", name = "enabled", havingValue = "true"))。
@ConditionalOnWebApplication 当应用是 Web 应用(Servlet 或 Reactive)时生效。

3. 如何自定义一个 Spring Boot Starter?

Starter 是 Spring Boot 的“依赖封装”,本质是“一组依赖 + 自动配置类”,让用户引入一个 Starter 即可使用某功能(如自定义 mybatis-starter)。自定义步骤如下:

步骤 1:创建 2 个模块(或 1 个模块包含 2 部分)
  • Starter 模块:仅包含依赖管理(pom.xml),不写代码,负责引入核心功能模块和 Spring Boot 自动配置依赖。
  • AutoConfigure 模块:包含自动配置类、Bean 定义、spring.factories 文件,是 Starter 的核心逻辑。
步骤 2:配置 Starter 模块的 pom.xml

引入 AutoConfigure 模块和 Spring Boot 自动配置依赖:

<dependencies>
    <!-- 引入自动配置模块 -->
    <dependency>
        <groupId>com.example</groupId>
        <artifactId>my-starter-autoconfigure</artifactId>
        <version>1.0.0</version>
    </dependency>
    <!-- Spring Boot 自动配置基础依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
    <!-- 配置绑定依赖(可选,如需读取 application.yml) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
步骤 3:编写 AutoConfigure 模块的核心逻辑
  1. 配置属性类:通过 @ConfigurationProperties 绑定外部配置(如 application.yml 中的 my.starter 前缀配置):

    @ConfigurationProperties(prefix = "my.starter")
    public class MyStarterProperties {
        private String name = "default"; // 默认值
        private int timeout = 3000;
        // getter/setter
    }
    
  2. 自动配置类:通过 @Configuration@Conditional 定义 Bean,依赖配置属性类:

    @Configuration
    @EnableConfigurationProperties(MyStarterProperties.class) // 启用配置属性绑定
    @ConditionalOnClass(MyService.class) // 当 classpath 存在 MyService 时生效
    public class MyStarterAutoConfiguration {
        private final MyStarterProperties properties;
    
        // 构造注入配置属性
        public MyStarterAutoConfiguration(MyStarterProperties properties) {
            this.properties = properties;
        }
    
        // 注册自定义 Bean,依赖配置属性
        @Bean
        @ConditionalOnMissingBean // 允许用户自定义 Bean 覆盖
        public MyService myService() {
            MyService service = new MyService();
            service.setName(properties.getName());
            service.setTimeout(properties.getTimeout());
            return service;
        }
    }
    
  3. 创建 spring.factories 文件:在 src/main/resources/META-INF/ 下创建该文件,指定自动配置类:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.example.my.starter.MyStarterAutoConfiguration
    
步骤 4:使用自定义 Starter

其他项目引入 Starter 依赖后,即可直接注入 MyService,并通过 application.yml 配置 my.starter.name 等属性。

4. 如何禁用某个自动配置类?

有 2 种常用方式:

  1. 通过 @SpringBootApplicationexclude 属性(启动类上):

    // 禁用 DataSourceAutoConfiguration(避免自动配置数据源)
    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
  2. 通过外部配置 spring.autoconfigure.excludeapplication.yml 中):

    spring:
      autoconfigure:
        exclude:
          - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
          - org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
    

三、配置相关

1. Spring Boot 的配置文件有哪些格式?优先级如何?

Spring Boot 支持 3 种配置文件格式,用于外部化配置(如端口、数据库连接):

  • application.properties:键值对格式(server.port=8080),兼容性好。
  • application.yml:YAML 格式(缩进敏感,server: port: 8080),结构清晰,推荐使用。
  • application.yaml:与 application.yml 完全一致,只是文件名不同。
配置文件优先级(从高到低,高优先级覆盖低优先级):
  1. 命令行参数(如 java -jar app.jar --server.port=8081)。
  2. 系统环境变量(如 OS 的 SPRING_DATASOURCE_URL)。
  3. 项目内配置文件:
    • file:./config/(项目根目录下的 config 文件夹)。
    • file:./(项目根目录)。
    • classpath:/config/(resources 下的 config 文件夹)。
    • classpath:/(resources 根目录,默认位置)。
  4. 特殊配置(如 application-{profile}.yml,需结合 spring.profiles.active 激活)。

2. @Value@ConfigurationProperties 的区别?如何选择?

两者都用于注入外部配置,但适用场景不同:

对比维度 @Value @ConfigurationProperties
注入方式 单个属性注入(需逐个标注)。 批量注入(绑定整个前缀的配置,如 spring.datasource)。
支持类型 基本类型(String、int 等)、SpEL 表达式。 支持复杂类型(对象、集合、Map 等),自动类型转换。
校验 不支持 JSR-380 校验(如 @NotNull)。 支持 JSR-380 校验(需配合 @Validated)。
松散绑定 不支持(如 myName 无法绑定 my-name)。 支持松散绑定(myNamemy-nameMY_NAME 均可绑定)。
适用场景 简单配置(如单个端口、单个密钥)。 复杂配置(如数据源、Redis、自定义 Starter 配置)。

选择建议

  • 注入单个简单配置 → 使用 @Value(如 @Value("${server.port}"))。
  • 注入一组相关配置(如数据源的 url、username、password)→ 使用 @ConfigurationProperties

3. 什么是 Spring Boot Profile?如何使用?

Profile 是 Spring Boot 的“环境隔离”功能,用于区分开发(dev)、测试(test)、生产(prod) 等环境的配置,避免配置文件冲突。

使用步骤:
  1. 创建环境专属配置文件

    • 开发环境:application-dev.yml
    • 测试环境:application-test.yml
    • 生产环境:application-prod.yml
      (核心配置仍可放在 application.yml 中,环境专属配置覆盖核心配置)。
  2. 激活 Profile

    • 方式 1:配置文件激活(application.yml 中):
      spring:
        profiles:
          active: dev # 激活开发环境
      
    • 方式 2:命令行激活(优先级更高):
      java -jar app.jar --spring.profiles.active=prod
      
    • 方式 3:代码激活(启动类中):
      public static void main(String[] args) {
          SpringApplication app = new SpringApplication(Application.class);
          app.setAdditionalProfiles("test"); // 激活测试环境
          app.run(args);
      }
      
  3. Profile 注解:通过 @Profile 控制 Bean/配置类在指定环境生效:

    @Configuration
    @Profile("prod") // 仅生产环境生效
    public class ProdDataSourceConfig {
        // 生产环境数据源配置
    }
    

四、Web 开发相关

1. Spring Boot 如何整合 Spring MVC?需要手动配置吗?

无需手动配置,引入 spring-boot-starter-web 依赖后,Spring Boot 会自动完成 Spring MVC 的核心配置:

  • 自动配置 DispatcherServlet(前端控制器),并映射到 / 路径。
  • 自动配置 CharacterEncodingFilter(解决中文乱码,默认 UTF-8)。
  • 自动配置 ViewResolver(视图解析器,支持 JSP、Thymeleaf 等)。
  • 自动配置静态资源映射(classpath:/static/classpath:/public/ 等目录下的资源可直接访问)。
如需自定义 Spring MVC 配置(如拦截器、视图解析器),有 2 种方式:
  1. 实现 WebMvcConfigurer 接口(推荐)

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
        // 自定义拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoginInterceptor())
                    .addPathPatterns("/**") // 拦截所有路径
                    .excludePathPatterns("/login"); // 排除登录路径
        }
    
        // 自定义视图解析器
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix("/WEB-INF/views/");
            resolver.setSuffix(".jsp");
            registry.viewResolver(resolver);
        }
    }
    
  2. 使用 @EnableWebMvc 注解(谨慎使用)
    标注 @EnableWebMvc完全覆盖 Spring Boot 的自动配置,需手动配置所有 Spring MVC 组件(如 DispatcherServlet、静态资源映射),仅在需要完全自定义时使用。

2. Spring Boot 如何处理跨域(CORS)问题?

跨域是浏览器的安全限制(不同域名/端口/协议的请求被拦截),Spring Boot 有 3 种常用解决方案:

方案 1:通过 WebMvcConfigurer 全局配置
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 允许跨域的路径(所有路径)
                .allowedOrigins("http://localhost:8081") // 允许的源(前端地址)
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
                .allowedHeaders("*") // 允许的请求头
                .allowCredentials(true) // 是否允许携带 Cookie
                .maxAge(3600); // 预检请求(OPTIONS)的缓存时间(秒)
    }
}
方案 2:使用 @CrossOrigin 注解(局部配置)

在 Controller 或方法上标注,仅对当前 Controller/方法生效:

@RestController
@CrossOrigin(origins = "http://localhost:8081", maxAge = 3600)
public class UserController {
    @GetMapping("/users")
    public List<User> getUsers() {
        // ...
    }
}
方案 3:自定义 CORS 过滤器
@Component
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.setHeader("Access-Control-Allow-Origin", "http://localhost:8081");
        res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
        res.setHeader("Access-Control-Allow-Headers", "*");
        res.setHeader("Access-Control-Allow-Credentials", "true");
        chain.doFilter(request, response);
    }
}

3. Spring Boot 如何实现统一异常处理?

通过 @ControllerAdvice + @ExceptionHandler 实现全局统一异常处理,避免在每个 Controller 中重复捕获异常。

步骤:
  1. 创建全局异常处理类

    @ControllerAdvice // 全局Controller增强,捕获所有Controller的异常
    @ResponseBody // 返回JSON格式响应(如用@RestControllerAdvice可省略)
    public class GlobalExceptionHandler {
        // 处理特定异常(如空指针异常)
        @ExceptionHandler(NullPointerException.class)
        public Result handleNullPointerException(NullPointerException e) {
            log.error("空指针异常:", e);
            return Result.fail(500, "服务器内部错误:空指针");
        }
    
        // 处理自定义异常(如业务异常)
        @ExceptionHandler(BusinessException.class)
        public Result handleBusinessException(BusinessException e) {
            log.error("业务异常:", e);
            return Result.fail(e.getCode(), e.getMessage());
        }
    
        // 处理所有未捕获的异常(兜底)
        @ExceptionHandler(Exception.class)
        public Result handleException(Exception e) {
            log.error("未知异常:", e);
            return Result.fail(500, "服务器内部错误,请联系管理员");
        }
    }
    
  2. 定义统一响应类(Result)

    public class Result<T> {
        private int code; // 状态码(200=成功,500=失败)
        private String message; // 提示信息
        private T data; // 响应数据
    
        // 成功静态方法
        public static <T> Result<T> success(T data) {
            Result<T> result = new Result<>();
            result.setCode(200);
            result.setMessage("success");
            result.setData(data);
            return result;
        }
    
        // 失败静态方法
        public static <T> Result<T> fail(int code, String message) {
            Result<T> result = new Result<>();
            result.setCode(code);
            result.setMessage(message);
            return result;
        }
    
        // getter/setter
    }
    

五、数据访问相关

1. Spring Boot 如何整合 MyBatis?

MyBatis 是常用的 ORM 框架,Spring Boot 通过 mybatis-spring-boot-starter 简化整合:

步骤 1:引入依赖(pom.xml)
<dependencies>
    <!-- MyBatis Starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.3.1</version> <!-- 需与Spring Boot版本兼容 -->
    </dependency>
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- 数据源(HikariCP,Spring Boot默认) -->
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
</dependencies>
步骤 2:配置数据源和 MyBatis(application.yml)
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    type: com.zaxxer.hikari.HikariDataSource # 默认数据源,可省略

mybatis:
  mapper-locations: classpath:mapper/**/*.xml # MyBatis映射文件路径
  type-aliases-package: com.example.demo.entity # 实体类包路径(简化XML中的类名)
  configuration:
    map-underscore-to-camel-case: true # 开启下划线转驼峰(如user_name → userName)
步骤 3:编写 Mapper 接口

通过 @Mapper 注解或 @MapperScan 扫描 Mapper 接口:

// 方式1:在接口上标注@Mapper
@Mapper
public interface UserMapper {
    List<User> selectAll();
    User selectById(Long id);
    int insert(User user);
}

// 方式2:在启动类上标注@MapperScan(扫描所有Mapper接口,推荐)
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // Mapper接口所在包
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
步骤 4:编写 MyBatis 映射文件(XML)

resources/mapper/ 下创建 UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <select id="selectAll" resultType="User">
        select id, user_name, age from user
    </select>

    <select id="selectById" parameterType="Long" resultType="User">
        select id, user_name, age from user where id = #{id}
    </select>
</mapper>
步骤 5:Service 层调用 Mapper
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public List<User> getAllUsers() {
        return userMapper.selectAll();
    }
}

2. Spring Boot 默认的数据源是什么?如何切换数据源(如 Druid)?

(1)默认数据源:HikariCP

Spring Boot 2.x 及以上版本默认使用 HikariCP 作为数据源,原因是它是目前性能最优的 JDBC 连接池(启动快、内存占用低、并发性能好)。无需额外引入依赖(spring-boot-starter-jdbcmybatis-spring-boot-starter 已包含)。

(2)切换为 Druid 数据源

Druid 是阿里开源的数据源,支持监控、SQL 拦截、防注入等功能,切换步骤如下:

  1. 引入 Druid Starter 依赖

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.16</version>
    </dependency>
    
  2. 配置 Druid 数据源(application.yml)

    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource # 指定数据源类型
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC
        username: root
        password: 123456
        # Druid 特有配置
        druid:
          initial-size: 5 # 初始化连接数
          min-idle: 5 # 最小空闲连接数
          max-active: 20 # 最大活跃连接数
          max-wait: 60000 # 获取连接的最大等待时间(毫秒)
          time-between-eviction-runs-millis: 60000 # 检测间隔(毫秒)
          min-evictable-idle-time-millis: 300000 # 连接最小空闲时间(毫秒)
          validation-query: SELECT 1 FROM DUAL # 验证连接的SQL
          test-while-idle: true # 空闲时检测
          test-on-borrow: false # 借出时检测(影响性能)
          test-on-return: false # 归还时检测(影响性能)
          pool-prepared-statements: true # 开启PSCache
          max-pool-prepared-statement-per-connection-size: 20 # PSCache大小
          # 配置监控统计拦截的filters
          filters: stat,wall,log4j2 # stat=监控统计,wall=防SQL注入,log4j2=日志
          # 监控页面配置
          stat-view-servlet:
            enabled: true # 启用监控页面
            url-pattern: /druid/* # 监控页面访问路径
            login-username: admin # 监控页面登录用户名
            login-password: 123456 # 监控页面登录密码
    
  3. 访问 Druid 监控页面:启动项目后,访问 http://localhost:8080/druid,输入配置的用户名密码即可查看数据源监控、SQL 执行情况等。

六、生产级特性

1. Spring Boot Actuator 是什么?如何使用?

Actuator 是 Spring Boot 提供的“生产级监控工具”,通过 HTTP 端点暴露应用的健康状态、 metrics 指标、环境配置、Bean 信息等,帮助运维人员监控应用。

使用步骤:
  1. 引入 Actuator 依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
  2. 配置 Actuator 端点(application.yml)

    management:
      endpoints:
        web:
          exposure:
            include: "*" # 暴露所有Web端点(生产环境建议仅暴露必要端点,如health,info)
            exclude: env,beans # 排除敏感端点
      endpoint:
        health:
          show-details: always # 显示健康状态详情(如数据库、Redis的健康状态)
        shutdown:
          enabled: true # 启用关闭端点(需谨慎,生产环境不建议开启)
    
  3. 常用端点说明

    • /actuator/health:应用健康状态(UP/DOWN,可扩展监控数据源、Redis等)。
    • /actuator/info:应用信息(需配置 info 前缀的属性,如 info.app.name=demo)。
    • /actuator/metrics:应用 metrics 指标(如 JVM 内存、CPU 使用率、HTTP 请求数)。
    • /actuator/env:环境配置(如系统变量、配置文件属性)。
    • /actuator/beans:Spring 容器中所有 Bean 的信息。
    • /actuator/shutdown:POST 请求关闭应用(需开启 shutdown.enabled=true)。

2. Spring Boot 如何实现热部署?

热部署指“修改代码后无需重启应用,即可生效”,提高开发效率,常用方案是 Spring Boot DevTools

步骤:
  1. 引入 DevTools 依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional> <!-- 防止依赖传递 -->
    </dependency>
    
  2. 配置 IDE(以 IDEA 为例)

    • 开启自动编译:File → Settings → Build, Execution, Deployment → Compiler → 勾选 Build project automatically
    • 开启运行时自动编译:按 Ctrl + Shift + Alt + / → 选择 Registry → 勾选 compiler.automake.allow.when.app.running
  3. 原理
    DevTools 采用“双类加载器”机制:

    • Base ClassLoader:加载不变的类(如第三方 Jar 包)。
    • Restart ClassLoader:加载项目自定义的类(如 Controller、Service)。
      当代码修改时,仅重启 Restart ClassLoader,无需重启整个应用,实现热部署。
注意:
  • DevTools 仅用于开发环境,生产环境需排除(可通过 Profile 控制)。
  • 部分修改(如配置文件、静态资源)无需重启,DevTools 会自动刷新。

七、性能优化

1. Spring Boot 应用的常见性能优化手段有哪些?

从“配置、代码、依赖、JVM”等维度优化,核心手段如下:

  1. 优化数据源

    • 使用高性能数据源(如 HikariCP,默认),避免使用低性能的 C3P0。
    • 合理配置连接池参数(initial-sizemax-activemax-wait 等),避免连接泄露或连接数不足。
  2. 优化嵌入式容器

    • 切换为 Undertow 容器(比 Tomcat 性能更优,支持异步 IO):
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
          <exclusions>
              <exclusion>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-tomcat</artifactId>
              </exclusion>
          </exclusions>
      </dependency>
      <!-- 引入Undertow -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-undertow</artifactId>
      </dependency>
      
    • 配置容器线程池(如 Tomcat 的 max-threadsmin-spare-threads)。
  3. 优化 JVM 参数

    • 合理设置堆内存(-Xms-Xmx,建议相同值,避免内存波动)。
    • 启用 G1 垃圾收集器(-XX:+UseG1GC,适合大内存应用)。
    • 示例:java -jar -Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 app.jar
  4. 代码与框架优化

    • 避免频繁创建对象(如使用线程池、连接池、对象池)。
    • 使用缓存(如 Redis、Caffeine)减少数据库查询。
    • 减少 HTTP 请求(如接口聚合、压缩响应数据)。
    • 避免循环依赖(Spring 虽支持,但影响性能和代码可读性)。
  5. 依赖优化

    • 排除无用依赖(如 spring-boot-starter-web 中排除 Tomcat 切换为 Undertow)。
    • 使用最新稳定版本的依赖(修复已知性能问题)。
  6. 开启缓存与压缩

    • 开启 HTTP 响应压缩(server.compression.enabled=true)。
    • 静态资源使用 CDN 或缓存(设置 Cache-Control 头)。

2. 如何解决 Spring Boot 应用的循环依赖问题?

循环依赖指“两个或多个 Bean 互相依赖”(如 A 依赖 B,B 依赖 A),Spring 默认支持基于 setter 注入或字段注入的循环依赖,但不支持基于构造器注入的循环依赖(会抛 BeanCurrentlyInCreationException)。

(1)循环依赖的产生场景
// 场景1:构造器注入循环依赖(Spring 无法解决)
@Service
public class AService {
    private BService bService;
    // 构造器注入 BService
    @Autowired
    public AService(BService bService) {
        this.bService = bService;
    }
}

@Service
public class BService {
    private AService aService;
    // 构造器注入 AService
    @Autowired
    public BService(AService aService) {
        this.aService = aService;
    }
}
(2)解决方案
  1. 改用 setter 注入或字段注入(推荐):

    @Service
    public class AService {
        private BService bService;
        // setter注入
        @Autowired
        public void setBService(BService bService) {
            this.bService = bService;
        }
    }
    
    @Service
    public class BService {
        // 字段注入
        @Autowired
        private AService aService;
    }
    
  2. 使用 @Lazy 注解(延迟加载)
    对其中一个 Bean 延迟初始化,打破循环依赖:

    @Service
    public class AService {
        private BService bService;
        // 对 BService 延迟加载
        @Autowired
        public AService(@Lazy BService bService) {
            this.bService = bService;
        }
    }
    
    @Service
    public class BService {
        private AService aService;
        @Autowired
        public BService(AService aService) {
            this.aService = aService;
        }
    }
    
  3. 重构代码(从根源解决)
    提取循环依赖的公共逻辑到新的 Bean(如 CService),让 A 和 B 都依赖 C,而非互相依赖:

    // 提取公共逻辑到 CService
    @Service
    public class CService {
        // 公共方法
        public void commonMethod() { ... }
    }
    
    @Service
    public class AService {
        private CService cService;
        @Autowired
        public AService(CService cService) {
            this.cService = cService;
        }
    }
    
    @Service
    public class BService {
        private CService cService;
        @Autowired
        public BService(CService cService) {
            this.cService = cService;
        }
    }
    

八、其他高频问题

1. Spring Boot 中的 Bean 作用域有哪些?

Spring Bean 的作用域决定了 Bean 的创建时机和生命周期,Spring Boot 支持 6 种作用域:

作用域 说明 适用场景
singleton(默认) 整个 Spring 容器中,Bean 仅创建一个实例,全局共享。 无状态 Bean(如 Service、Dao)。
prototype 每次请求 Bean(如 getBean()@Autowired)时,都创建一个新实例。 有状态 Bean(如 Command、Request)。
request 每个 HTTP 请求创建一个新 Bean,请求结束后销毁。 Web 应用中,绑定请求数据的 Bean。
session 每个 HTTP Session 创建一个新 Bean,Session 过期后销毁。 Web 应用中,绑定用户会话的 Bean。
application 整个 Web 应用(ServletContext)中,Bean 仅创建一个实例。 Web 应用中,全局共享的 Bean(如应用配置)。
websocket 每个 WebSocket 会话创建一个新 Bean,WebSocket 关闭后销毁。 WebSocket 应用。
如何设置作用域?

通过 @Scope 注解:

@Service
@Scope("prototype") // 设置为 prototype 作用域
public class UserService {
    // ...
}

2. Spring Boot 如何实现定时任务?

Spring Boot 通过 @EnableScheduling + @Scheduled 简化定时任务开发,无需集成 Quartz(简单场景)。

步骤:
  1. 启动类开启定时任务

    @SpringBootApplication
    @EnableScheduling // 开启定时任务支持
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
  2. 编写定时任务方法
    在 Bean 类(如 Service)中,用 @Scheduled 标注定时方法:

    @Service
    public class ScheduledService {
        // 1. 固定延迟执行(上一次任务结束后,延迟 5 秒执行下一次)
        @Scheduled(fixedDelay = 5000)
        public void fixedDelayTask() {
            System.out.println("固定延迟任务:" + new Date());
        }
    
        // 2. 固定速率执行(上一次任务开始后,间隔 5 秒执行下一次,可能并发)
        @Scheduled(fixedRate = 5000)
        public void fixedRateTask() {
            System.out.println("固定速率任务:" + new Date());
        }
    
        // 3. 初始延迟后执行(项目启动后延迟 3 秒,再固定速率执行)
        @Scheduled(initialDelay = 3000, fixedRate = 5000)
        public void initialDelayTask() {
            System.out.println("初始延迟任务:" + new Date());
        }
    
        // 4. Cron 表达式(灵活配置,如每天 0 点执行)
        @Scheduled(cron = "0 0 0 * * ?")
        public void cronTask() {
            System.out.println("Cron 任务:" + new Date());
        }
    }
    
Cron 表达式格式:

秒 分 时 日 月 周 [年](年可选),常用示例:

  • 0 0 0 * * ?:每天 0 点执行。
  • 0 0/30 * * * ?:每 30 分钟执行一次。
  • 0 15 10 ? * MON-FRI:每周一到周五 10:15 执行。

3. Spring Boot 如何实现异步任务?

通过 @EnableAsync + @Async 实现异步任务,避免同步任务阻塞主线程(如发送短信、邮件等耗时操作)。

步骤:
  1. 启动类开启异步支持

    @SpringBootApplication
    @EnableAsync // 开启异步任务支持
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
  2. 编写异步任务方法
    在 Bean 类的方法上标注 @Async,该方法会在独立线程中执行:

    @Service
    public class AsyncService {
        // 异步方法(无返回值)
        @Async
        public void sendSms(String phone) {
            try {
                Thread.sleep(3000); // 模拟耗时操作(发送短信)
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("短信发送成功:" + phone);
        }
    
        // 异步方法(有返回值,需返回 Future)
        @Async
        public CompletableFuture<String> sendEmail(String email) {
            try {
                Thread.sleep(2000); // 模拟耗时操作(发送邮件)
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String result = "邮件发送成功:" + email;
            return CompletableFuture.completedFuture(result);
        }
    }
    
  3. 调用异步方法

    @RestController
    public class AsyncController {
        @Autowired
        private AsyncService asyncService;
    
        @GetMapping("/send")
        public String send() {
            long start = System.currentTimeMillis();
    
            // 调用异步方法(不阻塞主线程)
            asyncService.sendSms("13800138000");
            CompletableFuture<String> emailFuture = asyncService.sendEmail("test@example.com");
    
            // 等待异步方法结果(可选)
            try {
                String emailResult = emailFuture.get(); // 阻塞等待结果
                System.out.println(emailResult);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
    
            long end = System.currentTimeMillis();
            return "总耗时:" + (end - start) + "ms"; // 耗时约 2000ms(取最长的异步任务时间)
        }
    }
    
注意:
  • @Async 标注的方法必须在不同的 Bean 中调用(如果在同一个 Bean 中调用,异步不生效,因为 Spring AOP 基于代理,同类方法调用不会走代理)。
  • 可自定义线程池(实现 AsyncConfigurer 接口),避免使用默认线程池(SimpleAsyncTaskExecutor,每次创建新线程,性能差)。
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐