Teach you how to customize your own SpringBoot Starter component source code analysis

We know that SpringBoot Starter is also the starter. It is a major advantage of SpringBoot componentization. Based on this idea, based on this idea, SpringBoot has become very powerful, and the official provides us with many out-of-the-box starters.

Spring Boot Starter is an important feature of Spring Boot, which has the following advantages:

  1. Dependency management : Starter automatically handles the dependencies of the project, so that developers do not need to manually add and manage each dependency.

  2. Automatic configuration : Starter provides an automatic configuration method, which can automatically configure Spring applications according to your classpath and the properties you define.

  3. Simplified development : By providing starters of various services (such as database, security, cache, etc.), the development process is greatly simplified.

  4. Reduce Boilerplate Code : Thanks to Starter's automatic configuration and dependency management, developers can focus on business logic rather than configuration and infrastructure code.

  5. Rapid Prototyping : Use Starter to quickly create working prototypes.

  6. Easy to understand and use : One of the design goals of Spring Boot Starter is to allow non-professional developers to get started quickly.

  7. Community support : In addition to the officially provided Starters, there are also a large number of community-provided Starters that can meet various specific needs.

Now I will teach you how to package your own starter to make your own springboot components. Of course, you can also publish your own starter to the maven central warehouse for everyone to use.

Analysis of SpringBoot's own Starter

Let's take the automatic loading of WebMvcAutoConfiguration as an example

There is a requirement for automatic configuration classes to be able to be loaded. The result of source code analysis is that the following configuration needs to be done in \META-INF\spring.factories

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

In this way, when SpringBoot is started, it will find our introduction, the starter finds \META-INF\spring.factoriesthe property file, finds the class path that needs to be automatically loaded and configured, and then automatically injects it into the Spring IOC container for us, and we can use it directly in the project.

The automatic loading here also depends on some annotations such as:

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

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

Customize your own starter

After analyzing SpringBoot's official starter, we customize our own starter, (we copy it)

Naming conventions

configuration tips

If in the custom property file, IDEA smart prompt needs to be introduced

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

define starter

Here I take a SpringBoot starter that summarizes the project package since I encapsulated and summarized my work as an example.

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

It is the start packaged by myself. The central repository has been released.

The current updated version 1.3.0 features are as follows

  1. Support one-click configuration to customize RestFull API uniform format return
  2. Support RestFull API error internationalization
  3. Support global exception handling, global parameter verification processing
  4. Encapsulation of business error assertion tools, following the principle of returning errors first
  5. redis working package. Support all key operation tools
  6. RestTemplate encapsulates POST, GET request tool
  7. Log integration. Customize the log path, classify according to the log level, support compression and file size segmentation. display by time
  8. The tool library integrates lombok, hutool, commons-lang3, and guava. No need to import them individually
  9. Integrated mybatisPlus one-click code generation

rest-api-spring-boot-starter
Warehouse address
github

  1. Custom configuration properties file
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. Define property configuration class
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);
    }

}

  1. Define BeanAutoConfiguration to automatically load configuration classes
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. Autowiring
    in project

spring.factoriesConfigure yourself to load the configuration class

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

Expanding thinking, we can see that many of the SpringBoot official staters are similar to @Enablexxxannotations.
How to achieve this. My rest-api-spring-boot-starter1.3.0 has already been implemented, and all functions can be used directly in the startup class or configuration class without configuring a line in application.propertiesEnableRestFullApi

Perfect document use can see me

SpringBoot defines an elegant and globally unified Restful API response framework Ends the Sprinkle Chapter Encapsulates the starter component

This article

At this point, the definition of the starter is finished, and the next step is to package and publish it to the maven central warehouse .

I will continue to share in the next article

Leave your thoughts and pay attention to public programmers for three hours

Continue to output high-quality content and hope to bring you a little inspiration and help

おすすめ

転載: blog.csdn.net/u011738045/article/details/131589843
おすすめ