starter 模块设计-01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.
├── core # 动态线程池核心模块包,实现动态线程池相关基础类定义
├── dashboard-dev # 前端页面方便查看和调试
├── example # 动态线程池示例包,演示线程池动态参数变更、监控和告警等功能
│   ├── apollo-example
│   └── nacos-cloud-example
├── spring-base # 动态线程池基础模块包,包含Spring扫描动态线程池、是否启用以及Banner打印等
└── starter # 动态线程池配置中心组件包,实现线程池结合Spring框架和配置中心动态刷新
  ├── adapter # 动态线程池适配层,比如对接 Web 容器 Tomcat 线程池等
  │   └── web-spring-boot-starter # Web 容器线程池组件库
  ├── apollo-spring-boot-starter # Apollo 配置中心动态监控线程池组件库
  ├── common-spring-boot-starter # 配置中心公共监听等逻辑抽象组件库
  ├── dashboard-dev-spring-boot-starter # 控制台 API 组件库
  └── nacos-cloud-spring-boot-starter # Nacos 配置中心动态监控线程池组件库

SpringBoot Starter 是什么

在传统开发中我们要使用某个框架功能(比如数据库访问或消息队列),常常需要东拼西凑地添加一堆依赖,还要配置各种参数。这就像下厨做菜前,还得 满世界找食材、调料,步骤繁琐又容易出错。而 Spring Boot Starter(启动器)就像一个贴心的“大厨助手”或 预先配好的料理包,帮我们一次性备齐所需“食材”(依赖)和默认配置,让开发者开箱即用

Starter 背景与作用

在没有 SpringBoot 的年代,引入第三方组件通常意味着要在 Maven/Gradle 里添加多个坐标依赖,然后再去查文档写大量配置文件,非常麻烦(依赖版本不匹配、配置分散各处都是常见痛点)

SpringBoot 提出了“约定优于配置”的理念,Starters 正是这一理念的产物。Starter 的作用简单来说有以下几点:

1. 依赖管理一站式

一个 Starter 包含了一组相关的依赖,就像“自助套餐”。你加上一个 Starter,相当于引入了一系列经过官方测试搭配好的依赖,避免了自己挨个寻找版本兼容的库。不再需要东翻西找依赖,一个 Starter帮你打包好了所有常用库

2. 自动配置减负

Starter 配合 Spring Boot 的自动装配机制,能够根据类路径上的依赖自动进行默认配置。这意味着许多以前需要写的样板配置代码,现在 Spring Boot 会替你完成

3. 开箱即用的默认

大多数 Starter 都提供了一套合理的默认行为。例如 spring-boot-starter-logging 会默认使用 Logback 日志框架,spring-boot-starter-web 默认使用 Tomcat 容器并开启 Spring MVC。这些默认配置遵循官方最佳实践,避免了开发者从零开始配置

概括来说,Spring Boot Starter 的出现解决了依赖管理杂乱和配置碎片化的问题。过去可能我们需要拷贝粘贴各种依赖坐标、写很多配置,现在只需引入一个 Starter绝大部分配置就帮你默默搞定。这也是为什么 Spring Boot 能让项目搭建变得如此简单快速的关键原因之一

Starter 核心原理揭秘

Starters 之所以能做到“引入即用”,背后离不开 Spring Boot 自动装配(Auto-Configuration)机制 的支撑

Spring Boot 的启动流程

当我们在主应用类上标注了 @SpringBootApplication(其内部含有@EnableAutoConfiguration注解)时,Spring Boot 会在启动时做如下事情:

1. 搜集候选自动配置类

  • Spring Boot 利用 SpringFactoriesLoader 去扫描应用类路径下所有 JAR 包里的特殊配置文件

  • 对于 Spring Boot 2.x,这个文件就是每个 Starter JARMETA-INF/spring.factories

  • 而在 Spring Boot 3.x,这个机制有所改变,变成在每个 Starter JAR 中查找

    META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件

2. 按条件装配自动配置类

拿到所有候选的自动配置类名后(可能成百上千个),SpringBoot 并不会傻乎乎地把它们统统装配进来,而是会挨个筛选,判断条件是否满足。这得益于 SpringBoot 提供的一系列条件注解

  • @ConditionalOnClass:当类路径下存在某个类时才生效
  • @ConditionalOnMissingClass缺少某类时生效
  • @ConditionalOnBean:当容器中已有某个 Bean 时/不在时生效
  • @ConditionalOnProperty:当配置文件中某个属性有指定值时生效等等

3. 注册 Bean 到容器

通过条件筛选后,符合条件的自动配置类会被实例化并发挥作用。其实每个自动配置类本质上就是一个加了 @Configuration 注解的普通 Spring 配置类,其中定义了一系列 @Bean 方法用于注册组件

至此,原本我们需要手工配置的许多 Bean,因为 Starter 的引入而在后台自动完成注册

最小可运行的自定义 Starter 示例

假设我们要封装一个自定义的 HelloService,它提供一个 sayHello() 方法返回问候语。我们希望通过 Starter 来自动配置这个服务,并让使用者开箱即用地获得 HelloService

1. 核心功能代码

首先,我们编写 HelloService 及其自动配置类:

1
2
3
4
5
6
// 提供简单业务功能的服务类
public class HelloService {
public String sayHello() {
return "易思涯:向你问好!";
}
}

HelloAutoConfiguration 自动装配类:

1
2
3
4
5
6
7
8
9
10
// 自动配置类,将 HelloService 注册为 Bean
@Configuration // 标明这是配置类
@ConditionalOnMissingBean(HelloService.class) // 当容器中没有 HelloService 时才生效
public class HelloAutoConfiguration {
@Bean
public HelloService helloService() {
// 可以在这里定制 HelloService,比如读取配置属性来调整行为
return new HelloService();
}
}

这里我们用了 @ConditionalOnMissingBean,确保如果用户自己已经提供了一个名为 helloServiceBean,我们的默认配置就不重复注册,保持 SpringBoot 一贯的可扩展性。另外,在实际场景中,我们可能会为 HelloService 提供可配置的属性(比如问候语内容可配置)。这时可以引入 @ConfigurationProperties 注解的属性类,并在自动配置时通过 @EnableConfigurationProperties 注册它,从而让用户在 application.yml 里配置参数。例如我们可以有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
@ConfigurationProperties(prefix="hello")
public class HelloProperties {
private String message = "Hello, Spring Boot Starter!";
}


@Configuration
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public HelloService helloService(HelloProperties props) {
return new HelloService(props.getMessage());
}
}

2. 注册自动配置

有了上面的自动配置类,接下来需要让 Spring Boot 在启动时知道它的存在。

Spring Boot 2.x

我们会在 META-INF/spring.factories 写入:

1
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.starter.HelloAutoConfiguration

Spring Boot 3.x

则应该在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件里加入配置类全名,如:

1
com.example.starter.HelloAutoConfiguration 

把这一行添加到 .imports 文件后,我们的 Starter 打包发布时就携带了自动配置声明。Spring Boot 应用在引入该 Starter 后,会扫描到这条声明并据此加载 HelloAutoConfiguration

3. 发布与依赖声明

当我们的 Starter 编写完毕并发布到仓库后,其他人要使用就非常简单了——就和使用官方 Starter 差不多。在他们的 SpringBoot 项目的 pom.xml 中引入依赖:

1
2
3
4
5
<dependency>
<groupId>com.example</groupId>
<artifactId>hello-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>

实际的坐标应以发布到仓库的为准(一般指的是 公司内部私有仓库),如果是一个开源项目(Maven 中央仓库