第二节 快速自定义一个 Starter

tips:本系列文章的代码,均为 maven 构建, SpringBoot 版本 2.x。 (3.x 不再以 spring.factories 作为导入全路径名, Spring Boot 2.7 已经废弃,在 Spring Boot 3.0 彻底移除)

本文将自定义一个 Starter,所谓麻雀虽小五脏俱全,感受一下 Starter 的五脏六腑。

本项目源码地址: gitee.com/uzongn/uzon...

一、定义一个 Starter 需要哪些步骤

  1. 创建一个新的 Maven 项目作为 Starter 项目,主要是一些业务实现类
  2. 创建一个或多个配置类,使用 @Configuration 注解
  3. 创建 spring.factories 文件,位置:src/main/resources/META-INF/spring.factories ,在文件中添加对自动配置类的引用
  4. 使用 @ConfigurationProperties 注解创建一个属性类,以支持从 application.properties 或 application.yml 文件中读取配置。(可选)
  5. 将 Starter 项目打包并可能发布到 Maven 中央仓库或私有仓库
  6. 引用和使用

那么接下来将围绕这几个步骤实现一个 Starter。

二、Starter 应用场景

每一个 Starter 都应该解决一个场景。本案例选择一个简单的缓存场景,开发环境使用本地缓存,预发环境使用 redis。

在实际应用过程中,会根据环境装配不同的实现。

作为案例,由于保持简单,因此只提供了三个接口。put、get、delete。

csharp 复制代码
/**
 * 缓存服务接口定义,提供基本的缓存操作方法。
 */
public interface CacheService<K, V> {
    /**
     * 将键值对存储到缓存中。
     */
    void put(K key, V value);

    /**
     * 根据键从缓存中获取值。
     */
    V get(K key);

    /**
     * 从缓存中删除指定的键和对应的值。
     */
    void delete(K key);
}

下面是实现的类图, 一个是基于 redis,另外一个基于 ConcurrentHashMap。

三、Starter 快速案例

注意:生成环境,使用缓存需要小心,比如缓存失效时间、考虑复杂数据结构等。

3.1 Starter 的结构

maven 项目的结构如下所示:

  1. auto-configuration
  2. spring.factories
  3. service..

3.2 xxAutoConfiguration 类

auto-configuration 主要负责 bean 的加载,同时会根据条件注解,进行选择加载。比如本案例中还会出现一些条件注解:

  • ConditionalOnClass: 依赖场景,比如本案例中 RedisCacheService 依赖 RedisTemplate

  • ConditionalOnMissingBean 如果不存在特定的bean,则会加载,用于互斥场景

  • ConditionalOnProperty 条件装配,只有属性 cache.type 为 redis 才加载

less 复制代码
@Configuration
public class SimpleCacheAutoConfiguration {

    /**
     * 1. ConditionalOnClass 依赖 RedisTemplate
     * 2. ConditionalOnProperty 条件装配
     * 
     */
    @Bean
    @ConditionalOnClass(RedisTemplate.class)
    @ConditionalOnProperty(name = "cache.type", havingValue = "redis")
    public <K, V> SimpleCacheService<K, V> redisTemplateService(RedisTemplate redisTemplate) {
        return new RedisCacheService<K, V>(redisTemplate);
    }

    /**
     * 条件装配,ConditionalOnMissingBean 如果不存 SimpleCacheService 在则加载
     */
    @Bean
    @ConditionalOnMissingBean
    public <K, V> SimpleCacheService<K, V> inMemoryCacheService() {
        return new InMemoryCacheService<K, V>();
    }
}

注:条件装配的注解远不止上面几个。

3.3 xxAutoConfiguration注入 bean

留意一个细节,我们常常只会在 xxAutoConfiguration 中注入 bean,而基本不使用包扫描;避免不必要的类加载到容器; 另外使用路径扫描,可能会将相同路径下的其他包,扫描到容器中。 侧面也警醒我们需要将包路径尽量命名唯一。

实现类,不添加 @Service 等注解, Bean 的注入,由 xxAutoConfiguration 控制;实现统一、灵活的 bean 加载。

3.4 配置spring.factories

文件名和路径名要求严格一致。文件中的可以值,必须是 org.springframework.boot.autoconfigure.EnableAutoConfiguration

