自定义spring boot starter

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);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容