Spring Boot Starter设计实现

前言

Starter 是 Spring Boot 非常重要的一个硬核功能。

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

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

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

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

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。
项目结构

basic 复制代码
redis-client
 - client
 - redis-spring-boot
  - redis-spring-boot-starter
  - redis-spring-boot-autoconfigure

模块介绍

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

redis-client

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

xml 复制代码
<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 方法

java 复制代码
public interface RedisClient {

    void set(String key, String value);

    String get(String key);
}

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

java 复制代码
public class DefaultRedisClient implements RedisClient {

    private final Jedis jedis;

    public DefaultRedisClient(Jedis jedis) {
        this.jedis = jedis;
    }

    @Override
    public void set(String key, String value) {
        jedis.set(key, value);
    }

    @Override
    public String get(String key) {
        return jedis.get(key);
    }
}

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

java 复制代码
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 模块。它是一个父模块,主要管理依赖

xml 复制代码
<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 模块一般没有代码,只管理依赖的组件,这里主要是引入我们的自动装配模块

xml 复制代码
<dependencies>
    <dependency>
        <groupId>io.redis</groupId>
        <artifactId>redis-spring-boot-autoconfigure</artifactId>
    </dependency>
</dependencies>

redis-spring-boot-autoconfigure

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

xml 复制代码
<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 才会启用,否则是禁用的。

java 复制代码
@Configuration
@ConditionalOnClass(RedisClient.class)
@ConditionalOnProperty(
        prefix = "redis",
        name = {"enabled"},
        matchIfMissing = true
)
@EnableConfigurationProperties(RedisClientProperties.class)
public class RedisClientAutoConfiguration {

    @Bean
    public RedisClient redisClient(RedisClientProperties properties) {
        return RedisClientFactory.create(properties.getHost(), properties.getPort(),
                properties.getUser(), properties.getPassword(), properties.getDatabase());
    }
}

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

java 复制代码
@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文件

basic 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  io.redis.client.spring.boot.autoconfigure.RedisClientAutoConfiguration

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

测试一下

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

xml 复制代码
<dependencies>
    <dependency>
        <groupId>io.redis</groupId>
        <artifactId>redis-spring-boot-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

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

java 复制代码
@SpringBootApplication
public class Application {

    @Autowired
    RedisClient 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。

相关推荐
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
憨子周1 小时前
2M的带宽怎么怎么设置tcp滑动窗口以及连接池
java·网络·网络协议·tcp/ip
霖雨3 小时前
使用Visual Studio Code 快速新建Net项目
java·ide·windows·vscode·编辑器
SRY122404193 小时前
javaSE面试题
java·开发语言·面试
Fiercezm3 小时前
JUC学习
java
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows
我不是星海3 小时前
1.集合体系补充(1)
java·数据结构
P.H. Infinity4 小时前
【RabbitMQ】07-业务幂等处理
java·rabbitmq·java-rabbitmq
爱吃土豆的程序员4 小时前
java XMLStreamConstants.CDATA 无法识别 <![CDATA[]]>
xml·java·cdata