关于springboot:手把手教你自定义自己SpringBoot-Starter组件源码剖析

咱们晓得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,(咱们仿照着写)

命名标准

配置提醒

如果自定义属性文件中,须要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);
     }
    
    }
    
  2. 定义BeanAutoConfiguration主动加载配置类
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. 主动拆卸
    在我的项目

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地方仓库

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

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

继续输入优质内容 心愿给你带来一点启发和帮忙

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理