# 如何自定义Spring Boot Starter？

> 转载：[如何自定义SpringBoot Starter？](https://objcoding.com/2018/02/02/Costom-SpringBoot-Starter/)

[SpringBoot自动化配置源码分析](https://mp.weixin.qq.com/s/Be7vAudvvvuCI2EdwPX80Q)从源码的角度讲解了 SpringBoot 自动化配置的原理，知道了它最终要干的事情不过是读取 META-INF/spring.factories 中的自动化配置类而已。

SpringBoot 项目就是由一个一个 Starter 组成的，一个 Starter 代表该项目的 SpringBoot 起步依赖，除了官方已有的 Starter，如果你需要将自己的项目支持 SpringBoot，那么就需要把它制作成一个 Starter。这篇博客依据 SpringBoot 的自动化配置原理，开发一个属于自己的 Starter。

### 1. 自定义 Starter

自动化配置需满足两个条件：

1. 能够生成 Bean，并注册到 Bean 容器中；
2. 能够自动配置项目所需要的配置。

在这里创建一个 spring-boot-starter-helloworld 项目作为例子，实现以上两点。

引入 SpringBoot 自动化配置依赖：

```xml
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
    <version>1.5.9.RELEASE</version>
  </dependency>
</dependencies>
```

spring-boot-starter-helloworld 只是作为例子演示自定义 starter 的过程，实现的功能很简单就是创建一个 HelloworldService 的，并配置 sayHello() 方法打印的语句。

```java
public class HelloworldService {

  private String words;

  private String getWords() {
    return words;
  }

  public void setWords(String words) {
    this.words = words;
  }

  public String sayHello() {
     return "hello, " + words;
  }
}
```

创建属性类，`prefix = “helloworld”`代表该项目在属性文件中配置的前缀，即可以在属性文件中通过 helloworld.words=springboot，就可以改变属性类字段 words 的值了。

```java
@ConfigurationProperties(prefix = "helloworld")
public class HelloworldProperties {
  public static final String DEFAULT_WORDS = "world";

  private String words = DEFAULT_WORDS;

  public String getWords() {
    return words;
  }

  public void setWords(String words) {
    this.words = words;
  }
}
```

创建**自动化配置类**，这个相当于就是一个普通的 Java 配置类，可以在这里创建 Bean，并可获得与 `application.properties` 属性文件相对应的属性类的 Bean。

```java
// 相当于一个普通的 java 配置类
@Configuration
// 当 HelloworldService 在类路径的条件下
@ConditionalOnClass({HelloworldService.class})
// 将 application.properties 的相关的属性字段与该类一一对应，并生成 Bean
@EnableConfigurationProperties(HelloworldProperties.class)
public class HelloworldAutoConfiguration {

  // 注入属性类
  @Autowired
  private HelloworldProperties hellowordProperties;

  @Bean
  // 当容器没有这个 Bean 的时候才创建这个 Bean
  @ConditionalOnMissingBean(HelloworldService.class)
  public HelloworldService helloworldService() {
    HelloworldService helloworldService = new HelloworldService();
    helloworldService.setWords(hellowordProperties.getWords());
    return helloworldService;
  }
}
```

在 META-INF 目录下创建 `spring.factories`，这个属性文件可重要啦，因为 SpringBoot 自动化配置最终就是要扫描 `META-INF/spring.factories` 来加载项目的自动化配置类。

```properties
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.objcoding.starters.helloworld.HelloworldAutoConfiguration
```

### 2. 引用 Starter

为了引入 starter，我在这里再创建一个 spring-boot-starter-helloworld-sample 项目。

添加 spring-boot-starter-helloworld 起步依赖：

```xml
<dependency>
  <groupId>com.objcoding</groupId>
  <artifactId>spring-boot-starter-helloworld</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>
```

在 application.properties 中添加属性：

```properties
helloworld.words=springboot
```

在 SpringBoot 主程序中 注入 helloworldService。

```java
@RestController
@SpringBootApplication
public class HelloworldApplication {

  @Autowired
  private HelloworldService helloworldService;

  @RequestMapping("/")
  public String sayHello() {
    return helloworldService.sayHello();
  }

  public static void main(String[] args) {
    SpringApplication.run(HelloworldApplication.class, args);
  }
}
```

访问 <http://localhost:8080/，打印以下结果：>

![2020-09-02-xcoNgu](https://image.ldbmcs.com/2020-09-02-xcoNgu.jpg)

要源码的同学点击这里获取： <https://github.com/objcoding/spring-boot-starter-tutorial>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ldbmcs.gitbook.io/java/java-frameworks-59/spring/spring-boot/ru-he-zi-ding-yi-spring-boot-starter.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
