• 手把手教你自定义自己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智能提示需要引入

           
                org.springframework.boot
                spring-boot-configuration-processor
                true
            
    

    定义starter#

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

     
                cn.soboys
                rest-api-spring-boot-starter
                1.2.0
            
    

    就是我自己封装的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:
         */
        private Map> 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);
        }
    
    }
    
    
    1. 定义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> 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中央仓库

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

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

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

  • 相关阅读:
    C++|前言
    干货!一文搞定无头浏览器的概念以及在selenium中的应用
    Qt控件按钮大全
    Python笔记 · self,cls,实例方法,静态方法,类方法
    Github每日精选(第71期):自动网页抓取和浏览crawlee
    java计算机毕业设计疆域特色农家乐系统MyBatis+系统+LW文档+源码+调试部署
    python-生成器generator
    与迭代次数有关的一种差值结构
    实用篇-Eureka注册中心
    SpringBoot中几种好用的代码生成器(基于Mybatis-plus生成entity、mapper、xml等)
  • 原文地址:https://www.cnblogs.com/kenx/p/17533917.html