自定义Spring-boot-starter

自定义Spring Boot AutoConfiguration和Spring-boot-starter

前言

自定义spring-boot属性文章中,我们能够自定义属性,并在application.yml文件中配置,其实自定义属性一般常和自动配置结合使用。这里讲一下如何自定义自定配置。

场景

什么情况下我们会编写针对于spring boot 的自动配置类和自定义的spring-boot-starter呢?

目前很大一部分应用开发是基于spring boot的

  • 所开发的框架开源并适配spring boot,如Druid、Mybatis-plus、Feign.
  • 公司内部成熟的自己开发的框架,为了支持更多的场景,针对spring boot进行适配

官方文档

spring boot关于自定义自动配置的详细文档以及示例项目.一般自动配置会封装为一个spring-boot-starter的jar,对外提供

设计结构

目前各种框架的spring-boot-starter基本有两种。

  1. 框架源码和starter独立的,在提供starter的时候,引入框架的依赖。这时候使用者只需引入框架的starter即可。不需要引用框架。采取这种方式的框架如阿里的Druid数据源
  2. 框架源码和starter不是独立的,要想在spring-boot应用中自动配置,要引入两个依赖,框架的主要依赖,和对应的starter依赖。采取这种方式的框架如Mybatis-plus

这里采用第一种方式,即引入starter即可使用对应框架的全部功能。本文示例框架为自定义的一个小型项目,名字为my-app

一个标准的spring-boot-starter结构原则基本如下:

  • pom文件名字为框架-spring-boot-starter。本文是my-app-spring-boot-starter

  • 在starter项目中,要有名为autoconfigure的包,自动配置类放置在此包下。
    在这里插入图片描述

  • 其他的属性类放置在autocinfigure包下的子包下,子包自定义命名

  • 在resources文件夹下的META-INF文件夹下建立spring.factories文件

框架一般都带有具体的属性,对于自定义属性

  • 需要在META-INF下建立spring-configuration-metadata.json

编码

引入依赖

<?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.mycompany.app</groupId>
  <artifactId>my-app-spring-boot-starter</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>my-app-spring-boot-starter</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <!--引入spring-boot依赖,放在dependencyManagent标签中。关于imoprt的使用见maven使用的文章-->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.3.3.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
<!--    自动配置类所需依赖,官方建议加上这个依赖-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
<!--自定义属性application.yml中的属性所需依赖-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
<!--引入框架源码依赖-->
    <dependency>
      <groupId>com.mycompany.app</groupId>
      <artifactId>my-app</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--test依赖-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
      <plugins>
        <!--打包插件-->
        <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.3.3.RELEASE</version>
        </plugin>
      </plugins>
  </build>
</project>

关于上述pom文件中spring-boot-dependencies的使用疑问,详见Maven的使用

编写自定义属性文件spring-configuration-metadata.json

该文件放置在resources文件夹下的META-INF文件夹下

{
  "groups": [
    {
      "name": "my-app",
      "type": "com.mycompany.app.autoconfigure.properties.MyAppProperties",
      "sourceType": "com.mycompany.app.autoconfigure.properties.MyAppProperties"
    }
  ],
  "properties": [
    {
      "name": "my-app.enabled",
      "type": "java.lang.Boolean",
      "defaultValue": false,
      "sourceType": "com.mycompany.app.autoconfigure.properties.MyAppProperties"
    },
    {
      "name": "my-app.info",
      "type": "java.lang.String",
      "sourceType": "com.mycompany.app.autoconfigure.properties.MyAppProperties"
    }
  ]
}

自定义属性文件,基本由groups和properties两大部分组成。更详细的文档介绍见官方文档

建立自定义属性映射的Java类

上面说过,starter项目下都要放在autoconfigure下,对于不属于自动配置类的内容,建立子包。这里针对自定义的属性类,建立子包名为properties的包,并在该包下建立建立属性映射类,名为MyAppProperties。并使用@ConfigurationProperties注解标注,prefix属性是指具体属性的父级名字,这里对应的是groups里的内容

/**
 * @author jacksparrow414
 * @date 2021/2/3
 */
@ConfigurationProperties(prefix = "my-app")
public class MyAppProperties {
    
