[WEB series] SpringBootStarter component development

brief introduction

starter is a service (or plug-in)

  1. So that developers using a function do not need to pay attention to the processing of various dependent libraries and specific configuration information. Spring Boot automatically finds the required beans through the classes under the classpath path and weaves them into beans.
  2. In short: component development thinking, improve code reusability and avoid repeated wheel making!!

Knowledge points

  • The naming method of the project is [name] - spring boot starter (the official naming method is spring boot starter - [name])
  • In POM Add the dependencies required by starter in XML
  • Create starter related classes (at least one autoconfiguration class)
  • Create a META-INF folder (srping.factories) under the resource folder

actual combat

Establish a swagger spring boot starter to simplify the process of configuring swagger when building a project

General directory structure

General structure

The naming method of swagger spring boot starter is consistent, which also indicates that this project is not an official spring boot project

Configure POM xml

Add spring core development package and swagger dependency

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.13</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.mobaijun</groupId>
    <artifactId>swagger-spring-boot-starter</artifactId>
    <version>2.4.13</version>

    <name>swagger-spring-boot-starter</name>
    <inceptionYear>2022</inceptionYear>
    <description>swagger-spring-boot-starter</description>
    <url>https://github.com/mobaijun/swagger-spring-boot-starter</url>

    <!-- Version information -->
    <properties>
        <java.version>1.8</java.version>
        <swagger-ui.version>3.0.3</swagger-ui.version>
        <swagger.version>3.0.0</swagger.version>
        <nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version>
        <maven-release-plugin.version>3.0.0-M1</maven-release-plugin.version>
        <maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>
        <maven-source-plugin.version>3.1.0</maven-source-plugin.version>
        <maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
    </properties>

    <!--  licence  -->
    <licenses>
        <license>
            <name>Apache License, Version 2.0</name>
            <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
            <distribution>repo</distribution>
            <comments>A business-friendly OSS license</comments>
        </license>
    </licenses>


    <!--  GitHub issues -->
    <issueManagement>
        <system>GitHub Issues</system>
        <url>https://github.com/mobaijun/swagger-spring-boot-starter/issues</url>
    </issueManagement>

    <scm>
        <url>https://github.com/mobaijun/swagger-spring-boot-starter</url>
        <connection>https://github.com/mobaijun/swagger-spring-boot-starter.git</connection>
        <developerConnection>https://github.com/mobaijun</developerConnection>
    </scm>

    <!--  personal information  -->
    <developers>
        <developer>
            <name>mobaijun</name>
            <email>wljmobai@gmail.com</email>
            <url>https://www.mobaijun.com</url>
        </developer>
    </developers>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- swagger 3.0 Core dependency package -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>${swagger.version}</version>
        </dependency>

        <!-- swagger-ui -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <!--When quoting, please maven Central warehouse search 2.X Latest version number-->
            <version>${swagger-ui.version}</version>
        </dependency>
        <dependency>
            <!-- Core dependency package -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
    </dependencies>
</project>
copy
  • Dependency graph
  • Core development package

Core autoconfiguration

Explanation:

Auto configuration, spring boot has an @ EnableAutoConfiguration annotation, which reads spring The class specified under EnableAutoConfiguration in the factories file is used to initialize all methods added with @ bean under the specified class and initialize this bean

Property configuration class

package com.mobaijun.swagger.prop;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

/**
 * Software: IntelliJ IDEA 2021.3.2
 * ClassName: SwaggerProperties
 * Class description: Swagger configuration read class
 *
 * @author MoBaiJun 2022/3/12 17:24 ---- https://www.mobaijun.com
 */
@ConfigurationProperties(SwaggerProperties.PREFIX)
@EnableConfigurationProperties(SwaggerProperties.class)
public class SwaggerProperties {

    /**
     * identification
     */
    public static final String PREFIX = "swagger";
}
copy

Code Description:

Use the @ ConfigurationProperties annotation to set the prefix in application Through swagger. In YML XXX = to set.

Property configuration class

package com.mobaijun.swagger.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;

/**
 * Software: IntelliJ IDEA 2021.3.2
 * ClassName: SwaggerConfiguration
 * Class description:
 *
 * @author MoBaiJun 2022/4/26 14:57
 */
@Configuration
@ConditionalOnProperty(name = "swagger.enable", matchIfMissing = true)
public class SwaggerConfiguration {
}
copy

Auto configuration class

package com.mobaijun.swagger.config;

import com.google.common.collect.Lists;
import com.mobaijun.swagger.prop.SwaggerProperties;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.ApiKeyVehicle;

import java.util.Collections;
import java.util.List;

/**
 * Software: IntelliJ IDEA 2021.3.2
 * ClassName: SwaggerConfig
 * Class description: swagger configuration class
 *
 * @author MoBaiJun 2022/4/26 9:09
 */
@Configuration
@Import({SwaggerConfiguration.class})
public class SwaggerAutoConfiguration {

    private final Logger log = LoggerFactory.getLogger(SwaggerAutoConfiguration.class);

    @Bean
    @ConditionalOnMissingBean
    public SwaggerProperties swaggerProperties() {
        return new SwaggerProperties();
    }

