Spring之我见 - Spring Boot Starter 自动装配原理

news2025/4/21 7:35:24

 欢迎光临小站:致橡树

Spring Boot Starter 的核心设计理念是 约定优于配置,其核心实现基于 自动配置(Auto-Configuration)条件化注册(Conditional Registration)。以下是其生效原理:

约定大于配置

通过预定义合理的默认行为和规范,减少开发者需要手动配置的步骤。比较显著的变化就是减少XML配置。还有一些实际体现如下所示:

  • 项目结构约定

    • 默认目录结构:如 src/main/java 存放代码,src/main/resources 存放配置文件。

    • 配置文件命名application.propertiesapplication.yml 自动被加载,无需显式指定路径。

  • 自动配置(Auto-Configuration)

    • 条件化 Bean 注册:根据类路径依赖(如存在 DataSource 类)自动配置数据库连接池

    • 默认参数值:如嵌入式 Tomcat 默认端口为 8080,无需手动指定。

  • Starter 依赖

    • 依赖聚合:引入 spring-boot-starter-web 即自动包含 Web 开发所需的所有依赖(如 Tomcat、Jackson、Spring MVC)。

    • 开箱即用:无需手动管理版本兼容性。

  • RESTful 路由映射

    • 注解驱动:通过 @GetMapping("/path") 即可定义接口,无需在 XML 中配置路由规则。

自动配置机制

触发阶段:@EnableAutoConfiguration

  • 应用启动时,@SpringBootApplication 组合了 @EnableAutoConfiguration,触发自动配置流程。

  • AutoConfigurationImportSelector 被调用,负责加载所有候选的自动配置类。

public String[] selectImports(AnnotationMetadata metadata) {
  // 1. 加载所有候选自动配置类
  List<String> configurations = getCandidateConfigurations();
  // 2. 去重、过滤、排序
  configurations = removeDuplicates(configurations);
  configurations = filter(configurations, autoConfigurationMetadata);
  return configurations.toArray(new String[0]);
}

加载与筛选:spring.factories

  • 加载所有候选配置类
    从所有 META-INF/spring.factories 文件中读取 EnableAutoConfiguration 对应的配置类。在 Spring Boot 3.x 中,自动配置类的加载方式从 spring.factories 过渡到 AutoConfiguration.imports,并引入了 ImportCandidates 类来处理这一变化。

  • 去重与过滤
    移除重复的配置类,并通过条件注解(如 @ConditionalOnClass ,@ConditionalOnMissingBean ) 有选择的保留当前环境的配置类。

    • @ConditionalOnClass:类路径存在指定类时生效

    • @ConditionalOnMissingBean:容器中不存在指定 Bean 时生效

    • @ConditionalOnProperty:配置属性匹配时生效

  • 排序
    根据 @AutoConfigureOrder@AutoConfigureAfter 调整配置类的加载顺序。

Bean 注册

  • 筛选后的自动配置类被解析为标准的 @Configuration 类。

  • 每个配置类中的 @Bean 方法根据条件注解动态注册 Bean 到 Spring 容器。

编写自定义Spring Boot Starter

项目结构规划

建议分为两个模块:

  1. 自动配置模块:包含核心逻辑和自动配置类(如 hello-spring-boot-autoconfigure)。

  2. Starter模块:空项目,仅作为依赖聚合(如 hello-spring-boot-starter)。

hello-spring-boot-starter-parent(父POM)
├── hello-spring-boot-autoconfigure(自动配置模块)
└── hello-spring-boot-starter(Starter模块)
hello-spring-boot-starter/
├── hello-spring-boot-autoconfigure/
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/com/example/autoconfigure/
│   │   │   │   ├── HelloAutoConfiguration.java
│   │   │   │   ├── HelloProperties.java
│   │   │   │   └── HelloService.java
│   │   │   └── resources/
│   │   │       └── META-INF/
│   │   │           └── spring.factories
│   │   └── test/
│   └── pom.xml
├── hello-spring-boot-starter/
│   └── pom.xml
└── pom.xml

创建自动配置模块(hello-spring-boot-autoconfigure)

添加Maven依赖

<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot 自动配置基础依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
        <version>3.1.5</version>
    </dependency>
    <!-- 可选:配置属性处理 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <version>3.1.5</version>
        <optional>true</optional>
    </dependency>
</dependencies>

定义核心服务类

public class HelloService {
    private String message = "Hello, World!";  // 默认消息

    public String sayHello() {
        return message;
    }

    // Getter和Setter用于通过配置修改message
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }
}

定义配置属性类(可选)