    private Boolean enabled;
    
    private String info;
    
    public Boolean getEnabled() {
        return enabled;
    }
    
    public void setEnabled(final Boolean enabled) {
        this.enabled = enabled;
    }
    
    public String getInfo() {
        return info;
    }
    
    public void setInfo(final String info) {
        this.info = info;
    }
}

此类的完全限定名对应上述json文件中的sourceType属性。

建立自动配置类

一般spring-boot的@ConditionalXXX注解使用场景基本就在自动配置类这里。@Conditional相关注解文档

目标

建立此配置类的目标是,当项目中引用了my-app依赖,并且spring容器中没有MyAppLog相关的bean,则自动配置

Java类
@ConditionalOnProperty(prefix = "my-app", name = "enabled", havingValue = "true", matchIfMissing = false)
@ConditionalOnClass(MyAppLog.class)
@Configuration
@EnableConfigurationProperties(value = {MyAppProperties.class})
public class MyAppAutoConfiguration {
    
    @ConditionalOnMissingBean(MyAppLog.class)
    @Bean
    public MyAppLog myAppDefaultLog() {
        return new MyAppLogDefaultImpl();
    }
}

此自动配置类生效的条件是my-app.enabled的属性为true,此配置才生效

建立spring.factories

上面建立好自动配置类后,将其放置在spring.factories文件中。文件内容格式为

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.mycompany.app.autoconfigure.MyAppAutoConfiguration

Spring-boot的自动配置类的完全限定名=自定义自动配置类的完全限定名,如果有多个,以逗号分隔开
在这里插入图片描述

整个项目结构图

在这里插入图片描述

测试

对于自动配置的测试,spring-boot提供了ApplicationContextRunner进行验证详细文档

测试类如下

public class MyAppAutoConfigurationTest {
    
    private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
    
    @Test
    public void assertMyAppAutoConfiguration() {
        contextRunner
            .withPropertyValues("my-app.enabled=true")
            .withUserConfiguration(MyAppAutoConfiguration.class)
            .run(context -> {
            context.getBean("myAppDefaultLog");
        });
    }
}

对于我们自定义的属性的条件生效测试(@ConditionOnProperty),使用withPropertyValues进行赋值,即使在默认的application.yml文件中填写属性,test测试也拿不到属性

而对于其他一些特殊的注解@ConditionalOnMissingClass,如果想要测试其是否生效,spring-boot也提供了对应的测试方法withClassLoader
在这里插入图片描述

实际使用

将my-app-spring-boot-starter打包,在另外一个项目中使用

引入依赖

        <dependency>
            <groupId>com.mycompany.app</groupId>-->
            <artifactId>my-app-spring-boot-starter</artifactId>-->
           <version>1.0-SNAPSHOT</version>-->
        </dependency>

配置属性

在application.yml文件中配置

my-app:
  enabled: true
  info: "myAppTest"

启动项目

在项目主类上添加@EnableAutoConfiguration,高版本的spring-boot可以不加,因为@SpringBootApplication注解中已经包含了此注解

启动项目发现,在我们外部没有配置MyAppLog的相关bean的情况下,my-app框架使用默认的实现,打印出default Impl信息

使用自定义配置覆盖默认配置

my-app框架同时提供了拓展的功能,只要实现MyAppLog接口并配置即可

@Configuration
@AutoConfigureBefore(MyAppAutoConfiguration.class)
public class CustomMyAppConfiguration {

    @Bean
    public MyAppLog myCustomLog() {
        return new MyCustomLog();
    }
}

要求此配置在自动配置类前生效。这样,就会使用我们自己拓展的实现,而不是自动配置的默认实现

结束

至此为止,自定义属性,并利用自定义属性开启我们的自定义的自动配置,到测试,到实际使用,整体流程就结束了

项目中用到的代码

  1. my-app框架源码下载地址
  2. my-app-spring-boot-starter源码下载地址
  3. 实际项目中的使用示例代码

使用须知

按照顺序下载源码,下载之后按照顺序执行

mvn install

命令,将源码对应的依赖install到本地,再重新刷新引用了相关依赖的pom文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值