8

徒手撸一个Spring Boot中的starter

 3 years ago
source link: https://my.oschina.net/u/4728925/blog/4945192
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

starter背景

Spring Boot目前已经变成了后端开发这必备技能之一,其中一个主要原因是Spring Boot中有个非常重要的机制(starter机制)。

starter能够抛弃以前繁杂的配置,将其统一集成进starter,使用的时候只需要在maven中引入对应的starter依赖即可,Spring Boot就能自动扫描到要加载的信息并启动相应的默认配置。

starter让我们摆脱了各种依赖库的处理,以及各种配置信息的烦恼。SpringBoot会自动通过classpath路径下的类发现需要的Bean,并注册进IOC容器。Spring Boot提供了针对日常企业应用研发各种场景的spring-boot-starter依赖模块。所有这些依赖模块都遵循着约定成俗的默认配置,并允许我们调整这些配置,即遵循“约定大于配置”的理念。

我们经常会看到或者使用到各种xxx-starter。比如下面几种:

img

Spring Boot starter原理

从总体上来看,无非就是将Jar包作为项目的依赖引入工程。而现在之所以增加了难度,是因为我们引入的是Spring Boot Starter,所以我们需要去了解Spring Boot对Spring Boot Starter的Jar包是如何加载的?下面我简单说一下。

SpringBoot 在启动时会去依赖的 starter 包中寻找 /META-INF/spring.factories 文件,然后根据文件中配置的 Jar 包去扫描项目所依赖的 Jar 包,这类似于 Java 的 SPI 机制。

细节上可以使用@Conditional 系列注解实现更加精确的配置加载Bean的条件。

JavaSPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。

自定义starter的条件

如果想自定义Starter,首选需要实现自动化配置,而要实现自动化配置需要满足以下两个条件:

  1. 能够自动配置项目所需要的配置信息,也就是自动加载依赖环境;

  2. 能够根据项目提供的信息自动生成Bean,并且注册到Bean管理容器中;

实现自定义starter

pom.xml依赖

<dependencies>
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
    <version>2.0.0.RELEASE</version>
 </dependency>
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <version>2.0.0.RELEASE</version>
    <optional>true</optional>
  </dependency>
</dependencies>

根据需要自定义Starter的实现过程大致如下(以我定义的Starter为例):

img

定义XxxProperties类,属性配置类,完成属性配置相关的操作,比如设置属性前缀,用于在application.properties中配置。

TianProperties代码:

import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "spring.tian")
public class TianProperties {
    private String name;
    private int age;
    private String sex = "M";
    //省略 get set 方法
}

创建XxxService类,完成相关的操作逻辑 。

TianService代码:

public class TianService {
​
    private TianProperties properties;
​
    public TianService() {
    }
​
    public TianService(TianProperties userProperties) {
        this.properties = userProperties;
    }
    public void sayHello(){
        System.out.println("hi, 我叫: " + properties.getName() +
        ", 今年" + properties.getAge() + "岁"
         + ", 性别: " + properties.getSex());
    }
}

定义XxxConfigurationProperties类,自动配置类,用于完成Bean创建等工作。

TianServiceAutoConfiguration代码:

@Configuration
@EnableConfigurationProperties(TianProperties.class)
@ConditionalOnClass(TianService.class)
@ConditionalOnProperty(prefix = "spring.tian", value = "enabled", matchIfMissing = true)
public class TianServiceAutoConfiguration {
​
    @Autowired
    private TianProperties properties;
​
    @Bean
    @ConditionalOnMissingBean(TianService.class)
    public TianService tianService() {
        return new TianService(properties);
    }
}

在resources下创建目录META-INF,在 META-INF 目录下创建 spring.factories,在SpringBoot启动时会根据此文件来加载项目的自动化配置类。

「spring.factories中配置」

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.tian.TianServiceAutoConfiguration

把上面这个starter工程打成jar包:

img

使用自定义starter

创建一个Spring Boot项目test,项目整体如下图:

img

在项目中把自定义starter添加pom依赖

<dependency>
    <groupId>com.tian</groupId>
    <artifactId>spring-boot-tian-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

TestApplication启动类

@SpringBootApplication
@EnableEurekaServer
public class TestApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

application.properties中配置

spring.tian.name=tian
spring.tian.age=22
spring.tian.sex=M

写一个TestController.java类

RestController
@RequestMapping("/my")
public class TestController {
​
    @Resource
    private TianService tianService;
​
    @PostMapping("/starter")
    public Object starter() {
        tianService.sayHello();
        return "ok";
    }
}

把我们自定义的starter打成的jar依赖进来后,

img

可以看到其中多了一个json的文件。

img

最后启动项目,输入

http://localhost:9091/my/starter

img

controller成功返回ok,再看后台打印

hi, 我叫: tian, 今年22岁, 性别: M

这就成功的现实了自定义的starter。

关键词:开箱即用、减少大量的配置项、约定大于配置。

  1. Spring Boot在启动时扫描项目所依赖的JAR包,寻找包含spring.factories文件的JAR包,

  2. 然后读取spring.factories文件获取配置的自动配置类AutoConfiguration`,

  3. 然后将自动配置类下满足条件(@ConditionalOnXxx)的@Bean放入到Spring容器中(Spring Context)

  4. 这样使用者就可以直接用来注入,因为该类已经在容器中了。

「只要我们的方向对了,就不怕路远!」


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK