准备工作
首先需要明白,一个 starter 需要的前置条件是什么?有什么东西可以使用?怎么才能让其生效?等等一些基本问题。
关于上面的几个问题,相信有阅读过 SpringBoot 的底层启动流程的代码,会有一种知其然知其所以然。没有阅读过源代码,可能会有一种迷迷糊糊的感觉,虽然可以参照现成的 starter ,然难免有些不灵活,毕竟 SpringBoot 在启动的时候会有一个监听器,在每个阶段会有回调,还有一个初始化器,在 SpringBoot 启动的时候,可以初始化容器等等。
参考现成的 starter,发现一个 starter 必要的几点:
- 相关注解:
- @Configuration
- @ConditionalOnXxx
- @AutoConfigurationAfter
- @Bean
- @ConfigurationProperties
- @EnableConfigurationProperties
- ......
- 生效自动配置
- 在 META-INF/spring.factories 文件中添加相应的全类名
- EnableAutoConfiguration:自动配置类加载
- ApplicationInitializer:初始化器
- SpringApplicationRunListener:监听器
- ......
以上是前置条件,以下是 starter 结构或者说模式;
启动器模块是一个空 jar,仅仅提供辅助性的依赖管理,这些依赖可能用于自动装配或者其它类库依赖;
什么意思呢?
简单来说,就是要有一个空 jar 作为 starter 启动的模块,然后可以在这里面添加各种依赖,来辅助 starter 启动器的正常所需;
按照上面的描述,还需要另外的模块,来专门处理自动配置的功能,关于这一点可以参考 SpringBoot 官方的自动配置类模块;
启动器是面对面向外部的,其它一些模块则是由启动器依赖,maven 好在一点,按需要来添加依赖;
所以在启动的中,有一个标签添加的依赖管理,但不会直接加载;
结构大概这样:
命名规约
官方的命名规范:
- 前缀:spring-boot-starer-
- 举例:spring-boot-starter-web|spring-boot-starter-webmvc ......
自定义命名规范:
- 后缀:-spring-boot-starter
- 举例:mybatis-spring-boot-starter|druid-spring-boot-starter
采用自定义的命名规范来编写 starter 的命名规约;
编写 Starter
首先,需要三个模块:
- starter 启动器
- autoconfigurer 自动配置加载
它们之间的结构关系是:
- starter 作为对外被依赖的模块,而 autoconfigurer 是一个配置模块,所以被 starter 依赖
- 居于 starter 的功能模块,类似与 spring-boot-web 等模块,都是在依赖 starter 的基础上进行开发的,所以功能模块依赖 starter
理清楚关系之后,接下来按顺序创建相应的模块;
第一个,是一个空 Maven 项目,什么都不需要,只依赖 autoconfigurer 模块即可;
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.huasio.starter</groupId>
<artifactId>huasio-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.huasio.starter</groupId>
<artifactId>huasio-spring-boot-starter-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
由于是演示用的,所以很简单,这样就行了。
接下来创建 autoconfigurer 模块:
<?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.huasio.starter</groupId>
<artifactId>huasio-spring-boot-starter-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>huasio-spring-boot-starter-autoconfigurer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
</dependencies>
</project>
这个项目可以继承 spring-boot,单纯依赖;
这里为了简洁,就采用依赖方式;
由于是采用两个模块来演示,所以暂时键一个功能放在 autoconfigurer 模块下面,然后需要创建一个 Properties、AutoConfiguration 两个类;
Properties 类作用是为了配置,从配置文件中可以定义配置项;
@ConfigurationProperties(prefix = "huasio.hello", ignoreUnknownFields = true)
public class HelloProperties {
private String name;
private String welcome;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getWelcome() {
return welcome;
}
public void setWelcome(String welcome) {
this.welcome = welcome;
}
}
说明以下,顶部注解作用是:将配置文件中以 huasio.hello 开头的导入注入进来,根据 setXxx() 方法来赋值;
ignoreUnknownFields 是忽略未知的字段;
编写一个 Service 类,作为演示:
public class HelloService {
HelloProperties helloProperties;
public HelloService(HelloProperties helloProperties) {
this.helloProperties = helloProperties;
}
public String sayHello() {
return helloProperties.getName() + helloProperties.getWelcome();
}
}
编写 AutoConfiguration 自动配置:
@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties({HelloProperties.class})
public class HelloAutoConfiguration {
HelloProperties helloProperties;
public HelloAutoConfiguration(HelloProperties helloProperties) {
this.helloProperties = helloProperties;
}
@Bean
public HelloService helloService() {
HelloService helloService = new HelloService(helloProperties);
return helloService;
}
}
使用 @EnableConfigurationProperties 来开启 Properties 文件的注入,若是没有用 @Autowired 注解的话,则会通过单个有参构造方法自动注入;
@Configuration:表明这是一个配置类;
@ConditionalOnWebApplication:表示在 Web 环境下才生效;
通过 @Bean 注册一个组件,将 HelloService 注册到容器中;
接下来只需要将这个自动配置类编写到 META-INF/spring.factoies 文件即可;这个文件结构放在 resoureces 目录下即可;
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.huasio.starter.autoconfigurer.HelloAutoConfiguration
这个文件的作用会在 SpringBoot 启动的时候,被读取,其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 是自动配置类的键;
这样一个简单粗糙的 starter 就面世啦,接下来在配置文件中可以通过 huasio.hello 配置属性,然后访问 /hello 就可以获得输出内容;
huasio:
hello:
name: 123
welcome: welcome
输出:
123:welcome
总结
- 通过阅读 SpringBoot 现成的 starter,可以更加清晰的明白如何编写要给优秀的 starter,并且借助 SpringBoot 提供的各种 API 来完成;
- 自动配置类的作用注定是后置处理的,也就是通过某某前置条件触发之后,才会自动注册;而且,加载顺序也在容器组件组件注册之后,所以才会生效;
- 自定义的 starter 不需要全部依赖 spring-boot 提供的 模块或者 API,只要满足自动加载的,自动注册的即可;
扩展
- 一个优秀的 starter,一定是读透或者清晰了解 SpringBoot 启动加载顺序原理之后才会产生,所以可以根据这一点来提升代码的优秀,同时根据 SpringBoot 提供的各种回调,来处理 SpringBoot 各个阶段性的触发代码;
- 了解,自动配置生效的原理,例如说:为何 SpringBoot 的 autoconfigurer 依赖包可以配置 Web 模块的配置?
- 了解,SpringBoot 提供的各个阶段的回调;
Q.E.D.
Comments | 0 条评论