自定义 spring-boot-starter

Spring Boot Java Maven About 5,521 words

新建 Spring Boot 项目

添加配置处理器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

更改插件

原插件是打包Spring Boot应用包的,包含了启动函数,需替换掉。

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

更改为apachemaven编译插件。

若不更换,依赖了自定义spring-boot-starter的工程本地能正常运行,但打包时会报:error:(3,47) java: 程序包不存在

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
    </configuration>
</plugin>

属性配置类

自定义spring-boot-starter中添加配置类,@Data使用了Lombok插件。

application.propertiesyml中配置的属性映射到类字段中。

@Data
@ConfigurationProperties(prefix = "test")
public class TestConfig {

    private Integer code;

    private String msg;

}

测试服务类

自定义spring-boot-starter中添加测试服务类,注意:不用加@Service注解。

public class TestService {

    private TestConfig testConfig;

    public TestService(TestConfig testConfig) {
        this.testConfig = testConfig;
    }

    public String say() {
        return testConfig.getCode() + ":" + testConfig.getMsg();
    }
}

自动配置类

属性配置类,在Spring启动时根据spring.factories自动加载。

@Configuration
@ConditionalOnClass(TestService.class)
@EnableConfigurationProperties(TestConfig.class)
public class TestAutoConfiguration {

    @Autowired
    private TestConfig testConfig;

    @Bean
    @ConditionalOnMissingBean(TestService.class)
    public TestService testService() {
        return new TestService(testConfig);
    }
}

配置文件

resource文件夹下新建META-INF文件夹,在META-INF文件夹下新建一个名为spring.factories的文件。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.myspringbootstarter.config.TestAutoConfiguration

编译工程

使用maven命令行编译,或IDEA提供的图形化界面。

mvn clean install -DskipTests

使用自定义starter

新建另一个工程, 并依赖刚编译的starter

<dependency>
    <groupId>com.example</groupId>
    <artifactId>my-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

配置属性

application.properties配置文件中配置自定义starter中所需要的属性。

test.code=1
test.msg=This is msg

测试

注入TestService并打印参数。

@SpringBootApplication
public class MySpringBootStarterTestApplication implements CommandLineRunner {

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

    @Autowired
    private TestService testService;

    @Override
    public void run(String... args) throws Exception {
        String say = testService.say();
        System.out.println(say);
    }
}

相关注解

  • @ConditionalOnClass:只有当指定的classclasspath上时,才会被加载;
  • @ConditionalOnMissingBean:只有当指定的class没有在classpath上时,才会被加载;
  • @EnableConfigurationProperties:开启自动加载指定的配置类;

实现原理

@SpringBootApplication注解中包含了@EnableAutoConfiguration,而@EnableAutoConfiguration注解中又包含了@Import(AutoConfigurationImportSelector.class)注解。

选择需要加载的配置。

//AutoConfigurationImportSelector类
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
            .loadMetadata(this.beanClassLoader);
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
            annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

获取自动配置的实体。

//AutoConfigurationImportSelector类
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
        AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

getCandidateConfigurations获取参与的配置。

SpringFactoriesLoader会从META-INF/spring.factories中读取对应的工厂信息。

当我们启动项目时,会检查META-INF/spring.factorieskeyorg.springframework.boot.autoconfigure.EnableAutoConfiguration的值。

//AutoConfigurationImportSelector类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
            getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
            + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

相关面试题

问:Spring Boot项目能以第三方jar包形式给其他工程依赖吗?这个jar包工程启动类中的@SpringBootApplicationmain方法需不需要去掉?

答:可以打成依赖类型的jar包。也不需要去掉启动类等,只需修改maven插件即可。

代码

https://github.com/fendoudebb/learning/tree/master/java/learn-spring-boot/my-spring-boot-starter

Views: 3,187 · Posted: 2020-04-06

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb/LiteNote

扫描下方二维码关注公众号和小程序↓↓↓

扫描下方二维码关注公众号和小程序↓↓↓


Today On History
Browsing Refresh