如果你在公司里开发共享libraries,或者正在开发一个开源或商业library,你可能想开发自己的自动配置(auto-configuration)。自动配置类可以打包到外部jars,并且依旧可以被Spring Boot识别。自动配置可以关联一个"starter",用于提供auto-configuration的代码及需要引用的libraries。我们首先讲解构建自己的auto-configuration需要知道哪些内容,然后讲解创建自定义starter的常见步骤。
注 可参考demo工程了解如何一步步创建一个starter。
从底层来讲,自动配置(auto-configuration)是通过标准的@Configuration
类实现的。此外,@Conditional
注解用来约束自动配置生效的条件。通常自动配置类需要使用@ConditionalOnClass
和@ConditionalOnMissingBean
注解,这是为了确保只有在相关的类被发现及没有声明自定义的@Configuration
时才应用自动配置,具体查看spring-boot-autoconfigure
源码中的@Configuration
类(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-specific配置,你的类就需要应用在WebMvcAutoConfiguration
后面。
你也可以使用@AutoconfigureOrder
注解为那些相互不知道存在的自动配置类提供排序,该注解语义跟常规的@Order
注解相同,但专为自动配置类提供顺序。
注 自动配置类只能通过这种方式加载,确保它们定义在一个特殊的package中,特别是不能成为组件扫描的目标。
你几乎总是需要在自己的自动配置类里添加一个或更多的@Conditional
注解。@ConditionalOnMissingBean
注解是一个常见的示例,开发者可以用它覆盖自动配置类提供的默认行为。
Spring Boot包含很多@Conditional
注解,你可以在自己的代码中通过注解@Configuration
类或单独的@Bean
方法来重用它们。
@ConditionalOnClass
和@ConditionalOnMissingClass
注解可以根据特定类是否出现来决定配置的包含,由于注解元数据是使用ASM来解析的,所以你可以使用value
属性来引用真正的类,即使该类没有出现在运行应用的classpath下,也可以使用name
属性如果你倾向于使用字符串作为类名。
@ConditionalOnBean
和@ConditionalOnMissingBean
注解可以根据特定类是否存在决定bean的包含,你可以使用value
属性指定beans(by type),也可以使用name
定义beans(by name),search
属性用于限制搜索beans时需要考虑的ApplicationContext
层次。
注 你需要注意bean定义添加的顺序,因为这些条件的计算是基于目前处理内容的。出于这个原因,我们推荐在自动配置类上只使用@ConditionalOnBean
和@ConditionalOnMissingBean
注解(即使保证它们在其他用户定义的beans后加载)。
注 @ConditionalOnBean
和@ConditionalOnMissingBean
不会阻止@Configuration
类的创建,在类级别使用那些conditions跟使用注解标记每个@Bean
方法是等价的。
@ConditionalOnProperty
注解可以根据一个Spring Environment
属性来决定是否包含配置,使用prefix
和name
属性指定要检查的配置。默认情况下,任何存在的只要不是false
的属性都会匹配,你也可以使用havingValue
和matchIfMissing
属性创建更高级的检测。
@ConditionalOnResource
注解只在特定资源出现时才会包含配置,可以使用常见的Spring约定命名资源,例如file:/home/user/test.dat
。
@ConditionalOnWebApplication
和@ConditionalOnNotWebApplication
注解可以根据应用是否为'web应用'来决定是否包含配置,web应用是任何使用Spring WebApplicationContext
,定义一个session
作用域,或有一个StandardServletEnvironment
的应用。
@ConditionalOnExpression
注解可以根据SpEL表达式结果来决定是否包含配置。
一个完整的Spring Boot starter可能包含以下组件:
autoconfigure
模块,包含自动配置类的代码。starter
模块,提供自动配置模块及其他有用的依赖,简而言之,添加本starter就能开始使用该library。注 如果不需要将它们分离开来,你可以将自动配置代码和依赖管理放到一个单一模块中。
确保为你的starter提供一个合适的命名空间(namespace),模块名不要以spring-boot
作为开头,尽管使用一个不同的Maven groupId,未来我们可能会为你正在做的自动配置提供官方支持。
这里是经验之谈,假设你正在为“acme”创建一个starter,命名自动配置模块为acme-spring-boot-autoconfigure
,命名starter为acme-spring-boot-starter
,如果只有一个模块结合它们,通常会使用acme-spring-boot-starter
。
此外,如果你的starter提供配置keys,需要为它们提供一个合适的命名空间,特别是不要使用Spring Boot的命名空间(比如,server
,management
,spring
等),这些是属于Spring Boot的,我们可能会在将来以相同方式提高/修改它们,这可能会破坏你的东西。
确保触发meta-data生成,这样IDE辅助也就可以用于你的keys了,你可能想检查生成的元数据(META-INF/spring-configuration-metadata.json
)以确保keys被正确的文档化。
自动配置模块包含了使用该library需要的任何东西,它可能还包含配置的keys定义(@ConfigurationProperties
)和用于定义组件如何初始化的回调接口。
注 你需要将对该library的依赖标记为可选的,这样在项目中添加该自动配置模块就更容易了。如果你这样做,该library将不会提供,Spring Boot会回退到默认设置。
starter模块实际是一个空jar,它的目的是提供使用该library所需的必要依赖。不要对添加你的starter的项目做任何假设,如果你正在自动配置的library需要其他starters,一定要提到它。提供一个合适的默认依赖集可能比较困难,特别是存在大量可选依赖时,你应该避免引入任何非必需的依赖。