码迷,mamicode.com
首页 > 编程语言 > 详细

实用主义之自定义SpringBootStarter

时间:2020-05-16 20:56:43      阅读:73      评论:0      收藏:0      [点我收藏+]

标签:dde   enabled   文件内容   more   初始   work   mat   tst   tps   

今天以实用角度切入,来示范自定义 springboot starter 在项目开发过程中的实际应用。

SpringBoot 相对于 Spring 最大的优点就是提供了相当数量的 starter,只需引入 starter 进行极少量的配置就可使用相应的功能,有效减少 Spring 冗余复杂的配置文件。但日常工作当中,我们自己也会封装一些比较通用的代码 jar 包,引入到新的项目中很难避免添加相应的配置,比如 Bean 的声明。借鉴 SpringBoot 官方做法,我们自己也可以自定义所需要的 Starter。

今天以统一项目日期格式化功能为例,从头到尾地介绍如何自定义一个 Starter。

本文首发个人技术博客:http://nullpointer.pw/http://nullpointer.pw/custom-springboot-starter.html

引入 Starter 初衷

对于一个需要提供 HTTP 接口的服务来说,日期的格式化为友好的格式返回是一个无法避免的问题。实体类结构如下:

@Data
public class Example {
    private LocalDateTime localDateTime;
    private LocalDate localDate;
    private Date date;
    private LocalTime localTime;
}

做法有很多,第一种是比如数据库实体类字段使用的是 Date 类型,LocalDateTime 类型,返回给前端的 VO 中字段则使用 Long 类型或者 String 类型,在实体类进行转换的过程中转换时间类型。

第二种方式是可以使用@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")注解的方式,不过这种方式就需要在所有的 VO 上添加这个注解。

第三种方式是还可以在 application.yml 中配置如下

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss

前两种方式都比较 low,暂且不谈,第三种方式可以实现统一全局 Date 类型的格式化,但是随着 Java8 的广泛应用,Java8 新的 Time 相关类使用更多,而第三种方式无法实现 LocalDateTime 等格式化。

访问接口 http://127.0.0.1:8083/learning-example/test

未添加配置前返回:

{
    "data": {
        "localDateTime": "2020-05-14T10:15:13.311",
        "localDate": "2020-05-14",
        "date": "2020-05-14T02:15:13.306+0000",
        "localTime": "10:15:13.311"
    },
    "success": true
}

添加配置后返回:

{
    "data": {
        "localDateTime": "2020-05-14T10:17:37.437",
        "localDate": "2020-05-14",
        "date": "2020-05-14 02:17:37",
        "localTime": "10:17:37.437"
    },
    "success": true
}

发现即便添加 spring.jackson.date-format 配置,也仅仅对 Date 类型的生效完成格式化,而 Java 8 新的时间对象均未起到作用。

下面通过解决以上问题过程,来示范如何自定义一个 SpringBoot Starter

自定义 Starter

创建一个 starter 工程

spring Boot starter 的工程官方给出了规范,如下

官方命名空间

  • 前缀:spring-boot-starter-
  • 模式:spring-boot-starter-模块名
  • 举例:spring-boot-starter-web、spring-boot-starter-jdbc

自定义命名空间

  • 后缀:-spring-boot-starter
  • 模式:模块-spring-boot-starter
  • 举例:mybatis-spring-boot-starter

新建工程命名为 common-spring-boot-starter,添加相关依赖

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
  </dependency>
  <dependency>
    <!--生成属性提示-->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
  </dependency>
  <dependency>
    <!--jackson -->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
  </dependency>
</dependencies>

定义自动配置类

为了使 starter 更灵活,便于自定义,比如是否启用全局格式化,格式化的格式等,提供 starter 配置开关来控制。

通过配合使用 @ConditionalOnProperty 注解,来达到自定义是否开启全局格式化开关的目的,只有当配置中存在 common.date-format.enabled ,且值为 true 时,才启用。

通过 @EnableConfigurationProperties 注解加载配置,可以自行定义格式化的格式,比如 LocalDate 默认格式为 yyyy-MM-dd,可以在配置文件中修改为 yyyy/MM/dd,可以做到相当灵活。

@Configuration
@EnableConfigurationProperties(DateFormatProperties.class)
@ConditionalOnProperty(prefix = "common.date-format", name = "enabled", matchIfMissing = false, havingValue = "true")
public class DateFormatAutoConfiguration {
    private DateFormatProperties dateFormatProperties;
    public DateFormatAutoConfiguration(DateFormatProperties dateFormatProperties) {
        this.dateFormatProperties = dateFormatProperties;
    }

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer() {
        return builder -> {
            builder.locale(Locale.CHINA);
            builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
            builder.simpleDateFormat(dateFormatProperties.getDatePattern());
            builder.modules(new JavaTimeModule());
        };
    }

