妹子请喝奶茶: 问我Spring Boot自定义Starter

2,593 阅读5分钟

晚上八点,办公室里只剩零星几盏灯倔强地亮着,时不时响起键盘的噼啪声音。

我伸了个懒腰,揉了揉发酸的眼睛,心想,工作告一段落,一天窝囊费到手,撤。这时,小悠抱着笔记本电脑,脚步匆匆地走了过来,发梢扫过她泛红的脸颊,像是染上了晚霞的颜色。

“嘿!飞哥,先别溜!” 她把电脑往我桌上一放,“我最近在学习spring boot starter,本来想着引入现成的依赖就能轻松搞定,结果搞了一下午还是搞不定,我都快抑郁了!” 。

我凑近一看,屏幕上满是红色的报错信息,像极了她此刻烦躁的心情。 “多大点事儿。Spring Boot 自定义 starter 确实是个好东西,能把那些重复又繁琐的配置封装起来,以后想用相关功能,直接引入咱们自己的 starter 就行了“来,我给你说道说道。

我一边打开IDEA,一边解释:“首先,咱们要知道一个自定义 starter 的基本结构。它就像是一个精心包装的礼物盒,里面包含了各种必要的礼物。一般来说,它需要有一个自动配置类,这可是核心部分,就像礼物盒的钥匙,用来告诉 Spring Boot 该怎么初始化相关的组件和配置。” 说着,我敲下了一段代码:

import org.springframework.context.annotation.Configuration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {

    private final MyProperties myProperties;

    public MyAutoConfiguration(MyProperties myProperties) {
        this.myProperties = myProperties;
    }

    @Bean
    @ConditionalOnProperty(name = "my.enabled", havingValue = "true")
    public MyService myService() {
        return new MyService(myProperties.getMessage());
    }
}

小悠盯着代码,眉头紧锁,睫毛在眼下投出一片小小的阴影:“这 ConditionalOnClass、ConditionalOnProperty 都是啥意思啊?”

我:“ConditionalOnClass 就是条件注解,它表示只有当类路径下存在指定的类时,这个配置才会生效。就好比只有当你拥有某把特定的钥匙,才能打开对应的宝箱。而 ConditionalOnProperty 呢,是根据配置文件中的属性值来决定是否生效,像刚刚代码里的‘my.enabled’,只有当这个属性值为‘true’时,myService 这个 bean 才会被创建。” 她似懂非懂地点头,长发蹭到了我的手臂,痒痒的。

小悠眨了眨眼:“原来是这样!那这个 MyProperties 类又是做什么的呀?” 我接着说道:“它是用来读取配置文件里咱们自定义的属性的。你看。” 我又新建了一个类:

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "my")
public class MyProperties {

    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

“在配置文件里,只要写‘my.message=你好小悠’,这里的 message 就能读取到对应的值,方便我们灵活配置。哦对了,这里用到的MyService类,是一个自定义的业务类,用于实现具体的业务逻辑,比如这样定义:” 我又补充了一段代码:

public class MyService {

    private final String message;

    public MyService(String message) {
        this.message = message;
    }

    public String doSomething() {
        return "执行自定义操作,获取到的信息是:" + message;
    }
}

“在实际项目中,你可以根据具体需求,在MyService里编写更复杂的业务代码,doSomething方法只是一个示例,你可以添加更多的方法,实现不同的功能。” 我解释道。小悠突然凑近,呼吸扫过我的侧脸:“要是我还是不会,你可要一直教我哦。” 我心跳漏了一拍,佯装镇定地点头。

小悠兴奋地说:“明白了!那接下来是不是就可以把这个自定义 starter 用起来啦?” 我点点头:“没错,但还有关键的一步,就是要告诉 Spring Boot 这是一个自动配置类。我们需要在 src/main/resources/META-INF/spring.factories 文件里添加配置。” 我快速创建了这个文件,并写入内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.starter.MyAutoConfiguration

“这样,Spring Boot 启动的时候,就会自动加载我们的自定义配置类了。” 我说完,转头看向小悠。她已经迫不及待地开始创建自己的项目,不过,很快她又皱起眉头,委屈巴巴地说:“哎呀,引入之后怎么报错了,说找不到相关的依赖!” 我说:"修改一下 pom.xml 文件就可以了“

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.youyou</groupId>
    <artifactId>my-spring-boot-starter</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <!--其他依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

经过一番折腾,小悠终于成功引入了自定义 starter,项目顺利启动。 我笑着说:“这只是入门,自定义 starter 还有很多高级玩法,比如整合更多的第三方库,优化配置的灵活性。以后遇到问题,随时叫我,嗯?但是得请我喝奶茶”。

第二天,小悠拿着杯奶茶踩着轻快的步子找到了我。“昨天太晚了,还没说完呢,我现在有个新工程,想用咱们之前开发的自定义 starter,该怎么操作呀?” 她边帮我插好吸管,一边看着我。

我打开IDEA:“首先,你得确保咱们的自定义 starter 已经打包成 jar 包了。还记得之前在 pom.xml 里配置的打包插件吧?在 IDEA 的 Maven 窗口中,找到 Lifecycle,点击 package,就能把 starter 打包成 jar。打包好之后,如果你只是在本地项目使用,可以把 jar 包安装到本地 Maven 仓库。在命令行进入到 starter 项目的根目录,执行mvn install命令,这样本地仓库就有这个 starter 了。” 我操作完后,继续说:“接下来,在新工程的 pom.xml 文件里添加依赖。就像这样:” 我在新工程的 pom.xml 中添加了如下代码:

<dependency>
    <groupId>com.youyou</groupId>
    <artifactId>my-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

“添加完依赖,刷新一下 Maven,让项目下载相关的 jar 包。” 我点击了刷新按钮,“最后,别忘了在新工程的 application.properties 或者 application.yml 文件里,配置咱们 starter 需要的参数,比如刚刚定义的my.message。像这样在 application.yml 里写:

my:
  enabled: true
  message: "你好小悠"

保存配置,启动项目,你就能使用 starter 里封装的功能了。”

旁边工位阿强也来了:”飞哥,一大早喝奶茶呢,哦,你们俩在研究什么呢”

我猛吸一口奶茶:“去去去,瞎打听啥,写你的bug去。”

小悠:“谢谢飞哥”,一溜烟的跑回自己工位上。