像Spring Boot那样创建一个你自己的Starter

时间:2022-05-06
本文章向大家介绍像Spring Boot那样创建一个你自己的Starter,主要内容包括1.理解 auto-configured beans、2.如何定位自动配置类、3.2 Bean conditions、3.4 Resource conditions、3.5 Web application conditions、3.6 SpEL expression conditions、4 创建你自己的starter、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

如果你所在的公司要开发一个共享的lib,或者如果你想要为开源世界做点贡献,你也许想要开发你自己的自定义的自动配置类以及你自己的starter pom。这些自动配置类虽然在一个单独的jar包中,但却依然能够被Spring Boot获取到。

自动配置(Auto-configuration)其实就是一个starter。starter这个词是不是很熟悉,没错,就是Spring Boot中的那些看起来略屌的启动器。现在你可以自己编写一个自己的starter了。在一个starter里,你可以把一组dependency和标记有@Configuration的类放在这个starter里。

1.理解 auto-configured beans

自动配置使用标准的@Configuration类实现。 然后可以搭配@Conditional注解来规定在什么条件下这些配置会被使用和生效。 其中自动配置类最常用到的就是@ConditionalOnClass和@ConditionalOnMissingBean这两个注解。

这样的话,就可以保证在找到相关的类或Bean的时候的时候自动配置类才会生效。

2.如何定位自动配置类

如果你在自己单独的jar包中编写了一个自动配置类,而且加上了@Configuration以及其它约束条件,这时候引入依赖的话,你的Configuration并不会生效。

你还需要在你的starter项目中的resources文件夹下加上META-INF /spring.factories这样的目录和文件,Spring Boot会检查你发布的jar中是否存在META-INF / spring.factories文件。 在这个文件中你应该把你的配置类加入进来,像下面这样,在一个EnableAutoConfiguration key下。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

如果你的配置类需要按照一定的顺序来逐个生效的话,你可以使用@AutoConfigureAfter@AutoConfigureBefore来实现。

比如,如果你搞了个web的一个自定义配置,那么你的这个配置的生效就要排在WebMvcAutoConfiguration的后面

蕾丝下面这样:

@Configuration
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
public class HornetQAutoConfiguration {...

如果您想排序某些彼此不应该有什么直接关联的自动配置类的话,您还可以使用@AutoconfigureOrder。 该注解与常规的@Order注解具有相同的语义,但为自动配置类提供了一个你想要的顺序。

/**
 * Auto-configuration为你定义了好几个注解可以让你的自动配置类按照一定的顺序自动排列
 * 具体就是通过AnnotationConfigApplicationContext#register(Class...)}来为你生成的

 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
public @interface AutoConfigureOrder {
    /**
     * 排序方式. 默认是Ordered#LOWEST_PRECEDENCE
     * @see Ordered#getOrder()
     * @return the order value
     */
    int value() default Ordered.LOWEST_PRECEDENCE;
}

这个Ordered也就是两种情况:

public interface Ordered {
    int HIGHEST_PRECEDENCE = -2147483648;//优先级最高
    int LOWEST_PRECEDENCE = 2147483647;//优先级最低
    int getOrder();
}

NOTE:自动配置类只能以这种方式加载。这样才能保证他们被限定在特定包中并且它们不会被其它的组件扫描到!

3. Condition注解

一般情况下,你都要给你的自动配置类加上一个或者更多的@Conditional注解。比如@ConditionalOnMissingBean就是一个比较常见的例子,当你允许开发人员去覆盖自动配置的时候,那么强烈建议你使用这个注解,这样的话,如果开发人员觉得你的自动配置并不能满足需求,那么他可以自定义。

特别注意:@ConditionalOnMissingBean也只适合在自动配置类中使用。千万不要乱用。

Spring Boot自带了一些@Conditional注解,你可以在你的代码中重用,在标记了@Configuration的类或@Bean方法上。

3.1 Class conditions

@ConditionalOnClass和@ConditionalOnMissingClass注解其实就是当特定的类存在就生效,反之亦然。 由于这些注解上的元数据是使用ASM来解析,所以你可以通过name属性来引入一个类,这样也不在乎这个类在没在你的classpath中。

@ConditionalOnClass(name ="de.MyService")

如果你使用value属性的话,如果所在类不存在的话,则会报错导致编译无法通过。

像下面这样:

@ConditionalOnClass({de.MyService.class})

3.2 Bean conditions

@ConditionalOnBean和@ConditionalOnMissingBean上面已经介绍过了。也可以通过name或value属性来指定bean。 还有一个search属性,通过该属性你可以指定搜索bean时所使用的ApplicationContext。

3.3 Property conditions

@ConditionalOnProperty注解允许配置包含基于Spring Environment的属性。 使用prefix和name属性指定应检查的属性。 默认情况下,任何存在且不等于false的属性都将匹配。 你还可以使用havingValue和matchIfMissing属性进行更高级的过滤和检查。

@ConditionalOnProperty(prefix = "hornetq.embedded", value = "enabled", havingValue = "true", matchIfMissing = true) static class EmbeddedServerConfiguration { //类似这样,hrnetq.embedded.enabled=true,EmbeddedServerConfiguration的配置就生效了; //如果你把havingValue修改为false,该配置还是生效的,因为你加了matchIfMissing = true,如果你把 //matchIfMissing改为false,该配置就失效了。

3.4 Resource conditions

@ConditionalOnResource 同理,当某个资源文件存在时,则配置生效。

资源可以使用通常的Spring约定来指定,例如,file:/home/user/test.dat。

3.5 Web application conditions

同理@ConditionalOnWebApplication@ConditionalOnNotWebApplication是用来判断应用是不是web应用:web application。

这里所说的Web应用程序是指任何使用了Spring WebApplicationContext,定义了session scope或者有标准servlet环境的任何应用程序。

3.6 SpEL expression conditions

@ConditionalOnExpression也是一样的。至于SpEL是个什么东东?这里不赘述了。

类似这样的用法:

@ConditionalOnExpression("'Hello World'.concat('!')")

源码:

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnExpressionCondition.class)
public @interface ConditionalOnExpression {

    /**
     * 设置spring的表达式语言: SpEL expression。表达式应该返回true或false。默认是true
     * @return the SpEL expression
     */
    String value() default "true";

}

4 创建你自己的starter

一个完整的Spring Boot 的starter可以包含两个组件(注意:是“可以”包含):

  • autoconfigure 模块。 自动配置类代码。
  • starter 模块。 自动配置相关的依赖以及其它要启动starter所要提供能力的相关依赖,总之,要保证starter所提供的能力能够正常启动。

NOTE:如果你觉得没必要把这两个分开的话,你可以把两个模块合并成一个。

4.1 起名字

不说了。学会模仿。

4.2 Autoconfigure 模块

Autoconfigure模块包含有你的自动配置类以及和配置类相关联的类比如@ConfigurationProperties这些类以及任何将来要进一步自定义组件的回调接口等代码。

NOTE:注意把一些库的dependency标记为optional,以便您可以更轻松地在项目中包含自动配置模块。这也是你设计sdk的一些技巧。

4.3 Starter 模块

Starter模块就是一个空的jar。里边只有一个pom。然后提供启动所需要的依赖。

-----------分割线--------------

:你需要加入两个与自动配置相关的依赖:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-configuration-processor</artifactId>
   <optional>true</optional>
</dependency>