    /**
     * # Common notes
     * 1,@Api: Used in the controller class to describe the API interface
     * 2,@ApiOperation: Describe interface method
     * 3,@ApiModel: Description object
     * 4,@ApiModelProperty: Describe object properties
     * 5,@ApiImplicitParams: Describe interface parameters
     * 6,@ApiResponses: Describe interface response
     * 7,@ApiIgnore: Ignore interface methods
     * 8,Access address: http://localhost:8003/swagger-ui/index.html#/
     * 9,doc Document access address: http://localhost:8003/doc.html
     */
    @Bean
    public Docket createRestApi(SwaggerProperties swaggerProperties) {
        log.info("============================ Swagger Api Build successful ============================");
        return new Docket(DocumentationType.SWAGGER_2)
                // Enable swagger / production environment shutdown
                .enable(swaggerProperties.getEnable())
                // server address
                .host(swaggerProperties.getHost())
                // Setting the name of the Docket can realize multiple dockets and grouping
                .apiInfo(apiInfo(swaggerProperties))
                .select()
                // withMethodAnnotation scans all API s containing (@ ApiOperation), which is more flexible in this way
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                // The regular matching request path is allocated to the current packet and all interfaces
                .paths(PathSelectors.any())
                .build()
                // Group name
                .groupName(swaggerProperties.getGroupName())
                // Global application of authorization information
                .securityContexts(securityContexts())
                // Authorization information setting, necessary header token and other authentication information
                .securitySchemes(apiKeys());
    }

    /**
     * API The top half of the page displays information
     */
    private ApiInfo apiInfo(SwaggerProperties sp) {
        return new ApiInfoBuilder()
                // title
                .title(sp.getTitle())
                // explain
                .description(sp.getDescription())
                // Official website
                .termsOfServiceUrl(sp.getTermsOfServiceUrl())
                // licence
                .license(sp.getLicense())
                // License address
                .licenseUrl(sp.getLicenseUrl())
                // Author information
                .contact(new Contact(
                        // author
                        sp.getContact().getAuthor(),
                        // Blog address
                        sp.getContact().getUrl(),
                        // mailbox
                        sp.getContact().getEmail()))
                // edition
                .version(sp.getVersion())
                .build();
    }

    /**
     * Set authorization information
     */
    private List<SecurityScheme> apiKeys() {
        return Lists.newArrayList(new ApiKey(swaggerProperties().getAuthorization().getHeader(),
                swaggerProperties().getAuthorization().getToken(), ApiKeyVehicle.HEADER.getValue()));
    }

    /**
     * Global application of authorization information
     */
    private List<SecurityContext> securityContexts() {
        return Lists.newArrayList(SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex(swaggerProperties().getAuthorization().getAuthRegex()))
                .build());
    }

    /**
     * Configure the default global authentication policy; In the returned SecurityReference, the reference is the name in the ApiKey object. Global authentication can only be enabled if it is consistent
     */
    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Collections.singletonList(SecurityReference.builder()
                .reference(swaggerProperties().getAuthorization().getHeader())
                .scopes(authorizationScopes).build());
    }
}
copy

Extended annotation

package com.mobaijun.swagger.annotation;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.mobaijun.swagger.config.SwaggerAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author MoBaiJun 2022/4/26 9:26
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@EnableSwagger2
@EnableKnife4j
@Import({BeanValidatorPluginsConfiguration.class, SwaggerAutoConfiguration.class})
public @interface EnableSwagger {
}
copy

Source address: mobaijun/swagger-spring-boot-starter (github.com)

Registration configuration

Under src/main/resource, create a new meta-info / spring Factories file. The settings are as follows:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mobaijun.swagger.config.SwaggerConfiguration
copy

If there are multiple, they can be separated by commas, as follows:

# example
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
copy

Packaging and publishing

$ mvn clean install
copy

Quick start

Because the starter has been uploaded to the maven central warehouse, it can be directly copied and used in your project

<dependency>
    <groupId>com.mobaijun</groupId>
    <artifactId>swagger-spring-boot-starter</artifactId>
    <version>2.4.13</version>
</dependency>
copy

Configuration description

server:
  port: 8002

swagger:
  # Open swagger
  enable: true
  # title
  title: spring-boot-swagger-demo
  # Service address
  host: localhost:${server.port}
  # edition
  version: 1.0.0
  # Group name
  group-name: R & D department
  # describe
  description: This is a demo
  # Program address
  terms-of-service-url: https://localhost:${server.port}/index.html
  # Author information configuration
  contact:
    # Author information
    author: mobaijun
    # Blog address or official website address
    url: https://www.mobaijun.com
    # Configure mailbox
    email: mobaijun8@163.com
copy

For details, please refer to the project source code readme MD document description

summary

No matter what the architecture of the project is, we should pursue the reusability of the code. In particular, some reusable functions, such as log, cache, result api, database and other operations, can be extracted and made into components. When developing new projects in the future, we can directly introduce dependency, so that developers can focus more on business development. We recommend that you develop a starter suitable for your own business as the company's infrastructure, After all, repeated wheel making is meaningless, and it is terrible to quote private bags for maintenance!!!

Posted by Ajita on Thu, 12 May 2022 11:35:15 +0300