自定义spring boot starter

作者: ImushroomT | 来源:发表于2017-07-09 15:19 被阅读0次

    0. 写在前面

    这部分内容将会简要介绍如何定制自己的starter,以及starter的一些基本原理

    1. starter的pom文件配置

    starter本身也是作为一个spring boot的应用,因此需要引入parent,同时引入基本的starter

    <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.3.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    

    为了给可配置的bean属性生成元数据,我们需要引入如下jar包

    <!--将被@ConfigurationProperties注解的类的属性注入到元数据-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
    

    生成的元数据位于jar文件中的META-INF/spring-configurationmetadata. json。元数据本身并不会修改被@ConfigurationProperties修饰的类属性,在我的理解里元数据仅仅只是表示配置类的默认值以及java doc,供调用者便利的了解默认配置有哪些以及默认配置的含义,在idea里面如果有元数据则可以提供良好的代码提示功能以方便了解默认的配置。
    自定义的starter的配置类如下:

    @ConfigurationProperties(prefix = QssProperties.PROP_PREFIX)
    public class QssProperties {
        public static final String PROP_PREFIX = "qss";
    
        /**
         * qss en name
         */
        private String name;
    
        private Integer age = 27;
    
        @NestedConfigurationProperty
        private BasicProperties basicProperties;
    
        public String getName() {
            return name;
        }
    
        public QssProperties setName(String name) {
            this.name = name;
            return this;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public QssProperties setAge(Integer age) {
            this.age = age;
            return this;
        }
    
        public BasicProperties getBasicProperties() {
            return basicProperties;
        }
    
        public QssProperties setBasicProperties(BasicProperties basicProperties) {
            this.basicProperties = basicProperties;
            return this;
        }
    }
    public class BasicProperties {
        private String basic;
    
        public String getBasic() {
            return basic;
        }
    
        public BasicProperties setBasic(String basic) {
            this.basic = basic;
            return this;
        }
    }
    

    生成的元数据配置如下:

    {
      "hints": [],
      "groups": [
        {
          "sourceType": "com.qss.autoconfigure.QssProperties",
          "name": "qss",
          "type": "com.qss.autoconfigure.QssProperties"
        },
        {
          "sourceType": "com.qss.autoconfigure.QssProperties",
          "name": "qss.basic-properties",
          "sourceMethod": "getBasicProperties()",
          "type": "com.qss.autoconfigure.BasicProperties"
        }
      ],
      "properties": [
        {
          "sourceType": "com.qss.autoconfigure.QssProperties",
          "defaultValue": 27,
          "name": "qss.age",
          "type": "java.lang.Integer"
        },
        {
          "sourceType": "com.qss.autoconfigure.BasicProperties",
          "name": "qss.basic-properties.basic",
          "type": "java.lang.String"
        },
        {
          "sourceType": "com.qss.autoconfigure.QssProperties",
          "name": "qss.name",
          "description": "qss en name",
          "type": "java.lang.String"
        }
      ]
    }
    

    group表示元数据的分组,properties表示属性列表,sourceType表示来源类,type表示属性的类型,name表示属性名,defaultValue表示默认值,sourceMethod表示贡献该组属性的方法名,比如这里的getBasicProperties()来获取basicProperties的配置。这里需要注意的是在basicProperties上使用了@NestedConfigurationProperty,该注解会为basicProperties生成单独的一个属性组。如果不添加该注解,则不会生成单独的属性组,而是形成如下的一个属性节点:

    {
          "sourceType": "com.qss.autoconfigure.QssProperties",
          "name": "qss.basic-properties",
          "type": "com.qss.autoconfigure.BasicProperties"
        },
    

    2. starter的入口

    Spring Boot会检查你发布的jar中是否存在 META-INF/spring.factories 文件,
    该文件中以 EnableAutoConfiguration 为key的属性应该列出你的配置类

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
    com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
    

    我们可以使用 @AutoConfigureAfter@AutoConfigureBefore或者@Order 注解为配置类指定特定的顺序,在demo中我使用的配置是:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.qss.autoconfigure.QssStarterConfig
    

    配置类的定义如下,首先需要使用@Configuration,通过@EnableConfigurationProperties可以将被@ConfigurationProperties注解的类生成一个bean。再来看QssService这个bean的配置,我们可以通过一系列@Condition*注解来表示bean的生成条件,这里使用了@ConditionalOnClass({QssService.class}),@ConditionalOnMissingBean({QssService.class})表示生成这个bean需要保证类路径下QssProperties.class这个类并且该bean不存在于spring的上下文。注意@ConditionalOnBean并不保证bean的加载顺序,spring boot只是通过扫描判断是否会生成该bean

    @Configuration
    @EnableConfigurationProperties({QssProperties.class})
    public class QssStarterConfig {
    
        @Autowired
        private QssProperties qssProperties;
    
        @Bean
        @ConditionalOnClass({QssService.class})
        @ConditionalOnMissingBean({QssService.class})
        public QssService getQssService(){
            return new QssService().setProperties(qssProperties);
        }
    }
    

    一些其他的条件注解如下:

    @ConditionalOnWebApplication : web环境
    @ConditionalOnNotWebApplication : 条件是当前不是web应用
    @ConditionalOnProperty : 检查特定属性是否已经配置了特定值
    @ConditionalOnResource : 检查特定的资源是否已经在类路径下
    @ConditionalOnMissingClass : 不包含某个类
    @ConditionalOnSingleCandidate: 表示只能有一个候选bean,如果超过一个,可以使用@Primary指定首选,这样才不会抛出异常
    

    3. bean类的编写

    这里只使用了一个简单的bean,当其他项目引入该starter时,qssService这个bean已经可以直接依赖注入到项目中其他类。

    public class QssService {
    
        private QssProperties properties;
    
        public QssService setProperties(QssProperties properties) {
            this.properties = properties;
            return this;
        }
    
        public String sayHello(){
            return JSON.toJSONString(properties);
        }
    }
    

    相关文章

      网友评论

        本文标题:自定义spring boot starter

        本文链接:https://www.haomeiwen.com/subject/jezzcxtx.html