Analysis and Practice of Spring Boot Starter | JD Cloud Technical Team

introduction

For Java developers, the Spring framework is almost a must. It is an open-source lightweight framework widely used for developing enterprise applications. In recent years, Spring Boot has emerged on the basis of the traditional Spring framework. It not only provides all the functions of Spring, but also makes it easier for developers to use. When using Spring Boot, we often come into contact with various Spring Boot Starters, for example  spring-boot-starter-web. Just add this dependency to the project, and we can start developing the application; after importing  spring-boot-starter-data-jdbc , just fill in the database connection information in the configuration file to connect to the database. In addition, you can switch data source component dependencies at will without modifying business code. How does Spring Boot Starter adapt? Can we implement a Spring Boot Starter ourselves? This article will analyze the principle of Spring Boot Starter and implement a custom Spring Boot Starter component.

1. What is Spring Boot Starter?

Spring Boot Starter is an important concept in Spring Boot. It is a dependency descriptor that can help you simplify configuration. When you need to build a web application, you don't have to go through all the dependent packages and add them to the project's dependency management one by one, but only need one configuration, as in the following example: spring-boot-starter-web

From the example above, we have created a REST application with a relatively small amount of code. Spring officially provides many starters, and third parties can also customize starters. In order to distinguish them, the starters have been standardized as follows: spring-boot-starter-xxx; the starter names provided by third parties are: xxx-spring-boot-starter.

2. Analysis of Spring Boot Starter

The concept of Starter and how to quickly create a REST application were introduced earlier. REST interface development can be done by just adding a dependency and a few lines of code. So, how do we develop without Spring Boot and Starter? How does Spring Boot Starter work? Next, we will analyze pure Spring and Spring Boot Starter respectively by developing Web services and Dubbo services as examples.

Spring

environment dependent

  • JDK 1.8

  • Maven 3

  • Tomcat 8 (requires web container server to start)

  • spring-webmvc 4.3.30.RELEASE

  • dubbo 2.7.23

Development Process

  1. Let me introduce first, this is a standard Maven directory structure and dependent content demo-service 

    <dependencies>
        <!-- SpringMVC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.30.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <!-- 此处需要导入databind包即可, jackson-annotations、jackson-core都不需要显示自己的导入了-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>
    
        <!-- Dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.23</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-x-discovery</artifactId>
            <version>5.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.8.0</version>
        </dependency>
    
        <!-- Demo API -->
        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>demo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    
  2. Since it still needs to rely on Java Web and Web containers to run under Spring XML, it also needs a Web configuration file, and the content configures the SpringMVC entry web/WEB-INF/web.xml 

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!-- Spring监听器 -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:dubbo.xml</param-value>
        </context-param>
    
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:mvc.xml</param-value>
            </init-param>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  3. SpringMVC configuration file and Dubbo configuration file mvc.xml  dubbo.xml

    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <context:component-scan base-package="com.demo.controller"/>
    
        <!-- 开启 MVC 注解驱动 -->
        <mvc:annotation-driven/>
    
        <!-- 访问静态资源 -->
        <mvc:default-servlet-handler/>
    </beans>
    
    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    
        <!-- Dubbo -->
        <dubbo:application name="demo-service"/>
        <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
        <dubbo:protocol name="dubbo" port="20880"/>
        <bean id="demoServiceImpl" class="com.demo.provider.DemoServiceImpl"/>
        <dubbo:service interface="com.demo.api.DemoService" ref="demoServiceImpl"/>
    </beans>
    
  4. Write Controller interface and Dubbo RPC interface

    package com.demo.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloController {
    
        @GetMapping(value = "/say/hello")
        public HelloEntity sayHello() {
            return new HelloEntity("Hello World");
        }
    
    }
    
    package com.demo.provider;
    
    import com.demo.api.DemoService;
    import com.demo.dto.HelloEntity;
    
    public class DemoServiceImpl implements DemoService {
    
        @Override
        public HelloEntity sayHello() {
            return new HelloEntity("Hello World");
        }
    }
    
  5. The above cannot be run independently, and the above needs to be packaged into a package and put into Tomcat to run. war 

analyze

