大数据、Java EE 学习资料请关注 B 站:https://space.bilibili.com/204792350

粗糙简陋版的自定义 starter,通过这个想必对 SpringBoot 的 starter 能有点逼数吧

准备工作

首先需要明白,一个 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 启动器的正常所需;

Snipaste_2020-05-31_14-20-05

按照上面的描述,还需要另外的模块,来专门处理自动配置的功能,关于这一点可以参考 SpringBoot 官方的自动配置类模块;

Snipaste_2020-05-31_14-20-49

启动器是面对面向外部的,其它一些模块则是由启动器依赖,maven 好在一点,按需要来添加依赖;

所以在启动的中,有一个标签添加的依赖管理,但不会直接加载;

结构大概这样:

Snipaste_2020-05-31_14-25-16

命名规约

官方的命名规范:

  • 前缀: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 两个类;

Snipaste_2020-05-31_16-26-42

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 提供的各个阶段的回调;
# Java   SpringBoot  

评论

公众号:mumuser

企鹅群:932154986

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×