; value可以用逗号分隔,可以有多个 xxAutoConfiguration。

3.5 ConfigurationProperties 可选

ConfigurationProperties 是为了增加一定的灵活性而存在;不是必须,可以直接使用 ConditionalOnProperty 等替代。本项目略过。

@ConditionalOnProperty(name = "cache.type", havingValue = "redis")

3.6 maven 依赖

关于 maven 保持最小依赖,保持干净。

注意 maven 中两个细节,分别是:

  1. <scope>provided</scope>
  2. <optional>true</optional>

这个内容,我们将在实战篇中详细说明。

特别注意: packaging 要求 为 jar

3.7 测试打包

可以先打包到本地,然后测试使用,没有问题后,再上传到公司的私服。

maven 中的 packaging 为 jar,而不是 pom,否则上传不成功。

3.8 使用

引入 maven 依赖,即可使用,可以通过条件装配使用不同的实现。

xml 复制代码
<dependency>
    <groupId>com.uzong</groupId>
    <artifactId>simplecache-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

引用接口; 实现类可以通过条件装配,加载不同的实现类。

在 application.propterties 中选择不同的实现。

3.9 验证阶段

启动应用程序,并访问地址。

http://localhost:8080/api/cache/test?key=1&value=a

到这里,自定义 Starter 已经完成。可以下载代码进行体验。

四、其他注意事项(补充阅读)

4.1 Bean 定义顺序

在同一个 configuration 中的 Bean, 其加载是按照从上到下的顺序进行加载; 所以在使用条件装配的时候,务必要注意顺序。

比如下面情况会,在配置了 cache.type=redis

因为顺序加载解析 ConditionalOnMissingBean(不存在则加载),所以会先加载 InMemoryCacheService,然后又加载 InMemoryCacheService,出现两个实现类。所以在实现的时候,需要控制好条件注解和bean加载的顺序。

4.2 Starter 没有生效排查

如果发现自己的 Starter 没有生效。比如找不到实现类

A component required a bean of type 'com.uzong.simple.cache.SimpleCacheService' that could not be found.

其一、org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports

断点排查,查看是否引入 auto-configuration 类

这里的内容,我们先不深入,后面我们会深入源码进入分析。

其二、检查条件配置等,是否配置错误。当然可能还会有其他问题,需要一一对症下药。

4.3 autoconfigure 模块与 Starter 模块

官网建议将自动装配相关类放在 autoconfigure 模块中,Starter 模块依赖引用这个模块,并且引入其他需要的依赖, 这不是必须的。而我们自定义 Starter, 常常会将这两者合并到一个模块中。不过 mybatis-spring-boot-starter 则使用这种方式。

五、最后小结

本章快速定义了一个 Starter,并提供了源码实现,可以下载源码进行体验。这一章可以作为实现 Starter 的参考范本。

接下来,以 mybatis-spring-boot-starter 为例,看看 Mybatis 是如何使用 Starter 的,并逐步揭开 Starter 底层原理的面纱。

相关推荐
IT学长编程6 分钟前
计算机毕业设计 Java酷听音乐系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·音乐系统·计算机毕业设计选题
IT学长编程23 分钟前
计算机毕业设计 基于协同过滤算法的个性化音乐推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·毕业论文·协同过滤算法·计算机毕业设计选题·个性化音乐推荐系统
小小娥子28 分钟前
Redis的基础认识与在ubuntu上的安装教程
java·数据库·redis·缓存
几何心凉36 分钟前
已解决:org.springframework.web.HttpMediaTypeNotAcceptableException
java
华农第一蒟蒻38 分钟前
Java中JWT(JSON Web Token)的运用
java·前端·spring boot·json·token
两点王爷40 分钟前
使用WebClient 快速发起请求(不使用WebClientUtils工具类)
java·网络
计算机学姐1 小时前
基于SpringBoot+Vue的高校运动会管理系统
java·vue.js·spring boot·后端·mysql·intellij-idea·mybatis
平凡的小码农1 小时前
JAVA实现大写金额转小写金额
java·开发语言
一直在进步的派大星1 小时前
Docker 从安装到实战
java·运维·docker·微服务·容器
老华带你飞1 小时前
公寓管理系统|SprinBoot+vue夕阳红公寓管理系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·spring boot·课程设计