From the above development process, we can see that the entrances are all in  web.xml . There is a listener and a Servlet, as well as initialization parameters  dubbo.xml and  mvc.xml. Before the emergence of Spring Boot, Spring usually used XML configuration to describe beans, or configured annotation-driven and context-scanning methods to parse beans in XML. So we can see that there are two XML files here. After analyzing the source code, we sorted out the following flow from XML tag parsing to Bean parsing. as follows:

  1. Start loading by Tomcat and let Spring load and parse XML through listeners and Servlets. web.xml 

  2. Until a custom tag is parsed, an XML namespace is found or the tag is found. BeanDefinitionParserDelegate#parseCustomElement  mvc:xxx  dubbo:xxx 

  3. DefaultNamespaceHandlerResolver Processing logic: load all jars in a lazy loading manner (the path must be this) and cache them , and find the corresponding processing class through the namespace URI. The SpringMVC and Dubbo namespace processing classes are and META-INF/spring.handlers  handlerMappings  MvcNamespaceHandler  DubboNamespaceHandler

  4. MvcNamespaceHandler and have respectively implemented the method, the content is as follows: The method  registers  the SpringMVC and Dubbo tags correspondingly   . In the previous step,  the label was obtained according to the label  , and the corresponding Bean was registered in the Spring IOC container. Registration logic is not the focus of this article, so I won't repeat it here. So far, the loading process of SpringMVC and Dubbo has been completed. DubboNamespaceHandler  NamespaceHandler#init 


    init BeanDefinitionParserNamespaceHandlerSupport#parsersDefaultNamespaceHandlerResolverBeanDefinitionParser

From the above loading process, we can see that before Spring Boot, Spring mainly relied on XML configuration to start. It will load the custom tags in XML, find the corresponding namespace, and then scan the classpath to  META-INF/spring.handlersfind the namespace processing class to parse the current tag.

Spring Boot

environment dependent

  • JDK 1.8

  • Maven 3

  • spring-boot 2.6.9

  • dubbo 2.7.23

Development Process

  1. Directory structure and Maven dependency content demo-spring-boot 

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    
        <!-- dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.23</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-x-discovery</artifactId>
            <version>5.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.8.0</version>
        </dependency>
    
        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>demo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    
  2. application entry DemoSpringBootApplication

    @SpringBootApplication
    public class DemoSpringBootApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoSpringBootApplication.class, args);
        }
    
    }
    
  3. application.yml The content of the file is only the configuration of Dubbo

    dubbo:
      application:
        name: demo-provider
      protocol:
        port: 20880
        name: dubbo
      registry:
        address: zookeeper://127.0.0.1:2181
    
  4. Write Controller interface and Dubbo RPC interface

    package com.demo.controller;
    
    import com.demo.dto.HelloEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloController {
    
        @GetMapping(value = "/say/hello")
        public HelloEntity sayHello() {
            return new HelloEntity("Hello World");
        }
    
    }
    
    package com.demo.provider;
    
    import com.demo.api.DemoService;
    import com.demo.dto.HelloEntity;
    
    @DubboService 
    public class DemoServiceImpl implements DemoService {
    
        @Override
        public HelloEntity sayHello() {
            return new HelloEntity("Hello World");
        }
    }
    
  5. Since tomcat has been embedded, you only need to run the method directly to run the application spring-boot-starter-web  DemoSpringBootApplication#main 

analyze