@ConfigurationProperties(prefix = "hello")
public class HelloProperties {
    private String message = "Hello, World!";

    // Getter和Setter
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }
}

编写自动配置类

@Configuration
@EnableConfigurationProperties(HelloProperties.class)  // 启用配置属性
@ConditionalOnClass(HelloService.class)  // 当HelloService在类路径时生效
public class HelloAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean  // 当用户未自定义HelloService时生效
    public HelloService helloService(HelloProperties properties) {
        HelloService service = new HelloService();
        service.setMessage(properties.getMessage());
        return service;
    }
}

注册自动配置

resources/META-INF/ 下创建 spring.factories 文件:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.HelloAutoConfiguration

创建Starter模块(hello-spring-boot-starter)

添加Maven依赖

<!-- pom.xml -->
<dependencies>
    <!-- 引入自动配置模块 -->
    <dependency>
        <groupId>com.example</groupId>
        <artifactId>hello-spring-boot-autoconfigure</artifactId>
        <version>1.0.0</version>
    </dependency>
</dependencies>

使用自定义Starter

在应用中引入Starter依赖

<!-- 用户项目的pom.xml -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>hello-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

在代码中注入Bean

@RestController
public class HelloController {
    @Autowired
    private HelloService helloService;

    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}

自定义配置(可选)

application.properties 中修改消息:

hello.message=你好, Spring Boot!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2339281.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV基础01-图像文件的读取与保存

介绍: OpenCV是 Open Souce C omputer V sion Library的简称。要使用OpenCV需要安装OpenCV包&#xff0c;使用前需要导入OpenCV模块 安装 命令 pip install opencv-python 导入 模块 import cv2 1. 图像的读取 import cv2 img cv2.imread(path, flag)这里的flag 是可选参数&…

go语言优雅关机和优雅重启笔记

一、优雅关机 生活化例子 餐馆关门&#xff1a;你去餐馆吃火锅&#xff0c;刚坐下点完菜&#xff08;客户端发请求&#xff09;&#xff0c;餐馆老板突然接到通知要停电&#xff08;收到关机指令&#xff09;。老板很贴心&#xff0c;先停止接待新客人&#xff08;停止接收新请…

【算法】计数排序、桶排序、基数排序

算法系列八&#xff1a;非比较排序 一、计数排序 1.实现 1.1步骤 1.2代码 2.性质 2.1稳定性 2.1.1从前往后前始版&#xff1a; 2.1.2从后往前末始版&#xff1a; 2.2复杂度 2.2.1时间复杂度 2.2.2空间复杂度 二、桶排序 1.实现 1.1步骤 1.2代码 2.稳定性 三、…

Halcon应用:相机标定

提示&#xff1a;若没有查找的算子&#xff0c;可以评论区留言&#xff0c;会尽快更新 Halcon应用&#xff1a;相机标定 前言一、Halcon应用&#xff1f;二、应用实战1、图像理解1.1、开始标定 前言 本篇博文主要用于记录学习Halcon中算子的应用场景&#xff0c;及其使用代码和…

【C++ 程序设计】实战:C++ 实践练习题(31~40)

目录 31. 数列&#xff1a;s 1 &#xff0b; 2 &#xff0b; 3 &#xff0b; … &#xff0b; n 32. 数列&#xff1a;s 1 - 2 - 3 - … - n 33. 数列&#xff1a;s 1 &#xff0b; 2 - 3 &#xff0b; … - n 34. 数列&#xff1a;s 1 - 2 &#xff0b; 3 - … &#…

绿幕抠图直播软件-蓝松抠图插件--使用相机直播,灯光需要怎么打?

使用SONY相机进行绿幕抠图直播时&#xff0c;灯光布置是关键&#xff0c;直接影响抠图效果和直播画质。以下是详细的灯光方案和注意事项&#xff1a; 一、绿幕灯光布置核心原则 均匀照明&#xff1a;绿幕表面光线需均匀&#xff0c;避免阴影和反光&#xff08;亮度差控制在0.5…

从外网访问局域网服务器的方法

一、为什么局域网的服务器无法在外网访问&#xff1f; 服务器、电脑之间靠IP地址寻址&#xff0c;目前大部分基于IPV4进行寻址访问。但是因为IPV4的地址数量有限&#xff0c;中国分到的还比较少&#xff0c;所以非常紧缺。 一个解决方案就是在局域网来建立一个内部的网…

机器学习 Day12 集成学习简单介绍

