Spring Boot「12」自定义 starter

184 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情

基于 Spring Boot 的应用开发之所以如此便捷,一个非常重要的原因就是拥有丰富的 starter。 基于 starter,开发者可以避免冗长的配置,而直接上手编写业务代码。 除了那些常用的 starter 外,Spring Boot 支持自定义 starter。 今天,我们一块来学习下如何实现一个自定义的 starter。

实现一个自定 starter 需要实现两个部分内容:

  1. 实现一个自动配置类,根据条件来自动化的构造 Bean,另外需要一个外部配置属性对应的 POJO 类,用来保存配置信息;
  2. 构造一个 pom.xml 文件,引入必要的依赖

接下来,我们逐步实现一个可以 say hello 的 starter。

01-实现自动配置类

首先,我们创建一个 say-hello 的工程(这里我们同样复用之前的 payroll 工程,在其下增加一个 say-hello 的模块)。 在这个模块里,需要增加三个类:

  • SayHello.class,是一个工具类:
public class SayHello {
    private String name;
    private String message;

    public SayHello(String name, String message) {
        this.name = name;
        this.message = message;
    }

    public void greeting() {
        System.out.println("Hi~, " + name + ", " + message);
    }
}

SayHello 只有2个属性和一个方法 greeting,会向控制台打印欢迎消息。

  • SayHelloAutoConfiguration.class,自动配置类,标注了条件化注解:
@Configuration
@EnableConfigurationProperties(SayHelloProperties.class)
@ConditionalOnClass(SayHello.class)
public class SayHelloAutoConfiguration {

    private SayHelloProperties sayHelloProperties;

    @Autowired
    public SayHelloAutoConfiguration(SayHelloProperties sayHelloProperties) {
        this.sayHelloProperties = sayHelloProperties;
    }

    @Bean
    @ConditionalOnMissingBean
    public SayHello sayHello() {
        return new SayHello(sayHelloProperties.getUserName(), sayHelloProperties.getMessage());
    }
}

@Configuration表示这是一个配置类,用来自动化配置 SayHello 工具类。 @EnableConfigurationProperties(SayHelloProperties.class)会自动创建一个 SayHelloProperties 对象,并将外部属性值绑定到该对象上。 @ConditionalOnClass(SayHello.class)表示该配置类中定义的 Bean 只有在项目的 classpath 中存在 SayHello.class 时才有效 @ConditionalOnMissingBean表示只有在当前容器中并不包含 SayHello.class 类型的实例时,该 Bean 方法才生效,创建一个 Bean 对象。

为了是自动配置生效,还需要在 META-INF/spring.factories 中增加如下内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  self.samson.example.SayHelloAutoConfiguration
  • SayHelloProperties.class,标注了@ConfigurationProperties,表示外部化配置信息的 POJO:
@ConfigurationProperties(prefix = "example.say.hello")
public class SayHelloProperties {
    private String userName;
    private String message;
    // getters & setters and constructors
}

注:上面的写法可能并不是一个标准的 starter,标准的 starter 项目中应该不包含任何代码,仅包含 pom.xml 上述 SayHello.class 应该是在某个应用包中; ConfigurationProperties.class 和 SayHelloAutoConfiguration.class 应该在某个 auto-configure 包中。

02-定义 pom.xml

因为我们的项目比较简单,所以 pom.xml 中并不需要配置什么。 在实际的 starter 项目中,例如 spring-boot-starter-validation 中,jar 包中基本是不包含任何内容的。 starter 最重要的是它的 pom.xml 文件,它描述了所有的依赖项。 我们仍然以 spring-boot-starter-validation 为例,它的 pom.xml 中的主要内容包括:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.7.4</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-el</artifactId>
        <version>9.0.65</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.2.5.Final</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

它定义了 spring-validator 所有的依赖项,所有希望使用 spring-validator 的项目只需要引入 starter,而不需要手动的引入每一个依赖。 简化依赖配置也是 starter 设计的初衷之一。

03-使用 starter

使用自定义的 starter 与使用其他 starter 的步骤没有其他区别,只需要在 pom.xml 中增加依赖即可。

接下来演示如何使用 say-hello 中的 SayHello。

我们在 classpath:/properties/say-hello.properties 中增加如下配置,以便使 SayHelloProperties 中的属性能够有值绑定。

example.say.hello.user-name=Samson
example.say.hello.message=Nice to meet you!

然后,在 Application 类上通过@PropertyResource中引入外部配置文件。

@PropertySource("classpath:/properties/say-hello.properties")
public class ListAllManagedBeanApplication {
    public static void main(String[] args) {
        final ConfigurableApplicationContext ctx = SpringApplication.run(ListAllManagedBeanApplication.class, args);

        final SayHello greeter = ctx.getBean(SayHello.class);

        greeter.greeting();
    }
}

运行程序,即可在控制台得到如下输出:

Hi~, Samson, Nice to meet you!

03-总结

今天我们一块学习了如何实现一个 starter,使用 starter 是一种良好地编程或者项目管理风格。 通过 starter,依赖管理和升级都比较方便,只需要升级 starter 的版本即可。 不过,starter 也不是完全没有缺点,过度依赖 starter 可能会使得依赖关系更难理解。