From the development process, there is no way to find the analysis entry at the first time. The only entry is in . After source code analysis, the following flow is obtained: DemoSpringBootApplication 

  1. There are annotations on the application class , and the annotation consists of the following three annotations: DemoSpringBootApplication  @SpringBootApplication 

    • @SpringBootConfiguration, mark the current class as a configuration class, which is consistent with the annotation function, and the annotated class corresponds to Spring's XML version of the container. @Configuration  @Configuration 

    • @EnableAutoConfiguration, the key to enable autowiring, consisting of @AutoConfigurationPackage  @Import(AutoConfigurationImportSelector.class)

    • @ComponentScan, according to the current class path to scan classes containing annotations such as , etc., which is equivalent to that in Spring XML . @Service@Controller context:component-scan

  2. Spring Boot auto-assembles the imported class and calls to scan the contents of all jar packages from ClassPath . Since it is passed in , it will only return the value of the key and get a string array of fully qualified class names . @EnableAutoConfiguration  AutoConfigurationImportSelector  SpringFactoriesLoader#loadFactoryNames  META-INF/spring.factories  EnableAutoConfiguration.class org.springframework.boot.autoconfigure.EnableAutoConfiguration  configurations

  3. configurations After deduplication and declarative exclusion, the following filtering and automatic assembly will be performed:

    configurations = getConfigurationClassFilter().filter(configurations)
    

    Divided into two parts: getting filters and performing filtering.

    • getConfigurationClassFilter(), also by SpringFactoriesLoader#loadFactoryNames finding the value META-INF/spring.factories of Key , currently there are only three filters : , , and . org.springframework.boot.autoconfigure.AutoConfigurationImportFilterOnBeanConditionOnClassConditionOnClassCondition 

    • When filtering is performed, some configuration classes will be filtered out according to conditional annotations containing , , etc. on the configuration class. For example, the specification needs to be below to take effect. @ConditionOnBean@ConditionalOnClass@ConditionalOnWebApplication  WebMvcAutoConfiguration  @ConditionOnWebApplication 

  4. After introducing various Configuration configuration classes, the configuration classes are combined to complete Spring Bean parsing and injection. At the same time, Spring Boot also provides many flexible injections for developers. @Bean  @ConditionalXXX 

The above is the automatic assembly process of Spring Boot. Spring Boot uses annotated  @Configuration configuration classes instead of Spring XML to complete Bean injection. Then,  the automatic configuration class in SpringFactoriesLoader the final load will  be implemented to realize the automatic assembly process. META-INF/spring.factoriesRelying on the idea of ​​"agreement is greater than configuration", if the developed Starter wants to take effect, it needs to follow the Spring Boot agreement.

summary

By comparing the development process of Spring and Spring Boot, we can find that Spring Boot uses relatively less code and configuration when completing the independent application development of Web and Dubbo. This is thanks to the autowiring capability of Spring Boot Starter, which is the main function of Spring Boot. Auto-configuration can help simplify and speed up the development process by eliminating the need to define some classes that are part of auto-configuration.

SPI

From the above analysis, we found that both use a mechanism to load the configuration file in the imported jar package to load the corresponding class, that is, SPI (Service Provider Interface)  

SPI (Service Provider Interface) is a built-in service discovery mechanism in Java to improve the scalability of the framework.

Java SPI

Java's built-in SPI parses the file named with the fully qualified name of the interface under the classpath and jar package directory through the class , and loads the interface implementation class specified in the file to complete the call. java.util.ServiceLoader  META-INF/services 

However, Java SPI has certain shortcomings:

  • On-demand loading cannot be achieved. It is necessary to traverse all implementations and instantiate them, and then find the required implementation in the loop.

  • It is not safe for multiple concurrent multithreaded use instances of the class ServiceLoader 

  • When the implementation class cannot be loaded, an exception that is not the real reason is thrown, and the error is difficult to locate.

Spring SPI

Spring SPI follows Java SPI, but there are differences in implementation and Java SPI, but the core mechanism is the same, and the extended development of Spring framework can be achieved without modifying the Spring source code.

  • In Spring XML, it is responsible for parsing and generating the mapping of namespaceUri and NamespaceHandler names, and instantiating them when necessary. DefaultNamespaceHandlerResolver  spring.handlers

  • In Spring Boot, it is responsible for parsing the file and returning all implementation classes/fully qualified class names of the specified interface. SpringFactoriesLoader  spring.factories 

Spring Boot 2.7.0

In this article, Spring Boot auto-assembly uses SPI to load to the specified auto-assembly class name, but after Spring Boot 2.7.0 , the auto-assembly SPI mechanism has been changed and will be discarded. At the same time, relevant The code is removed, and the changes are as follows: EnableAutoConfiguration   META-INF/spring.factories 

  • New annotation: @AutoConfiguration instead of @Configuration

  • The location of the class file to read the automatic assembly is changed to: META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, and the fully qualified class name of the implementation class is one line at a time

  • is responsible for parsing , where is a placeholder for the interface name org.springframework.boot.context.annotation.ImportCandidates#load  META-INF/spring/%s.imports  %s 

