如何创建自己的第一个springboot-starter

对于SpringBoot我们一定不会陌生,由于其快速方便的开发方式让我们从各种繁琐的配置中解放出来,让我们的开发效率得到了大大提升,而各种starter则是其最重要的组成部分之一

什么是starter?

我觉得可以简单认为starter是不同场景下(如MySQL、Redis、RocketMQ等)需要的一系列依赖的集合,就像是一个个可以单独引用的模块,有了starter,我们就可以使用其内置的各种依赖完成当前场景下的需求开发

starter的结构?

  1. 在IDEA中打开一个SpringBoot项目,选择一个starter依赖展开查看,这里以MyBatis为例

    • pom.properties:记录了starter的artifactId、groupId和version信息

    • pom.xml:是starter所需的依赖,比如mybatis-spring、mybatis-spring-boot-autoconfigure

    • MANIFEST.MF:记录了jar包的构建信息,比如jdk版本等

  2. 用同样的方式打开mybatis-spring-boot-autoconfigure的依赖

    • additional-spring-configuration-metadata.json:手动添加IDE配置提示
    • spring.factories:key-value键值对形式,记录需要被加载的配置类信息
    • spring-configuration-metadata.json:自动生成的IDE配置提示
    • spring-autoconfigure-metadata.properties:自动配置类是否被加载的条件
    • 除了上述,还有若干Class,如MybatisAutoConfiguration配置类,MybatisProperties属性类等等

如何创建自己的springboot-starter?

简单来说,分成如下几个步骤

1.创建一个maven项目,名字为xxx-springboot-autoconfigure(不固定),里面包含XxxAutoConfiguration配置类、XxxProperties属性类(非必须)、spring.factories文件(key为org.xxx.EnableAutoConfiguration,value为XxxAutoConfiguration)

2.创建一个空的maven项目,名字为xxx-springboot-starter(不固定),在pom.xml中引入上述的xxx-springboot-autoconfigure项目

2.创建一个springboot项目,引入xxx-springboot-starter项目,创建测试类使用starter中提供的功能类验证是否成功

实例演示

  1. 创建一个名为redisson-util-springboot-autoconfigure的maven项目,结构如下

    RedissonUtilAutoConfiguration.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Configuration
    @EnableConfigurationProperties(RedissonUtilProperties.class)
    public class RedissonUtilAutoConfiguration {

    @Resource
    private RedissonUtilProperties properties;

    // 创建RedissonClient
    @Bean
    @ConditionalOnMissingBean
    public RedissonClient redissonClient() {
    String password = properties.getPassword();
    List<String> list = new ArrayList<>();
    List<String> nodes = properties.getNodes();
    for (String node : nodes) {
    list.add("redis://" + node);
    }
    Config config = new Config();
    config.useClusterServers().setPassword(password).setScanInterval(5000).setNodeAddresses(list);

    return Redisson.create(config);
    }

    }

    RedissonUtilProperties.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    @ConfigurationProperties(prefix = RedissonUtilProperties.PREFIX)
    public class RedissonUtilProperties {

    public static final String PREFIX = "redisson";

    // redis密码
    private String password;

    // redis集群地址(ip:port)
    private List<String> nodes;

    public String getPassword() {
    return password;
    }

    public void setPassword(String password) {
    this.password = password;
    }

    public List<String> getNodes() {
    return nodes;
    }

    public void setNodes(List<String> nodes) {
    this.nodes = nodes;
    }
    }

    spring.factories

    1
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.codecho.RedissonUtilAutoConfiguration
  2. 创建一个名为redisson-util-springboot-starter的maven项目,结构如下

  3. 创建一个名为test-starter的springboot项目,引入redisson-util-springboot-starter项目依赖,结构如下

    application.properties(redis配置信息自填)

    1
    2
    redisson.password=xxx
    redisson.nodes=xxx.xxx.xxx.xxx:6379,xxx.xxx.xxx.xxx:6389,xxx.xxx.xxx.xxx:6399

    测试类代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    @Resource
    private RedissonClient redissonClient;

    @Test
    public void run() {
    Thread t1 = new Thread(() -> {
    winPrize();
    }, "张三");
    Thread t2 = new Thread(() -> {
    winPrize();
    }, "李四");

    t1.start();
    t2.start();
    }

    void winPrize() {
    String lockKey = "goods:1001";
    RLock lock = redissonClient.getLock(lockKey);
    if (lock.isLocked()) {
    System.out.println(Thread.currentThread().getName() + " 痛失大奖");
    return;
    }

    boolean lockFlag = lock.tryLock();
    if (!lockFlag) {
    System.out.println(Thread.currentThread().getName() + " 离中奖只差一步,别灰心,后面还有机会");
    return;
    }

    try {
    System.out.println(Thread.currentThread().getName()+ " 他就是天选之子!");
    TimeUnit.SECONDS.sleep(5);
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    if (lock.isHeldByCurrentThread()) {
    lock.unlock();
    }
    }
    }
  4. 运行测试方法,观察控制台输出

    使用Redis可视化工具(这里使用的Another Redis Desktop Manager)获取分布式锁的信息,发现其存活时间为30s,到期后锁被删除。如果需要多次运行测试方法,记得等待锁过期后再测试,否则会产生两个线程都获取不到锁的情况。