    class JavaTimeModule extends SimpleModule {
        JavaTimeModule() {
            super(PackageVersion.VERSION);
            DateTimeFormatter localDateTimeFormatter = DateTimeFormatter.ofPattern(dateFormatProperties.getLocalDateTimePattern());
            DateTimeFormatter localDateFormatter = DateTimeFormatter.ofPattern(dateFormatProperties.getLocalDatePattern());
            DateTimeFormatter localTimeFormatter = DateTimeFormatter.ofPattern(dateFormatProperties.getLocalTimePattern());

            this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(localDateTimeFormatter));
            this.addSerializer(LocalDate.class, new LocalDateSerializer(localDateFormatter));
            this.addSerializer(LocalTime.class, new LocalTimeSerializer(localTimeFormatter));
            this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(localDateTimeFormatter));
            this.addDeserializer(LocalDate.class, new LocalDateDeserializer(localDateFormatter));
            this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(localTimeFormatter));
        }
    }
}
/**
 * @author WeJan
 * @since 2020-05-14
 */
@ConfigurationProperties("common.date-format")
public class DateFormatProperties {

    /**
     * Date Format Pattern
     */
    private String datePattern = "yyyy-MM-dd HH:mm:ss";

    /**
     * LocalDateTime Format Pattern
     */
    private String localDateTimePattern = "yyyy-MM-dd HH:mm:ss";

    /**
     * LocalDate Format Pattern
     */
    private String localDatePattern = "yyyy-MM-dd";

    /**
     * LocalTime Format Pattern
     */
    private String localTimePattern = "HH:mm:ss";
		
  	// TODO 篇幅限制,此处省略 getter/setter 方法
}

让配置类生效

上文仅定义了自动配置类,但是想要让 Spring 加载这个配置类,还需要在 starter 工程中添加 spring.factories

resources 目录下建立 META-INF 文件夹,再创建 spring.factories 文件,再指定自动配置类的全限定类名。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=pw.nullpointer.boot.autoconfigure.date.DateFormatAutoConfiguration

Install 本地 maven 仓库

执行 mvn install,安装到本地 maven 仓库

测试 starter

引入自定义的 starter

创建 example 工程,引入刚才 install 到 maven 仓库的 starter

<dependency>
  <groupId>pw.nullpointer.tp</groupId>
  <artifactId>common-spring-boot-starter</artifactId>
  <version>1.0.0</version>
</dependency>

开启 starter 时间格式化功能

上文中自定义的 starter 默认设置的是不启用,因此需要在配置文件application.yml中启用一下格式化功能。

common:
  date-format:
    enabled: true  # 开启时间格式化功能

启动项目测试

访问接口:http://127.0.0.1:8083/learning-example/test,返回如下:

{
    "data": {
        "localDateTime": "2020-05-14 11:22:29",
        "localDate": "2020-05-14",
        "date": "2020-05-14 11:22:29",
        "localTime": "11:22:29"
    },
    "success": true
}

可以看到格式化已经生效了,顺便测试一下自定义格式化,修改 application.yml 文件

common:
  date-format:
    enabled: true
    local-date-pattern: yyyy/MM/dd

再次测试,接口返回:

{
    "data": {
        "localDateTime": "2020-05-14 11:27:16",
        "localDate": "2020/05/14",
        "date": "2020-05-14 11:27:16",
        "localTime": "11:27:16"
    },
    "success": true
}

优化 starter 属性提示

在使用 springboot 官方 starter 过程中,在编写 application.yml 的过程中,有哪些属性,IDEA 会自动提示出来,为了达到这种效果,需要引入依赖 spring-boot-configuration-processor,引入后,starter 在打包的过程中,会自动生成 spring-configuration-metadata.json 文件,使用 starter 过程中便有相应的提示了。前文初始化 starter 的过程中,我们已经引入过这个依赖了,所以就不用再重复引入了。

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

但只根据这个依赖生成的spring-configuration-metadata.json 文件,使用过程中可以发现 common.date-format.enabled 这个属性开关是没有提示出来的,可以猜测到 spring-boot-configuration-processor是根据属性生成的,但是 enabled 这个非属性,所以无法生成。为了方便使用 starter 的人,我们可以追加一个 additional-spring-configuration-metadata.json 文件,手动添加相应的提示,文件内容:

{
  "properties": [
    {
      "name": "common.date-format.enabled",
      "type": "java.lang.Boolean",
      "description": "是否开启时间格式化",
      "defaultValue": false
    }
  ]
}

使用效果:

技术图片

测试完毕,为了方便使用,可以将自定义的 starter 发布到公司的私服。mvn deploy

总结

本文通过讲解实现一个项目时间全局格式化功能,如何自定义一个 springboot starter,当然 starter 功能不仅限如此,可以自行触类旁通扩展,比如项目的登录拦截器、通用日志、全局异常处理等工程都可以作为 starter 的一部分来实现,读者若看完有帮助并实践应用,可以评论留言应用的场景。

代码下载

实用主义之自定义SpringBootStarter

标签:dde   enabled   文件内容   more   初始   work   mat   tst   tps   

原文地址:https://www.cnblogs.com/vcmq/p/custom-springboot-starter.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!