3. Practice of Spring Boot Stater

When using or waiting for database operations, a database data source connection pool is usually introduced, such as: , etc. At the same time, you can switch dependencies at will without changing any business code, and developers do not need to pay attention to the underlying implementation. Here we customize A Starter also implements this compatibility. Because we have developed a distributed lock starter and have multiple implementations: Zookeeper, Redis. Spring Boot 2.6.9 version is used here. spring-boot-starter-jdbc  spring-boot-starter-jpa HikariCPDBCP

to develop

Project structure and Maven dependencies

└── src
    ├── main
    │   ├── java
    │   │   └── com.demo.distributed.lock
    │   │      ├── api
    │   │      │   ├── DistributedLock.java
    │   │      │   └── LockInfo.java
    │   │      ├── autoconfigure
    │   │      │   ├── DistributedLockAutoConfiguration.java
    │   │      │   └── DistributedLockProperties.java
    │   │      ├── redis
    │   │      │   └── RedisDistributedLockImpl.java
    │   │      └── zookeeper
    │   │          └── ZookeeperDistributedLockImpl.java
    │   └── resources
    │       └── META-INF
    │           └── spring.factories
<dependencies>
    <!-- Spring Boot 自动装配注解 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>

    <!-- 生成 META-INF/spring-configuration-metadata.json -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>

    
    <!-- Zookeeper -->
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>5.1.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-recipes</artifactId>
        <version>5.1.0</version>
        <scope>provided</scope>
    </dependency>

    <!-- Redis -->
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.23.1</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

In the dependencies, you can see that the Zookeeper and Redis dependencies are set to be used in the compilation and testing phases, and will not be released with the project. That is, the dependency will not be brought when packaging. This setting is more effective in Spring Boot Starter. provided 

Distributed lock interface and implementation

interface

public interface DistributedLock {

    /**
     * 加锁
     */
    LockInfo tryLock(String key, long expire, long waitTime);

    /**
     * 释放锁
     */
    boolean release(LockInfo lock);

}

Redis implementation

public class RedisDistributedLockImpl implements DistributedLock {

    private final RedissonClient client;

    public RedisDistributedLockImpl(RedissonClient client) {
        this.client = client;
    }

    @Override
    public LockInfo tryLock(String key, long expire, long waitTime) {
        //do something
        return null;
    }

    @Override
    public boolean release(LockInfo lock) {
        //do something
        return true;
    }
}

Zookeeper implementation

public class ZookeeperDistributedLockImpl implements DistributedLock {

    private final CuratorFramework client;

    public ZookeeperDistributedLockImpl(CuratorFramework client) {
        this.client = client;
    }

    @Override
    public LockInfo tryLock(String key, long expire, long waitTime) {
        return null;
    }

    @Override
    public boolean release(LockInfo lock) {
        return false;
    }
} 

DistributedLockAutoConfiguration configuration class

@EnableConfigurationProperties(DistributedLockProperties.class)
@Import({DistributedLockAutoConfiguration.Zookeeper.class, DistributedLockAutoConfiguration.Redis.class})
public class DistributedLockAutoConfiguration {

    @Configuration
    @ConditionalOnClass(CuratorFramework.class)
    @ConditionalOnMissingBean(DistributedLock.class)
    @ConditionalOnProperty(name = "distributed.lock.type", havingValue = "zookeeper",
            matchIfMissing = true)
    static class Zookeeper {

        @Bean
        CuratorFramework curatorFramework(DistributedLockProperties properties) {
            //build CuratorFramework client
            return null;
        }


        @Bean
        ZookeeperDistributedLockImpl zookeeperDistributedLock(CuratorFramework client) {
            return new ZookeeperDistributedLockImpl(client);
        }
    }


    @Configuration
    @ConditionalOnClass(RedissonClient.class)
    @ConditionalOnMissingBean(DistributedLock.class)
    @ConditionalOnProperty(name = "distributed.lock.type", havingValue = "redis",
            matchIfMissing = true)
    static class Redis {

        @Bean
        RedissonClient redissonClient(DistributedLockProperties properties) {
            //build RedissonClient client
            return null;
        }

