前言

Starter 是 Spring Boot 非常重要的一个硬核功能。
通过 Starter 我们可以快速的引入一个功能或模块,而无须关心模块依赖的其它组件。关于配置,Spring Boot 采用“约定大于配置”的设计理念,Starter 一般都会提供默认配置,只有当我们有特殊需求的时候,才需要在application.yaml里进行单独配置以覆盖掉默认配置。

例如,我们开发一个 Web 应用,需要用到 Spring MVC、Tomcat 等组件,我们只需要依赖spring-boot-starter-web 即可,Starter 已经包含了开发一个 Web 应用所需的所有组件和依赖包,同时提供了 Web 服务的默认配置,比如端口是 8080。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

当我们有特殊需求时,比如要修改服务端口,才需要额外配置application.yaml

server:port: 8090

理解Starter

Starter 是什么???
Starter 是一个 Spring Boot 项目,它包含了一个功能或模块依赖的所有组件。一般来说,Starter 项目本身不包含任何代码,只管理依赖。

Starter 要解决什么问题???
没有 Starter 的时候,我们要引入一个功能或模块,首先要了解这个功能或模块依赖了哪些组件,还要管理依赖的版本。依赖完以后,还要配置所需的 bean,以及其它一些配置文件的编写。整个过程极其繁琐,门槛高。
Starter 解决了依赖和配置的问题,它把所需的依赖打成一个包,得益于 Spring Boot 自动装配,同时解决了 bean 的配置问题。引入 Starter 就可以实现零配置或少量配置来使用相应的功能。

Starter 是怎么实现的???
Starter 依赖 Spring Boot 自动装配的特性,它首先包含模块依赖的所有第三方库,再通过若干个XXXAutoConfiguration 的自动配置类来声明模块所需的 Bean,Spring Boot 程序启动时会自动加载这些配置类,把对应的 Bean 注册到容器,以实现某个功能。

Starter实战

通过编写一个我们自己的 Starter 来进一步理解其原理。
需求:编写一个可以操作 Redis 的客户端 Starter。
项目结构

redis-client - client - redis-spring-boot- redis-spring-boot-starter- redis-spring-boot-autoconfigure

模块介绍

模块说明
redis-client父项目,管理依赖
clientRedis 客户端核心模块
redis-spring-bootRedis 客户端和 Spring Boot 整合模块
redis-spring-boot-starterRedis 客户端启动器
redis-spring-boot-autoconfigureRedis 客户端自动装配模块(核心)

redis-client
基础模块,没有代码,主要管理依赖。因为要操作 Redis,这里引入 jedis。

<dependencyManagement><dependencies><dependency><groupId>io.redis</groupId><artifactId>client</artifactId><version>${project.version}</version></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.4.6</version></dependency></dependencies></dependencyManagement>

client
client 是 Redis 客户端核心模块,提供了操作 Redis 的客户端实现。它应该被设计成可以脱离 Spring 独立运行的,所以这个项目不应该依赖 Spring 任何东西。
首先抽象一个客户端接口,出于简单考虑,这里只提供 get、set 方法

public interface RedisClient {void set(String key, String value);String get(String key);}

同时提供一个默认实现,这里只是对 jedis 做一层包装

public class DefaultRedisClient implements RedisClient {private final Jedis jedis;public DefaultRedisClient(Jedis jedis) {this.jedis = jedis;}@Overridepublic void set(String key, String value) {jedis.set(key, value);}@Overridepublic String get(String key) {return jedis.get(key);}}

再提供一个工厂类,用来创建客户端实例

public class RedisClientFactory {public static RedisClient create(String host, int port, String user, String password, int database) {JedisClientConfig config = DefaultJedisClientConfig.builder().user(user).password(password).database(database).build();Jedis jedis = new Jedis(host, port, config);return new DefaultRedisClient(jedis);}}

至此,client 模块就完成了,而且是可以直接跑的,无需 Spring。

redis-spring-boot
为了开发者更加方便的在 Spring Boot 环境下使用我们的 Redis Client,我们要和 Spring Boot 做一个整合,为此再单独新建一个 redis-spring-boot 模块。它是一个父模块,主要管理依赖

<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.7.18</version></dependency><dependency><groupId>io.redis</groupId><artifactId>redis-spring-boot-autoconfigure</artifactId><version>${project.version}</version></dependency></dependencies></dependencyManagement>

redis-spring-boot-starter
我们的 Starter 模块,也就是开发者要引入的唯一一个模块。模块的命名上大家要注意,spring-boot-starter-XXX 这种前缀是 Spring Boot 官方保留的名字,我们不要用,建议用XXX-spring-boot-starter 风格命名。
Starter 模块一般没有代码,只管理依赖的组件,这里主要是引入我们的自动装配模块

<dependencies><dependency><groupId>io.redis</groupId><artifactId>redis-spring-boot-autoconfigure</artifactId></dependency></dependencies>

redis-spring-boot-autoconfigure
Redis Client 与 Spring Boot 整合的自动装配模块,也是我们这个示例中的核心模块。先看依赖:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>io.redis</groupId><artifactId>client</artifactId></dependency></dependencies>

RedisClientAutoConfiguration:自动配置类,Spring Boot 启动时会自动加载。它的加载是有条件的,首先得是 ClassPath 下包含 RedisClient.class,没有这个类我们就没法自动装配;其次是redis.enabled 不配置或者必须为 true 才会启用,否则是禁用的。

@Configuration@ConditionalOnClass(RedisClient.class)@ConditionalOnProperty(prefix = "redis",name = {"enabled"},matchIfMissing = true)@EnableConfigurationProperties(RedisClientProperties.class)public class RedisClientAutoConfiguration {@Beanpublic RedisClient redisClient(RedisClientProperties properties) {return RedisClientFactory.create(properties.getHost(), properties.getPort(),properties.getUser(), properties.getPassword(), properties.getDatabase());}}

自动配置类还开启了自动配置属性,类是 RedisClientProperties。按照“约定大于配置”的理念,我们提供了默认配置,开发者也可以选择覆盖默认配置

@ConfigurationProperties(prefix = "redis",ignoreUnknownFields = true)public class RedisClientProperties {private String host = "localhost";private int port = 6379;private String user;private String password;private int database = 0;// 忽略 getter、setter}

最后,要想办法让 Spring Boot 加载我们的配置类。按照 Spring Boot 自动装配的规则,我们编写META-INF/spring.factories文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\io.redis.client.spring.boot.autoconfigure.RedisClientAutoConfiguration

至此,我们的 Starter 就开发完毕了,把它安装到 Maven 仓库就可以给其它小伙伴用了。

测试一下
新建一个测试项目,引入依赖

<dependencies><dependency><groupId>io.redis</groupId><artifactId>redis-spring-boot-starter</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>

编写一个启动类,注入 RedisClient,尝试读取一下数据,是成功的

@SpringBootApplicationpublic class Application {@AutowiredRedisClient redisClient;public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);RedisClient redisClient = context.getBean(Application.class).redisClient;redisClient.set("haha", "xixi");System.err.println(redisClient.get("haha"));}}

尾巴

Starter 是 Spring Boot 框架中的一个重要概念,它提供了一种便捷的方式来配置和使用特定功能的 Spring Boot 应用程序。通常一个 Starter 是一个包含了一组相关依赖和配置的项目,目的是为了简化应用程序的开发和部署过程。
Starter 的设计目标是通过隐藏繁琐的配置细节和依赖声明,使开发者能够更专注于业务逻辑的实现。当你想要使用某个功能或集成某个组件时,只需引入相应的 Starter 依赖,Spring Boot 就会自动配置相关的组件,并提供默认的配置选项,大大减少了手动配置的工作量。
Starter 依赖于 Spring Boot 自动装配的特性,理解了自动装配你就理解了 Starter。