【Java Web 快速入门】十一、Spring Boot 原理

目录

  • [Spring Boot 原理](#Spring Boot 原理)
    • 配置优先级
    • [Bean 管理](#Bean 管理)
      • [获取 Bean](#获取 Bean)
      • [Bean 的作用域](#Bean 的作用域)
      • [第三方 Bean](#第三方 Bean)
    • [Spring Boot 底层原理](#Spring Boot 底层原理)

Spring Boot 原理

配置优先级

Spring Boot 中支持三种格式的配置文件:

  • application.properties

    yaml 复制代码
    server.port=8081
  • application.yml

    yaml 复制代码
    server:
    	port: 8082
  • application.yaml

    yaml 复制代码
    server:
    	port: 8083

在代码中配置这三个文件,运行程序的结果如下;

Tomcat 服务器在 8081 端口运行

如果只配置 application.yml 和 application.yaml,程序运行结果如下:

Tomcat 服务器在 8082 端口运行

通过测试同一属性在三个文件中的配置,得出优先级:properties 最高,yml 次之,yaml 最低。

Spring Boot 除了支持配置文件属性配置,还支持 Java 系统属性和命令行参数的方式进行属性配置

  • Java系统属性:-Dserver.port=9000
  • 命令行参数:--server.port=9001

命令行参数优先级高于 Java 系统属性。

在 Spring Boot 项目打包后,若需配置属性(如端口号等),可通过 Java 系统属性命令行参数两种方式进行,具体操作如下:

  1. 项目打包前提

    • 打包需执行 Maven 的package生命周期,生成可运行的 jar 包。

    • 注意:Spring Boot 项目打包必须依赖spring-boot-maven-plugin插件,基于官方骨架创建的项目会自动引入该插件,无需手动添加。

  2. 运行打包后的 jar 包

    • 基本命令:

      bash 复制代码
      java -jar 项目名.jar
    • (例如:java -jar spring-boot-web-config.jar,可通过tab键自动补全 jar 包名称)

  3. 配置 Java 系统属性

    • 格式 :在java命令后、-jar之前,使用-Dkey=value格式配置。

    • 示例:配置 Tomcat 端口号为 9000

      bash 复制代码
      java -Dserver.port=9000 -jar spring-boot-web-config.jar
  4. 配置命令行参数

    • 格式 :在 jar 包名称之后,使用--key=value格式配置。

    • 示例:配置 Tomcat 端口号为 10010

      bash 复制代码
      java -jar spring-boot-web-config.jar --server.port=10010

五种配置方式的优先级:从高到低依次为命令行参数、Java 系统属性、properties 配置文件、yml 配置文件、yaml 配置文件。

Bean 管理

获取 Bean

默认情况下,Spring 项目启动时,会把 Bean 都创建好放在 IOC 容器中,如果想要主动获取这些 Bean,可以通过以下方式:

  • 根据 name 获取 Bean:Object getBean(String name)
  • 根据类型获取 Bean:<T> T getBean(Class<T> requiredType)
  • 根据 name 获取 Bean(带类型转换):<T> T getBean(String name, Class<T> requiredType)

在测试类中加入以下代码进行获取 Bean 的测试:

java 复制代码
@Autowired
// 获取ApplicationContext对象
private ApplicationContext applicationContext;
@Test
public void testGetBean(){

  // 根据Bean的名称获取
  DeptController bean1 = (DeptController) applicationContext.getBean("deptController");
  System.out.println(bean1);

  // 根据Bean的类型获取
  DeptController bean2 = applicationContext.getBean(DeptController.class);
  System.out.println(bean2);

  // 根据Bean的名称和类型获取
  DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
  System.out.println(bean3);
}

测试运行结果如下:

三次调用 getBean 方法获取同一 bean 对象,其地址值相同,说明默认情况下 bean 是单例的。

Spring 项目启动时创建所有 bean 对象并放在 IOC 容器中,这仅针对默认情况下单例且非延迟加载的 bean,bean 的创建时间还受作用域和延迟初始化影响

Bean 的作用域

bean 是单例还是多例取决于其作用域,Spring 中 bean 支持五种作用域,重点关注前两种,后三种在 web 环境生效:

作用域 说明
singleton 在整个 Spring 容器中,同名称的 bean 对象只有一个实例,即单例,是作用域的默认值
prototype 代表非单例,每一次使用该 bean 对象时,都会创建一个新的实例对象
request 代表每一次请求对应一个实例对象
session 代表每一次会话对应一个新的实例对象
application 代表每一个应用对应一个实例对象

默认情况下,未设置作用域的 bean 是单例的,在 Spring 项目启动、IOC 容器创建时就会实例化并放到容器中,多次获取的是同一个对象。

设置 bean 作用域的方式:可以借助 Spring 中的 @Scope 注解来配置 bean 的作用域。

在要获取 Bean 的类上添加 @Scope("prototype"),运行前面的测试代码,结果如下:

延迟初始化注解 @lazy:在类上添加 @lazy 注解后,bean 会延迟初始化,延迟到第一次使用的时候实例化。

注意事项:

  • 默认作用域为 singleton,默认单例 bean 在容器启动时创建,可通过 @lazy 延迟到第一次使用时创建
  • prototype 非单例 bean 每次使用都会创建新实例

第三方 Bean

第三方 Bean 配置的必要性:项目中引入的第三方依赖提供的类(如 dom4j 中的 SAXReader),若每次使用都新建对象会耗费资源,需交给 Spring 的 IOC 容器管理,通过依赖注入使用。

第三方 Bean 配置的特殊之处:第三方类是只读的,无法直接在类上添加 @Component 及其衍生注解声明为 bean,需使用 @Bean 注解。

@Bean 注解的使用方法:在方法上添加 @Bean 注解,方法返回值为要管理的第三方 Bean 对象,Spring 会将方法返回值交给 IOC 容器管理,后续可通过 @Autowired 注入使用。

第三方 Bean 配置的位置:建议单独定义配置类(用 @Configuration 标识),在配置类中集中配置第三方 bean。

第三方 Bean 的名称规则:可通过 @Bean 的 name 或 value 属性指定名称,二者互为别名;未指定时,默认名称为方法名。

第三方 Bean 声明时的依赖注入:在定义第三方 bean 的方法中声明对应类型的形参,Spring 容器会根据类型自动装配 IOC 容器中的对应 Bean 对象。

Spring Boot 底层原理

Spring Boot 简化开发的原因:底层提供起步依赖和自动配置两个重要功能。起步依赖简化 pom 文件依赖配置,解决 Spring 框架依赖配置繁琐问题;自动配置简化框架使用时 Bean 的声明和配置,引入起步依赖后常见配置已存在,可直接使用。

起步依赖

以 Web 程序开发为例,使用 Spring 框架需引入多个依赖且版本需匹配,而使用 Spring Boot 只需引入对应的起步依赖(如 web 开发的 spring-boot-starter-web,aop 开发的 spring-boot-starter-aop)。其原理是 Maven 的依赖传递,起步依赖集成了开发所需的常见依赖,引入一个起步依赖后,其他依赖会通过依赖传递自动引入(若 A 依赖 B,B 依赖 C,C 依赖 D,引入 A 则 B、C、D 也会被引入)。

自动配置

自动配置的定义:指 Spring Boot 项目启动时,除了用户自己定义的 Bean 对象外,Spring Boot 会自动创建一些内置的配置类及 Bean 对象并放入 IOC 容器,使用户在开发时无需手动声明即可直接使用,简化开发,省去繁琐配置。

通过启动 Spring Boot 工程,在控制台的 Bean 中可查看所有 Bean 对象及配置类,包括用户自己定义的和 Spring Boot 自动加载的配置类及其生成的 Bean 对象

核心原理

自动配置的实现依赖 3 个关键机制:@EnableAutoConfiguration 注解SPI 机制(META-INF/spring.factories)条件注解(@Conditional)。三者协同工作,流程如下:

  1. 触发点:@SpringBootApplication 注解

SpringBoot 项目的启动类通常标注@SpringBootApplication,它是一个组合注解,包含 3 个核心注解:

  • @SpringBootConfiguration:标记当前类为配置类(类似@Configuration)。
  • @ComponentScan:默认扫描启动类所在包及其子包(但自动配置不依赖它,而是通过其他机制加载外部包的 Bean)。
  • @EnableAutoConfiguration自动配置的 "开关",正是这个注解触发了后续的自动配置流程。
  1. 核心:@EnableAutoConfiguration 的作用

@EnableAutoConfiguration通过@Import(AutoConfigurationImportSelector.class)导入了AutoConfigurationImportSelector类,这个类是自动配置的 "核心执行者",主要做两件事:

  • 加载候选配置类 :扫描类路径下所有META-INF/spring.factories文件,读取其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置类全类名(这些是 "候选自动配置类")。
  • 筛选有效配置类 :通过spring.factories加载的候选配置类会经过条件注解(如@ConditionalOnClass)的筛选,只有满足条件的配置类才会被真正加载到 IOC 容器。
  1. SPI 机制:META-INF/spring.factories 的作用

META-INF/spring.factories是 Java 的 SPI(Service Provider Interface)机制在 SpringBoot 中的应用,用于声明 "自动配置类" 的位置

格式如下(key 固定为org.springframework.boot.autoconfigure.EnableAutoConfiguration,value 为自动配置类的全类名列表):

properties 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration,\
com.example.OtherAutoConfiguration

当项目启动时,AutoConfigurationImportSelector会读取所有依赖中的spring.factories,收集所有声明的自动配置类,作为候选者。

  1. 条件注解:筛选有效的配置类

候选配置类不会全部生效,需要通过条件注解判断是否满足生效条件。常见的条件注解有:

  • @ConditionalOnClass:类路径下存在指定类时,配置类才生效(如引入spring-web依赖后,DispatcherServlet.class存在,Web 相关配置才生效)。
  • @ConditionalOnMissingBean:容器中不存在指定 Bean 时,配置类才生效(允许开发者自定义 Bean 覆盖默认配置)。
  • @ConditionalOnProperty:配置文件中存在指定属性时生效(如server.port配置触发端口绑定)。

只有满足所有条件的配置类,才会被 Spring 实例化,其内部定义的 Bean 才会被注册到 IOC 容器。

总结流程:

  1. 启动类标注@SpringBootApplication,触发@EnableAutoConfiguration
  2. AutoConfigurationImportSelector扫描所有META-INF/spring.factories,收集候选自动配置类。
  3. 候选配置类通过条件注解筛选,保留有效配置类。
  4. 有效配置类被加载,其内部的 Bean(如DataSourceDispatcherServlet)被注册到 IOC 容器。
实例说明

下面通过两个例子(自定义 starter 和 SpringBoot 自带 starter)直观理解自动配置。

例 1:自定义一个 "日志 starter"

假设我们要开发一个my-log-starter,功能是:引入后自动配置一个LogServiceBean,用于打印日志。

步骤 1:定义核心 Bean 和配置类

  • LogService:需要被自动配置的 Bean。

    java 复制代码
    public class LogService {
        public void log(String message) {
            System.out.println("[MyLog] " + message);
        }
    }
  • LogAutoConfiguration :自动配置类,负责注册LogService

    java 复制代码
    @Configuration  // 标记为配置类
    @ConditionalOnClass(LogService.class)  // 类路径存在LogService时生效
    public class LogAutoConfiguration {
        
        // 注册LogService到IOC容器
        @Bean
        @ConditionalOnMissingBean  // 若用户自定义了LogService,则不使用默认的
        public LogService logService() {
            return new LogService();
        }
    }

步骤 2:通过 spring.factories 声明自动配置类

src/main/resources下创建META-INF/spring.factories,声明LogAutoConfiguration为候选配置类:

properties 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.log.LogAutoConfiguration

步骤 3:使用 starter

  • 其他项目引入

    复制代码
    my-log-starter

    依赖后,启动项目时:

    1. AutoConfigurationImportSelector读取spring.factories,发现LogAutoConfiguration
    2. 检查到类路径存在LogService.class(依赖已引入),且容器中没有自定义的LogService,满足条件。
    3. LogAutoConfiguration生效,logService()方法被执行,LogServiceBean 被注册到 IOC 容器。
  • 开发者可直接注入使用:

    java 复制代码
    @RestController
    public class TestController {
        @Autowired
        private LogService logService;  // 直接使用自动配置的Bean
        
        @GetMapping("/test")
        public String test() {
            logService.log("测试日志");  // 输出:[MyLog] 测试日志
            return "ok";
        }
    }
例 2:SpringBoot 自带的 spring-boot-starter-web

引入spring-boot-starter-web后,SpringBoot 会自动配置 Web 开发所需的核心组件(如 Tomcat、DispatcherServlet),原理如下:

  1. 依赖引入starter-web包含spring-webspring-webmvctomcat-embed-core等依赖。
  2. 自动配置类spring-boot-autoconfigure包的META-INF/spring.factories中声明了DispatcherServletAutoConfigurationTomcatAutoConfiguration等配置类。
  3. 条件判断:
    • TomcatAutoConfiguration通过@ConditionalOnClass(Tomcat.class)判断:因引入了tomcat-embed-core,Tomcat 类存在,配置生效,自动启动内嵌 Tomcat。
    • DispatcherServletAutoConfiguration通过@ConditionalOnClass(DispatcherServlet.class)判断:因引入spring-webmvc,DispatcherServlet 类存在,配置生效,注册DispatcherServlet到容器。
  4. 最终效果:开发者无需手动配置 Tomcat 和 DispatcherServlet,引入依赖即可开发 Web 接口。
关键总结
  1. 自动配置的核心是:通过 @EnableAutoConfiguration 触发,SPI 机制加载候选配置类,条件注解筛选有效配置类
  2. 开发者可通过自定义 starter(含spring.factories和自动配置类)实现自动配置,也可通过@ConditionalOnMissingBean等注解覆盖默认配置。
  3. SpringBoot 的 starter(如webdata-jpa)都是基于此原理实现,极大简化了配置流程。
相关推荐
崔庆才丨静觅28 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
曹牧1 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法2 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty7252 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎2 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄2 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea