springboot starter 原理解析及实践
什么是springboot starter
starter是springBoot的一个重要部分。通过starter,我们能够快速的引入一个功能,而无需额外的配置。同时starter一般还会给我提供预留的自定配置选项,我们只需要在application.properties中设置相关参数,就可以实现配置的个性化。
那么这些方便的操作是怎么实现的呢?通过了解其原理,我们也可以做一个自己的starter,来让别人快速使用我们的功能。
按个人理解,我认为springBoot Starter就是一个智能化的配置类@Configuration
starter模块编写
1、【创建module】,首先我们自定义一个starter的module,根据你的starter实现复杂度,引入相关spring组件。最基本的,我们只需引入spring-boot-autoconfigure
模块。
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.linjianhui.springboot</groupId>
<artifactId>examplestarter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
<name>examplestarter</name>
<description>Demo project for Spring Boot starter</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
</dependencies>
</project>
2、【业务bean实现】实现我们的业务bean,案例中我们实现最简单的sayHello服务,输入msg,返回“hello,{msg}”。
public class DemoHelloService {
private String defaultMsg = "";
public String sayHello(String msg) {
return "hello, " + ((msg == null || msg.isEmpty()) ? defaultMsg : msg);
}
public void setDefaultMsg(String defaultMsg) {
this.defaultMsg = defaultMsg;
}
}
3、然后就是Configuration类的创建,这个类是starter自动初始化的核心类,负责把业务相关的bean智能的加载进来。
@Configuration
@ConditionalOnClass(DemoHelloService.class)
public class HelloDemoAutoConfiguration {
@Bean
@ConditionalOnMissingBean(DemoHelloService.class)//如果用户未自定义相关bean,生成默认bean
public DemoHelloService demoHelloService() {
DemoHelloService demoHelloService = new DemoHelloService();
return demoHelloService;
}
}
4、配置spring.factories
,通过该配置,才能让springboot来自动加载我们的Configuration类。具体原理我们稍后深入了解。
具体的,是在模块的resources/META-INF
目录下,新建spring.factories
文件。内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xxx.springboot.examplestarter.HelloDemoAutoConfiguration
最后我们把上述模块单独执行以下install或者deploy,一个starter就做好了。
其他项目使用我们的starter就非常简单了:(1)引入starter依赖;(2)注入需要的service。
<dependencies>
。。。
<dependency>
<groupId>com.xxx.springboot</groupId>
<artifactId>examplestarter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
。。。
@RestController
public class GreetingController {
@Resource
private DemoHelloService demoHelloService;
@RequestMapping("/sayhello")
public String sayHello(String msg) {
return demoHelloService.sayHello(msg);
}
done!
image.png
starter原理解读
回头再看上边的开发流程,有两个地方需要我们了解一下:
(1)如果让starter被自动识别加载:spring.factories里的EnableAutoConfiguration原理。
(2)如何实现自动加载的智能化、可配置化:@Configuration配置类里注解。
1.配置类自动加载机制
这里我们只简单的说一下大致的原理和流程,执行细节大家可以按照文章给出的思路自己去研读。
在SpringBoot的启动类,我们都会加上@SpringBootApplication
注解。这个注解默认会引入@EnableAutoConfiguration
注解。然后@EnableAutoConfiguration
会@Import(AutoConfigurationImportSelector.class)
。
AutoConfigurationImportSelector.class
的selectImports方法最终会通过SpringFactoriesLoader.loadFactoryNames
,加载META-INF/spring.factories
里的EnableAutoConfiguration
配置值,也就是我们上文中设置的资源文件。
2.自动加载的智能化可配置化
实际使用中,我们并不总是希望使用默认配置。比如有时候我想自己配置相关功能,有时候我想更改一下默认的服务参数。这些常见的场景Starter都想到了,并提供了如下的解决方案:
(1)@Conditional*注解
springboot starter提供了一系列的@Conditional*
注解,代表什么时候启用对应的配置,具体的可以去查看一下springboot的官方文档。
比如我们案例中的 「@ConditionalOnClass(DemoHelloService.class)」,代表如果存在DemoHelloService类时,配置类才会生效;又比如「@ConditionalOnMissingBean(DemoHelloService.class)」,代表着如果项目中没有DemoHelloService类型的bean,那么该配置类会自动创建出starter默认的DemoHelloService类型bean。
(2)@ConfigurationProperties注解
这个注解主要是为了解决如下场景:我想要使用starter的默认配置类,但是又想对配置中的某些参数进行自定义配置。@ConfigurationProperties
类就是做这个工作的。例如上述例子中,我想对默认的defaultMsg做些个性化的设置。就可以按如下方式来实现:
starter新增ConfigurationProperties类bean
@ConfigurationProperties(prefix = "hello.demo")
public class HelloDemoProperties {
private String defaultMsg = "";
public String getDefaultMsg() {
return defaultMsg;
}
public void setDefaultMsg(String defaultMsg) {
this.defaultMsg = defaultMsg;
}
}
启用property
image.png
在实际项目中自定义默认msg
image.png
当我们不输入msg值时,默认表现行为如下:
image.png