咱们都知道可使用SpringBoot快速的开发基于Spring框架的项目。因为围绕SpringBoot存在不少开箱即用的Starter依赖,使得咱们在开发业务代码时可以很是方便的、不须要过多关注框架的配置,而只须要关注业务便可。java
例如我想要在SpringBoot项目中集成Redis,那么我只须要加入spring-data-redis-starter的依赖,并简单配置一下链接信息以及Jedis链接池配置就能够。这为咱们省去了以前不少的配置操做。甚至有些功能的开启只须要在启动类或配置类上增长一个注解便可完成。web
那么若是咱们想要本身实现本身的Starter须要作些什么呢?下面就开始介绍如何实现本身的SpringBoot-xxx-starter。redis
首先说说原理,咱们知道使用一个公用的starter的时候,只须要将相应的依赖添加的Maven的配置文件当中便可,免去了本身须要引用不少依赖类,而且SpringBoot会自动进行类的自动配置。那么 SpringBoot 是如何知道要实例化哪些类,并进行自动配置的呢? 下面简单说一下。spring
首先,SpringBoot 在启动时会去依赖的starter包中寻找 resources/META-INF/spring.factories
文件,而后根据文件中配置的Jar包去扫描项目所依赖的Jar包,这相似于 Java 的 SPI 机制。json
第二步,根据 spring.factories
配置加载AutoConfigure
类。springboot
最后,根据 @Conditional
注解的条件,进行自动配置并将Bean注入Spring Context 上下文当中。app
咱们也可使用@ImportAutoConfiguration({MyServiceAutoConfiguration.class})
指定自动配置哪些类。框架
终于到了代码实现的步骤,接下来就开始编码咱们本身的SpringBoot-starter。spring-boot
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> </dependencies>
其中 spring-boot-configuration-processor
的做用是编译时生成 spring-configuration-metadata.json
,此文件主要给IDE使用。如当配置此jar相关配置属性在 application.yml
,你能够用ctlr+鼠标左键点击属性名,IDE会跳转到你配置此属性的类中。测试
咱们平常使用的Spring官方的Starter通常采起spring-boot-starter-{name}
的命名方式,如 spring-boot-starter-web
。
而非官方的Starter,官方建议 artifactId
命名应遵循{name}-spring-boot-starter
的格式。 例如:ysc-spring-boot-starter
。
<groupId>com.ysc</groupId> <artifactId>simple-spring-boot-starter</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>jar</packaging>
这里讲一下咱们的Starter要实现的功能,很简单,提供一个Service
,包含一个可以将配置文件中配置的字符串根据传入的字符进行分割的方法String[] split(String separatorChar)
。
public class StarterService { private String config; public StarterService(String config) { this.config = config; } public String[] split(String separatorChar) { return StringUtils.split(this.config, separatorChar); } }
@ConfigurationProperties("example.service") public class StarterServiceProperties { private String config; public void setConfig(String config) { this.config = config; } public String getConfig() { return config; } }
AutoConfigure
类 ,这步是关键点@Configuration @ConditionalOnClass(StarterService.class) @EnableConfigurationProperties(StarterServiceProperties.class) public class StarterAutoConfigure { @Autowired private StarterServiceProperties properties; @Bean @ConditionalOnMissingBean @ConditionalOnProperty(prefix = "example.service", value = "enabled", havingValue = "true") StarterService starterService (){ return new StarterService(properties.getConfig()); } }
解释一下代码中用到的几个注解:
@ConditionalOnClass
,当classpath
下发现该类的状况下进行自动配置。@ConditionalOnMissingBean
,当Spring Context
中不存在该Bean
时。@ConditionalOnProperty(prefix = "example.service",value = "enabled",havingValue = "true")
,当配置文件中example.service.enabled=true
时。@ConditionalOnBean:当容器中有指定的Bean的条件下 @ConditionalOnClass:当类路径下有指定的类的条件下 @ConditionalOnExpression:基于SpEL表达式做为判断条件 @ConditionalOnJava:基于JVM版本做为判断条件 @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置 @ConditionalOnMissingBean:当容器中没有指定Bean的状况下 @ConditionalOnMissingClass:当类路径下没有指定的类的条件下 @ConditionalOnNotWebApplication:当前项目不是Web项目的条件下 @ConditionalOnProperty:指定的属性是否有指定的值 @ConditionalOnResource:类路径下是否有指定的资源 @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者在有多个Bean的状况下,用来指定首选的Bean @ConditionalOnWebApplication:当前项目是Web项目的条件下
resources/META-INF/
下建立spring.factories
文件,并添加以下内容:org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.autocinfigure.StarterAutoConfigure
至此,咱们的一个Starter代码部分就是完成了,下面将项目安装到本地Maven仓库中。
在项目根目录执行 mvn install
进行打包安装。
将Starter项目的依赖添加到咱们本身的SpringBoot项目中
<dependency> <groupId>com.ysc</groupId> <artifactId>simple-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
在application.yml
配置文件中添加配置信息:
example service enabled: true config: abc-des-dde,SSS-DRS-RE,SDR-SDFR-XXX
在本地使用JUnit
进行代码测试
@Autowired private StarterService starterService; @Test public void starterTest() { String[] splitArray = starterService.split(","); System.out.println(splitArray); }
好,到这咱们的一个自定义Stater就完成了