zoukankan      html  css  js  c++  java
  • 实用主义之自定义SpringBootStarter

    今天以实用角度切入,来示范自定义 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
        }
      ]
    }
    

    使用效果:

    image-20200516184327486

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

    总结

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

    代码下载

  • 相关阅读:
    ajax学习笔记
    CSS3伪类
    《HTML5与CSS3基础教程》学习笔记 ——Four Day
    《HTML5与CSS3基础教程》学习笔记 ——Three Day
    《HTML5与CSS3基础教程》学习笔记 ——Two Day
    《HTML5与CSS3基础教程》学习笔记 ——One Day
    js面向对象笔记
    《锋利的jQuery》心得笔记--Four Sections
    《锋利的jQuery》心得笔记--Three Sections
    《锋利的jQuery》心得笔记--Two Sections
  • 原文地址:https://www.cnblogs.com/vcmq/p/custom-springboot-starter.html
Copyright © 2011-2022 走看看