手把手教你自定义自己SpringBoot Starter组件源码剖析

kenx
• 阅读 315

我们知道SpringBoot Starter也就是启动器。是SpringBoot组件化的一大优点。基于这个思想,基于这个思想SpringBoot 才变得非常强大,官方给我们提供很多开箱即用的启动器。

Spring Boot Starter 是 Spring Boot 的一个重要特性,它有以下优点:

  1. 依赖管理:Starter 自动处理项目的依赖关系,使得开发者无需手动添加和管理每个依赖。

  2. 自动配置:Starter 提供了一种自动配置的方式,可以根据你的 classpath 和你定义的属性自动配置 Spring 应用。

  3. 简化开发:通过提供各种服务的 Starter(如数据库、安全、缓存等),极大地简化了开发过程。

  4. 减少样板代码:由于 Starter 的自动配置和依赖管理,开发者可以专注于业务逻辑,而不是配置和基础设施代码。

  5. 快速原型开发:使用 Starter 可以快速创建可运行的原型。

  6. 易于理解和使用:Spring Boot Starter 的设计目标之一就是让非专业的开发者也能快速上手。

  7. 社区支持:除了官方提供的 Starter,还有大量的社区提供的 Starter,可以满足各种特定需求。

我现在手把手教大家如何封装自己的starter 做自己的springboot组件,当然你也可以发布自己的starter 到maven中央仓库供大家使用

剖析SpringBoot自带Starter

我们以WebMvcAutoConfiguration这个自动加载为例

自动配置类要能加载,有一个要求,源码分析结果是,需要在\META-INF\spring.factories中做如下配置

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

这样SpringBoot在启动完成时候,会找到我们引入,的starter 找到\META-INF\spring.factories 属性文件,找到需要自动加载配置的类路径,然后帮我们自动注入到Spring IOC 容器,我们在项目中就可以直接使用了。

这里实现自动加载还要依赖一些注解如:

@Configuration // 指定这个类是个配置类
@ConditionalOnXXX // 在指定条件成立的情况下自动配置类生效
@AutoConfigureOrder //配置类顺序
@AutoConfigureAfter // 在哪个配置类之后
@Bean //给容器中添加组件

@ConfigurationProperties //结合相关的XXXProperties类 来绑定相关的配置
@EnableConfigurationProperties // 让XXXProperties加入到容器中,别人就可以自动装配

自定义自己的starter

剖析了SpringBoot 官方的starter 我们自定义自己的starter,(我们仿照着写)

命名规范

手把手教你自定义自己SpringBoot Starter组件源码剖析

配置提示

如果自定义属性文件中,需要IDEA智能提示需要引入

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

定义starter

这里我以自己封装总结我工作以来总结项目封装的一个SpringBoot starter为例

 <dependency>
            <groupId>cn.soboys</groupId>
            <artifactId>rest-api-spring-boot-starter</artifactId>
            <version>1.2.0</version>
        </dependency>

就是我自己封装的start。已经发布中央仓库。

目前更新版本1.3.0 功能如下

  1. 支持一键配置自定义RestFull API 统一格式返回
  2. 支持RestFull API 错误国际化
  3. 支持全局异常处理,全局参数验证处理
  4. 业务错误断言工具封装,遵循错误优先返回原则
  5. redis工作封装。支持所有key操作工具
  6. RestTemplate 封装 POST,GET 请求工具
  7. 日志集成。自定义日志路径,按照日志等级分类,支持压缩和文件大小分割。按时间显示
  8. 工具库集成 集成了lombok,hutool,commons-lang3,guava。不需要自己单个引入
  9. 集成mybatisPlus一键代码生成

rest-api-spring-boot-starter 仓库地址 github

  1. 自定义配置属性文件
rest-api:
  enabled: false
  logging:
    path: ./logs
  i18n:
    # 若前端无header传参则返回中文信息
    i18n-header: Lang
    default-lang: cn
    message:
      # admin
      internal_server_error:
        en: Internal Server Error
        cn: 系统错误
      not_found:
        en: Not Found
        cn: 请求资源不存在
  1. 定义属性配置类
    package cn.soboys.restapispringbootstarter.i18n;
    
    

import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration;

import java.util.Map; import java.util.Optional;