        @Bean
        RedisDistributedLockImpl redisDistributedLock(RedissonClient client) {
            return new RedisDistributedLockImpl(client);
        }
    }
}
  • @EnableConfigurationProperties(DistributedLockProperties.class) Opening the configuration class Properties information will inject the information in the configuration file into the Properties class.

  • @Configuration Configuration Notes

  • @ConditionalOnClass(CuratorFramework.class) Conditional annotations require the existence of the current configuration class to take effect, and the sub-configuration class of Redis is the same. CuratorFramework 

  • @ConditionalOnMissingBean(DistributedLock.class) Conditional annotation, the current configuration class of Bean does not exist in Spring , and the sub-configuration class of Redis is the same. DistributedLock 

  • @ConditionalOnProperty(name = "distributed.lock.type", havingValue = "zookeeper", matchIfMissing = true) Conditional annotation, here it is judged that the configuration file is equal to take effect, if it is not configured, it will be regarded as the default , and the sub-configuration class of Redis is the same. distributed.lock.type  zookeeper  zookeeper

  • @Bean Inject the Bean returned by the method into the Spring IOC container, and the method input parameter contains the dependent Bean

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.demo.distributed.lock.autoconfigure.DistributedLockAutoConfiguration

We only need to put the file down, and it will be loaded by Spring Boot, which is also the idea of ​​Spring Boot that the contract is greater than the configuration. resource/META-INF/spring.factories 

use

Maven dependencies

<dependencies>
    <dependency>
        <groupId>com.demo</groupId>
        <artifactId>distributed-lock-spring-boot-starter</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </dependency>
</dependencies>

<profiles>
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <dependencies>
            <!-- Redis -->
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson</artifactId>
                <version>3.23.1</version>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>test</id>
        <dependencies>
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-framework</artifactId>
                <version>5.1.0</version>
            </dependency>
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-recipes</artifactId>
                <version>5.1.0</version>
            </dependency>
        </dependencies>
    </profile>
</profiles>

Here, the Maven profile function is combined with different environments to rely on different underlying implementations of distributed locks. At the same time, Spring Boot also provides Spring Boot Profile to load different configurations. Different underlying layers can be used from development, testing, and production environments. At the same time, Maven profiles can be loaded according to specifications . Different dependencies are packaged to solve the problem of using different distributed lock implementations in different environments. -P 

code usage

private final DistributedLock distributedLock;

public DemoServiceImpl(DistributedLock distributedLock) {
    this.distributedLock = distributedLock;
}

public void test() {
    LockInfo lock = null;
    try {
        lock = distributedLock.tryLock("demo", 1000, 1000);
        //do something
    } finally {
        if (lock != null) {
            distributedLock.release(lock);
        }
    }
}

Since the business code relies on the interface, combined with the Spring Boot Starter conditional annotation + Maven Profile, no matter which distributed lock implementation depends on it, there is no need to modify the code.

Four. Summary

This article introduces that before Spring Boot and Starter, developers need to refer to many dependencies when using traditional Spring XML to develop Web applications, and need to write a lot of XML code to describe Beans and their dependencies. Also learned how to use SPI to load custom tags to load beans and inject them. The Spring Boot Starter provides a more modern configuration method, which loads the auto-assembled  @Configuration configuration class through the SPI mechanism to replace the traditional Spring XML to complete the bean injection, thereby eliminating a large number of XML configurations. Finally, we developed a distributed lock Spring Boot Starter component by customizing, using a series of  @ConditionalXXX annotations and Maven Profile to complete the development. In this way, we can be compatible with multiple distributed lock implementations, and use different distributed lock implementations in different environments without modifying the business code.

Author: JD Retail Chen Yanqing

Source: JD Cloud Developer Community

Clarification about MyBatis-Flex plagiarizing MyBatis-Plus Arc browser officially released 1.0, claiming to be a substitute for Chrome OpenAI officially launched Android version ChatGPT VS Code optimized name obfuscation compression, reduced built-in JS by 20%! LK-99: The first room temperature and pressure superconductor? Musk "purchased for zero yuan" and robbed the @x Twitter account. The Python Steering Committee plans to accept the PEP 703 proposal, making the global interpreter lock optional . The number of visits to the system's open source and free packet capture software Stack Overflow has dropped significantly, and Musk said it has been replaced by LLM
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10092132