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

0次阅读

共计 7656 个字符,预计需要花费 20 分钟才能阅读完成。

咱们晓得 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 地方仓库

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

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

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

正文完
 0