/**

  • @author 公众号 程序员三时
  • @version 1.0
  • @date 2023/6/26 11:55
  • @webSite https://github.com/coder-amiao
  • / //@PropertySource(value = "classpath:i18n.yaml", factory = YamlPropertySourceFactory.class) @Configuration @ConfigurationProperties(prefix = "rest-api.i18n") @Data public class I18NMessage { /**
    • message-key:lang:message
    • / private Map<String, Map<String, String>> message; /**
    • Default language setting (Default "cn").
    • / private String defaultLang = "cn";
private String i18nHeader = "Lang";


/**
 * get i18n message
 *
 * @param key
 * @param language
 * @return
 */
public String message(I18NKey key, String language) {
    return Optional.ofNullable(message.get(key.key()))
            .map(map -> map.get(language == null ? defaultLang : language))
            .orElse(key.key());
}

/**
 * get i18n message
 *
 * @param key
 * @param language
 * @return
 */
public String message(String key, String language) {
    return Optional.ofNullable(message.get(key))
            .map(map -> map.get(language == null ? defaultLang : language))
            .orElse(key);
}

}


3. 定义BeanAutoConfiguration自动加载配置类

```java
package cn.soboys.restapispringbootstarter.config;

import cn.soboys.restapispringbootstarter.ApplicationRunner;
import cn.soboys.restapispringbootstarter.ExceptionHandler;
import cn.soboys.restapispringbootstarter.ResultHandler;
import cn.soboys.restapispringbootstarter.aop.LimitAspect;
import cn.soboys.restapispringbootstarter.i18n.I18NMessage;
import cn.soboys.restapispringbootstarter.utils.RedisTempUtil;
import cn.soboys.restapispringbootstarter.utils.RestFulTemp;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.List;

/**
 * @author 公众号 程序员三时
 * @version 1.0
 * @date 2023/6/27 11:36
 * @webSite https://github.com/coder-amiao
 */
@Configuration
@ConditionalOnProperty(name = "rest-api.enabled", havingValue = "true")
public class BeanAutoConfiguration {


    @Bean
    public I18NMessage i18NMessage() {
        return new I18NMessage();
    }

    @Bean
    public ResultHandler resultHandler() {
        return new ResultHandler();
    }

    @Bean
    public ExceptionHandler exceptionHandler() {
        return new ExceptionHandler();
    }

    @Bean
    public StartupApplicationListener startupApplicationListener() {
        return new StartupApplicationListener();
    }


    @Bean
    public RestApiProperties restApiProperties() {
        return new RestApiProperties();
    }

    @Bean
    public RestApiProperties.LoggingProperties loggingProperties(RestApiProperties restApiProperties) {
        return restApiProperties.new LoggingProperties();
    }

    @Bean
    public ApplicationRunner applicationRunner() {
        return new ApplicationRunner();
    }




    /**
     * restTemplate 自动注入
     */
    @Configuration
    @ConditionalOnProperty(name = "rest-api.enabled", havingValue = "true")
    class RestTemplateConfig {
        /**
         * 第三方请求要求的默认编码
         */
        private final Charset thirdRequest = Charset.forName("utf-8");

        @Bean
        public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
            RestTemplate restTemplate = new RestTemplate(factory);
            // 处理请求中文乱码问题
            List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
            for (HttpMessageConverter<?> messageConverter : messageConverters) {
                if (messageConverter instanceof StringHttpMessageConverter) {
                    ((StringHttpMessageConverter) messageConverter).setDefaultCharset(thirdRequest);
                }
                if (messageConverter instanceof MappingJackson2HttpMessageConverter) {
                    ((MappingJackson2HttpMessageConverter) messageConverter).setDefaultCharset(thirdRequest);
                }
                if (messageConverter instanceof AllEncompassingFormHttpMessageConverter) {
                    ((AllEncompassingFormHttpMessageConverter) messageConverter).setCharset(thirdRequest);
                }
            }
            return restTemplate;
        }

        @Bean
        public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
            SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
            factory.setConnectTimeout(15000);
            factory.setReadTimeout(5000);
            return factory;
        }


        @Bean
        public RestFulTemp restFulTemp() {
            return new RestFulTemp();
        }

    }

}
  1. 自动装配 在项目

手把手教你自定义自己SpringBoot Starter组件源码剖析

spring.factories 配置自己加载配置类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.soboys.restapispringbootstarter.config.BeanAutoConfiguration,\
cn.soboys.restapispringbootstarter.config.BeanAutoConfiguration.RestTemplateConfig,\
cn.soboys.restapispringbootstarter.utils.RedisTempUtil\

扩展思考,我们可以看到SpringBoot官方stater 很多启用都类似@Enablexxx注解 这个怎么实现。我的rest-api-spring-boot-starter 1.3.0已经实现不需要在application.properties配置一行 直接在启动类或者配置类使用EnableRestFullApi就可以使用全部功能

完善文档使用可以看我

SpringBoot定义优雅全局统一Restful API 响应框架完结撒花篇封装starter组件

这篇文章

到此自己定义starter就写完了 接下来就是打包,发布到maven中央仓库

我会在 下一篇文章继续分享

留下你的思考,关注公众 程序员三时

持续输出优质内容 希望给你带来一点启发和帮助

点赞
收藏
评论区
推荐文章
kenx kenx
3年前
SpringBoot 默认json解析器详解和字段序列化自定义
前言在我们开发项目API接口的时候,一些没有数据的字段会默认返回NULL,数字类型也会是NULL,这个时候前端希望字符串能够统一返回空字符,数字默认返回0,那我们就需要自定义json序列化处理SpringBoot默认的json解析方案我们知道在springboot中有默认的json解析器,SpringBoot中默认使用的Json解析技术框架是ja
Easter79 Easter79
3年前
springboot框架
  SpringBoot框架,就是一个简单的web应用。SpringBoot,看名字就知道是Spring的引导,就是用于启动Spring的,使得Spring的学习和使用变得快速方便。不仅适合替换原有的工程结构,更适合微服务开发。SpringCloud基于SpringBoot,为微服务体系开发中的架构问题,提供了一整套的解决方案——服务注册与发现
Stella981 Stella981
3年前
SpringBoot Starter大全,都帮你整理好了!
来源:https://www.cnblogs.com/zeussbook/p/11492733.html啥是应用启动器?springboot集成了spring的很多模块,比如tomcat、redis等等。你用springboot搭建项目,只需要在pom.xml引入相关的依赖,和在配置文件中简单的配置就可以使用相应模块了。非常方便,springboo
Stella981 Stella981
3年前
Spring Boot自定义starter必知必会条件
SpringBoot自定义starter必知必会前言在目前的SpringBoot框架中,不管是SpringBoot官方还是非官方,都提供了非常多的starter系列组件,助力开发者在企业应用中的开发,提升研发人员的工作效率,SpringBoot框架提出的约定大于配置的规则,确实帮助开发者简化了以前SpringMVC时代的很多繁杂的
Stella981 Stella981
3年前
Spring Boot(六):自定义starter
在springboot中,使用最多的就是starter。starter可以理解为一个可拔插式的插件,例如,你想使用jdbc插件,那么可以使用springbootstarterjdbc。随着版本的推移Starter家族成员也与日俱增。在传统Maven项目中通常将一些层、组件拆分为模块来管理,以便相互依赖复用,在SpringBoot项目中我们则可以创建自
Stella981 Stella981
3年前
Spring Boot中使用JavaMailSender发送邮件
相信使用过Spring的众多开发者都知道Spring提供了非常好用的JavaMailSender接口实现邮件发送。在SpringBoot的Starter模块中也为此提供了自动化配置。下面通过实例看看如何在SpringBoot中使用JavaMailSender发送邮件。快速入门在SpringBoot的工程中的pom.xm
Stella981 Stella981
3年前
Spirngboot
一.SpringBootStarter简介Starter是SpringBoot中的一个非常重要的概念,Starter相当于模块,它能将模块所需的依赖整合起来并对模块内的Bean根据环境(条件)进行自动配置。使用者只需要依赖相应功能的Starter,无需做过多的配置和依赖,SpringBoot就能自
Stella981 Stella981
3年前
Angular之自定义组件添加默认样式
Angular的核心思想之一就是:组件化。组件化可以使我们的代码更好的复用。在使用官方提供的Angular库AngularMaterial时,细心的同学就会发现,Material的每一个组件都有它自己样式,如:按钮:matbutton工具条:mattoolbar表格
Stella981 Stella981
3年前
SpringBoot 2.x 集成QQ邮箱、网易系邮箱、Gmail邮箱发送邮件
在Spring中提供了非常好用的JavaMailSender接口实现邮件发送,在SpringBoot的Starter模块中也为此提供了自动化配置。项目源码已托管在GiteeSpringBoot\_Guide(https://gitee.com/javen205/SpringBoot_Guide.git"SpringBoot_Guide")