1.集成学习概述 1.1. 什么是集成学习 集成学习是一种通过组合多个模型来提高预测性能的机器学习方法。它类似于&#xff1a; 超级个体 vs 弱者联盟 单个复杂模型(如9次多项式函数)可能能力过强但容易过拟合 组合多个简单模型(如一堆1次函数)可以增强能力而不易过拟合 集成…

交换机与路由器的主要区别:深入分析其工作原理与应用场景

在现代网络架构中&#xff0c;交换机和路由器是两种至关重要的设备。它们在网络中扮演着不同的角色&#xff0c;但很多人对它们的工作原理和功能特性并不十分清楚。本文将深入分析交换机与路由器的主要区别&#xff0c;并探讨它们的工作原理和应用场景。 一、基本定义 1. 交换…

【Oracle专栏】Oracle中的虚拟列

Oracle相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 1.背景 在EXP方式导出时&#xff0c;发现 出现如下提示 EXP-00107: virtual column 不支持&#xff0c;因此采用expdp方式导出。于是本文针对oracle虚拟列进行简单介绍。 2. 相…

2020 年 7 月大学英语四级考试真题(组合卷)——解析版

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;目前中南大学MBA在读&#xff0c;也考取过HCIE Cloud Computing、CCIE Security、PMP、CISP、RHCE、CCNP RS、PEST 3等证书。&#x1f433; &…

大语言模型的训练、微调及压缩技术

The rock can talk — not interesting. The rock can read — that’s interesting. &#xff08;石头能说话&#xff0c;不稀奇。稀奇的是石头能读懂。&#xff09; ----硅谷知名创业孵化器 YC 的总裁 Gar Tan 目录 1. 什么是大语言模型&#xff1f; 2. 语言建模&#xff…

firewall指令

大家好,今天我们继续来了解服务管理,来看看打开或关闭指定端口,那么话不多说,开始吧. 1.打开或者关闭指定端口 在真正的生产环境,往往需要防火墙,但问题来了,如果我们把防火墙打开,那么外部请求数据包就不能跟服务器监听通讯,这时,需要打开指定的端口,比如80,22,8080等. 2.fi…

【MySQL】MySQL表的增删改查(CRUD) —— 上篇

目录 MySQL表的增删改查&#xff08;CRUD&#xff09; 1. 新增&#xff08;Create&#xff09;/插入数据 1.1 单行数据 全列插入 insert into 表名 values(值, 值......); 1.2 单行数据 指定列插入 1.3 多行数据 指定列插入 1.4 关于时间日期&#xff08;datetime&am…

软考高级系统架构设计师-第15章 知识产权与标准化

【本章学习建议】 根据考试大纲&#xff0c;本章主要考查系统架构设计师单选题&#xff0c;预计考3分左右&#xff0c;较为简单。 15.1 标准化基础知识 1. 标准的分类 分类 内容 国际标准&#xff08;IS&#xff09; 国际标准化组织&#xff08;ISO&#xff09;、国际电工…

Spring Boot 整合 DeepSeek 实现AI对话 (保姆及教程)

文章目录 文章目录 前言 一、创建 spring boot 工程 二、申请key 三、修改配置文件 application.properties 四、编写控制器&#xff08;controller&#xff09; 五、运行调试 前言 提示&#xff1a;随着人工智能的不断发展&#xff0c;ai这门技术也越来越重要&#xff0c;很多…

前端与传统接口的桥梁:JSONP解决方案

1.JSONP原理 1.1.动态脚本注入 说明&#xff1a;通过创建 <script> 标签绕过浏览器同源策略 1.2.回调约定 说明&#xff1a;服务端返回 函数名(JSON数据) 格式的JS代码 1.3.自动执行 说明&#xff1a;浏览器加载脚本后立即触发前端预定义的回调函数&#xff08;现代开…

编码器---正交编码器

一、正交编码器定义与核心作用 正交编码器&#xff08;Orthogonal Encoder&#xff09;&#xff0c;又称增量式编码器&#xff0c;是一种通过输出两路相位差90的脉冲信号&#xff08;A相、B相&#xff09;来测量旋转角度、速度和方向的传感器。其核心优势是通过A/B相的脉冲顺序…

开发环境解决浏览器层面跨域问题

适用于开发环境临时调试等情况 新建一个 Chrome 的快捷方式&#xff0c;目标后面跟上&#xff1a; –disable-web-security --disable-gpu --user-data-dir%LOCALAPPDATA%\Google\chromeTemp 打开后会给出不安全的提示

2025年渗透测试面试题总结-拷打题库07(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 2025年渗透测试面试题总结-拷打题库07 1. CMS目录扫描的意义 2. 常见网站服务器容器 3. MySQL写入We…