Spring Boot Starter 剖析与实践

这篇具有很好参考价值的文章主要介绍了Spring Boot Starter 剖析与实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

对于 Java 开发人员来说,Spring 框架几乎是必不可少的。它是一个广泛用于开发企业应用程序的开源轻量级框架。近几年,Spring Boot 在传统 Spring 框架的基础上应运而生,不仅提供了 Spring 的全部功能,还使开发人员更加便捷地使用。在使用 Spring Boot 时,我们经常会接触到各种 Spring Boot Starter,例如 spring-boot-starter-web。只需将该依赖加入项目中,我们就可以开始开发应用;在引入 spring-boot-starter-data-jdbc 后,只需在配置文件中填写数据库连接信息,即可连接数据库。此外,您还可以随意切换数据源组件依赖,而无需修改业务代码。Spring Boot Starter 是如何适配的呢?我们能否自己实现一个 Spring Boot Starter 呢?本文将剖析 Spring Boot Starter 的原理,并自定义实现一个 Spring Boot Starter 组件。

一、Spring Boot Starter 是什么?

Spring Boot Starter 是 Spring Boot 中比较重要的概念, 是一种依赖描述符,它可以帮助您简化配置。当需要构建一个 Web 应用程序时,不必再遍历所有的依赖包,一个一个地添加到项目的依赖管理中,而是只需要一个配置spring-boot-starter-web,如以下示例:

从上面示例来看,我们使用了相当少的代码创建了一个 REST 应用程序。Spring 官方提供了许多 Starter,同时第三方也可以自定义 Starter,官方为了加以区分,Starter 从名称上进行了如下规范:spring-boot-starter-xxx;第三方提供的 starter 名称为:xxx-spring-boot-starter

二、Spring Boot Starter 剖析

前面介绍了 Starter 的概念以及如何快速创建 REST 应用程序。只需添加一个依赖和几行代码,就能完成 REST 接口开发。那么,在没有 Spring Boot 和 Starter 的情况下,我们该如何进行开发呢?Spring Boot Starter 的工作原理又是什么?接下来,我们将通过开发 Web 服务和 Dubbo 服务作为例子,分别剖析纯 Spring 和 Spring Boot Starter。

Spring

环境依赖

  • JDK 1.8

  • Maven 3

  • Tomcat 8(需要依靠 Web 容器服务器才能启动)

  • spring-webmvc 4.3.30.RELEASE

  • dubbo 2.7.23

开发流程

  1. 首先介绍一下,这是一个标准的 Maven 目录结构与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. 由于在 Spring XML 下还需要依靠 Java Web 和 Web 容器运行,还需要web/WEB-INF/web.xmlWeb 配置文件,内容配置了 SpringMVC 入口

    <?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 配置文件mvc.xml与 Dubbo 配置文件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. 编写 Controller 接口与 Dubbo RPC 接口

    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. 以上还无法单独运行,需要将以上打包成war包放入到 Tomcat 才可运行。

剖析

从上面的开发流程中,我们可以看到入口都在 web.xml 中。其中有一个监听器和一个 Servlet,以及初始化参数 dubbo.xml 和 mvc.xml。在 Spring Boot 出现之前,Spring 通常使用 XML 配置方式描述 Bean,或者在 XML 中配置注解驱动和上下文扫描方式解析 Bean。因此,我们可以看出这里有两个 XML 文件。经过分析源代码,我们整理出了以下 XML 标签解析到 Bean 解析的流程。如下:

  1. 由 Tomcat 启动加载web.xml并通过监听器和 Servlet 让 Spring 加载 XML 并解析。

  2. 直到BeanDefinitionParserDelegate#parseCustomElement开始解析自定义标签,找到mvc:xxxdubbo:xxx标签找到了 XML 命名空间。

  3. DefaultNamespaceHandlerResolver处理逻辑:以懒加载方式加载所有 jar 中META-INF/spring.handlers(路径必须得是这个)并缓存到handlerMappings,通过命名空间 URI 找到与之对应的处理类,SpringMVC 与 Dubbo 命名空间处理类分别为MvcNamespaceHandlerDubboNamespaceHandler

  4. MvcNamespaceHandlerDubboNamespaceHandler都分别实现了NamespaceHandler#init方法,内容如下:


    init方法将 SpringMVC 和 Dubbo 标签对应的 BeanDefinitionParser 注册到了 NamespaceHandlerSupport#parsers 中。在上一步中,DefaultNamespaceHandlerResolver 根据标签获取到了该标签的 BeanDefinitionParser,从而将对应的 Bean 注册到了 Spring IOC 容器中。注册逻辑不是本文的重点,这里就不再赘述。至此,SpringMVC 和 Dubbo 的加载流程已经完成。

从以上加载流程中,我们可以看出,在没有 Spring Boot 之前,Spring 主要依靠 XML 配置来启动。它会加载 XML 中的自定义标签,找到对应的命名空间,然后扫描 classpath 下的 META-INF/spring.handlers,找到命名空间处理类来解析当前标签。

Spring Boot

环境依赖

  • JDK 1.8

  • Maven 3

  • spring-boot 2.6.9

  • dubbo 2.7.23

开发流程

  1. 目录结构与 Mavendemo-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. 应用程序入口DemoSpringBootApplication

    @SpringBootApplication
    public class DemoSpringBootApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoSpringBootApplication.class, args);
        }
    
    }
    
    
  3. application.yml文件内容只有 Dubbo 的配置

    dubbo:
      application:
        name: demo-provider
      protocol:
        port: 20880
        name: dubbo
      registry:
        address: zookeeper://127.0.0.1:2181
    
    
  4. 编写 Controller 接口与 Dubbo RPC 接口

    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. 由于spring-boot-starter-web已经内嵌 tomcat ,只需要直接运行DemoSpringBootApplication#main方法即可运行应用

剖析

从开发流程上没办法第一时间找到解析入口,唯一入口就是在DemoSpringBootApplication,经过源代码分析得出以下流程:

  1. 应用DemoSpringBootApplication类上有@SpringBootApplication注解,而该注解由以下三个注解组成:

    • @SpringBootConfiguration,标注当前类为一个配置类,与[@Configuration](https://my.oschina.net/pointdance)注解功能一致 ,被[@Configuration](https://my.oschina.net/pointdance)注解的类对应 Spring 的 XML 版的容器。

    • @EnableAutoConfiguration,开启启动自动装配的关键,由@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)组成

    • @ComponentScan,按照当前类路径扫描含有@Service@Controller等等注解的类,等同于 Spring XML 中的context:component-scan

  2. Spring Boot 自动装配由@EnableAutoConfiguration导入的AutoConfigurationImportSelector类,会调用SpringFactoriesLoader#loadFactoryNames从 ClassPath 下扫描所有 jar 包的META-INF/spring.factories内容,由于传入的EnableAutoConfiguration.class,只会返回org.springframework.boot.autoconfigure.EnableAutoConfigurationkey 的值,得到一个全限定类名字符串数组configurations

  3. configurations经过去重与声明式排除后,会进行以下进行过滤自动装配:

    configurations = getConfigurationClassFilter().filter(configurations)
    
    

    分成两部分:获取过滤器和执行过滤。

    • getConfigurationClassFilter(),也是通过SpringFactoriesLoader#loadFactoryNamesMETA-INF/spring.factories找到 Key 为org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的值,目前只有:OnBeanConditionOnClassConditionOnClassCondition三个过滤器。

    • 执行过滤,会根据配置类上含有@ConditionOnBean@ConditionalOnClass@ConditionalOnWebApplication等等条件注解来过滤掉部分配置类。比如WebMvcAutoConfiguration指定需要在@ConditionOnWebApplication下才生效。

  4. 在引入各类 Configuration 的配置类后,配置类结合@Bean来完成 Spring Bean 解析和注入,同时 Spring Boot 还提供了许多@ConditionalXXX给开发者完成灵活注入。

以上就是 Spring Boot 的自动装配过程。Spring Boot 利用被 @Configuration 注解的配置类来代替 Spring XML 完成 Bean 的注入。然后,SpringFactoriesLoader 会最终加载 META-INF/spring.factories 中的自动配置类,实现自动装配过程。依靠“约定大于配置”的思想,如果开发的 Starter 想要生效,就需要按照 Spring Boot 的约定。

小结

通过对比 Spring 与 Spring Boot 的开发流程,我们可以发现 Spring Boot 在完成 Web 与 Dubbo 独立应用开发时,使用了相对较少的代码和配置。这得益于 Spring Boot Starter 的自动装配能力,它是 Spring Boot 的主要功能。通过消除定义一些属于自动配置类部分的需求,自动配置可以帮助简化开发流程并加速开发速度。

SPI

我们从上面剖析发现,两者都使用了一项机制去加载引入的 jar 包中的配置文件从而加载对应类,那就是SPI(Service Provider Interface)

SPI (Service Provider Interface), 是 Java 内置的一种服务提供发现机制,提高框架的扩展性。

Java SPI

Java 内置的 SPI 通过java.util.ServiceLoader类解析 Classpath 和 jar 包的META-INF/services目录下的以接口全限定名命名的文件,并加载该文件中指定的接口实现类,以此完成调用。

但是 Java SPI 会有一定不足:

  • 不能做到按需加载,需要遍历所有的实现并实例化,然后在循环中找到所需要的实现。

  • 多个并发多线程使用ServiceLoader类的实例不安全

  • 加载不到实现类时抛出并不是真正原因的异常,错误难定位。

Spring SPI

Spring SPI 沿用了 Java SPI ,但是在实现上和 Java SPI 存在差异,但是核心机制相同,在不修改 Spring 源码前提下,可以做到对 Spring 框架的扩展开发。

  • 在 Spring XML 中,由DefaultNamespaceHandlerResolver负责解析spring.handlers生成 namespaceUri 和 NamespaceHandler 名称的映射,等有需要时在进行实例化。

  • 在 Spring Boot 中,由SpringFactoriesLoader负责解析spring.factories文件,并将指定接口的所有实现类/全限定类名返回。

Spring Boot 2.7.0

在本文中 Spring Boot 自动装配使用了 SPI 来加载到EnableAutoConfiguration所指定的自动装配的类名,但在 Spring Boot2.7.0之后自动装配 SPI 机制有所改动,META-INF/spring.factories将废弃,同时在 Spring Boot 3 以上会将相关代码移除,改动如下:

  • 新的注解:@AutoConfiguration代替@Configuration

  • 读取自动装配的类文件位置改为:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,并且实现类全限定类名按照一行一个

  • org.springframework.boot.context.annotation.ImportCandidates#load负责解析META-INF/spring/%s.imports,其中%s是接口名的占位符

三、Spring Boot Stater 实践

在使用spring-boot-starter-jdbc或者spring-boot-starter-jpa等数据库操作时,通常会引入一个数据库数据源连接池,比如:HikariCPDBCP等,同时可随意切换依赖而不需要去更改任何业务代码,开发人员也无需关注底层实现,在此我们自定义一个 Starter 同时也实现这种兼容。因为我们以开发一个分布式锁的 Starter 并拥有多个实现:Zookeeper、Redis。 在此使用 Spring Boot 2.6.9 版本。

开发

项目结构与 Maven 依赖

└── 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>

在依赖里可以看到 Zookeeper 和 Redis 依赖关系被设置为provided,作用为编译与测试阶段使用,不会随着项目一起发布。即打包时不会带上该依赖。该设置在 Spring Boot Starter 作用较大。

分布式锁接口与实现

接口

public interface DistributedLock {

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

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

}

Redis 实现

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 实现

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 配置类

@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)开启配置类 Properties 信息,会将配置文件里的信息注入 Properties 类里。

  • @Configuration配置注解

  • @ConditionalOnClass(CuratorFramework.class)条件注解,要求存在CuratorFramework类当前配置类才生效,Redis 的子配置类同理。

  • @ConditionalOnMissingBean(DistributedLock.class)条件注解,Spring 不存在DistributedLockBean 当前配置类才生效,Redis 的子配置类同理。

  • @ConditionalOnProperty(name = "distributed.lock.type", havingValue = "zookeeper", matchIfMissing = true)条件注解,这里判断配置文件distributed.lock.type等于zookeeper才生效,当如果没配置则默认当做zookeeper,Redis 的子配置类同理。

  • @Bean将方法返回的 Bean 注入到 Spring IOC 容器里,方法入参中含依赖的 Bean

spring.factories

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

我们只需要将该文件放到resource/META-INF/spring.factories下,就会被 Spring Boot 加载,这也是 Spring Boot 的约定大于配置的思想。

使用

Maven 依赖关系

<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>

此处结合 Maven profile 功能按照不同环境依赖不同分布式锁底层实现,同时 Spring Boot 也提供了 Spring Boot Profile 加载不同配置,可以从开发、测试、生产环境使用不同底层了,同时 Maven profile 可以根据-P指定加载不同的依赖进行打包,解决了不同环境使用不同分布式锁实现。

代码使用

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);
        }
    }
}

业务代码中由于依赖的是接口,结合 Spring Boot Starter 条件注解 + Maven Profile 不管依赖哪个分布式锁实现,都无需去修改代码。

四、总结

本文介绍了在没有 Spring Boot 和 Starter 之前,开发人员在使用传统的 Spring XML 开发 Web 应用时需要引用许多依赖,并且需要大量编写 XML 代码来描述 Bean 以及它们之间的依赖关系。也了解了如何利用 SPI 加载自定义标签来加载 Bean 并进行注入。而 Spring Boot Starter 则提供了一种更加现代化的配置方式,它通过 SPI 机制加载自动装配的 @Configuration 配置类来代替传统的 Spring XML 完成 Bean 的注入,从而消除了大量的 XML 配置。最后,我们通过自定义开发了一个分布式锁 Spring Boot Starter 组件,利用一系列的 @ConditionalXXX 注解和 Maven Profile 来完成开发。这样,我们可以兼容多种分布式锁实现,并且在不同环境下使用不同的分布式锁实现,而无需修改业务代码。

作者:京东零售 陈炎清

来源:京东云开发者社区文章来源地址https://www.toymoban.com/news/detail-620237.html

到了这里,关于Spring Boot Starter 剖析与实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Spring Boot Starter Parent

    Spring Boot Starter Parent

    在这,您将学习了解 Spring Boot Starter Parent, 它是 Spring Boot 提供的父级 Pom 文件,旨在提供自动版本依赖管理,帮助我们轻松快速地进行 Spring Boot 开发。 通过 Spring Boot Starter Parent, 我们可以进行简单便捷地包依赖管理。在 Spring Boot 每一个发行版中, 均提供了该版本所兼容的依

    2024年02月08日
    浏览(9)
  • 自定义Spring Boot Starter

    自定义Spring Boot Starter

    Spring Boot starter 我们知道Spring Boot大大简化了项目初始搭建以及开发过程,而这些都是通过Spring Boot提供的starter来完成的。在实际项目中一些基础模块其本质就是starter,所以我们需要对Spring Boot的starter有一个全面深入的了解,这是我们的必备知识。 starter介绍 spring boot 在配置

    2024年02月10日
    浏览(12)
  • Spring Boot Starter设计实现

    Starter 是 Spring Boot 非常重要的一个硬核功能。 通过 Starter 我们可以快速的引入一个功能或模块,而无须关心模块依赖的其它组件。关于配置,Spring Boot 采用“约定大于配置”的设计理念,Starter 一般都会提供默认配置,只有当我们有特殊需求的时候,才需要在 application.yaml 里

    2024年01月18日
    浏览(12)
  • shiro-spring-boot-starter针对不同Spring Boot版本

    对于Spring Boot 2.4.10,无法找到shiro-spring-boot-starter的2.7.2版本,这是一个错误的版本号。 shiro-spring-boot-starter针对不同Spring Boot版本,推荐使用的版本如下: Spring Boot 1.x - 使用版本1.4.1 Spring Boot 2.0.x - 使用版本1.5.3 Spring Boot 2.1.x - 使用版本1.6.0 Spring Boot 2.2.x - 使用版本1.7.0 Spring Boot 2.3

    2024年02月13日
    浏览(10)
  • 自定义 Spring Boot Starter 组件

    自定义 Spring Boot Starter 组件是为了封装和简化特定功能的配置和集成,让用户能够更容易地集成你提供的库或功能。Spring Boot Starter 组件通常包括自动配置、依赖管理和必要的配置。 下面是创建一个简单的 Spring Boot Starter 的基本步骤: 步骤: 创建一个新的 Maven 或 Gradle 项目

    2024年02月05日
    浏览(15)
  • Spring Boot Starter介绍和实战

    Spring Boot Starter 是 Spring Boot 提供的一种机制,用于简化和集成应用程序的依赖管理。通过创建自定义的 Starter,可以将一组相关的依赖打包成一个简单的、可重用的模块,使应用程序的配置和依赖管理更加方便。在本文中,我们将深入探讨 Spring Boot Starter 的原理、创建过程,

    2024年01月23日
    浏览(9)
  • 【Spring Boot 初识丨三】starter

    【Spring Boot 初识丨三】starter

    上一篇讲了如何构建 MAVEN 项目 本篇来讲一讲 starter 依赖项 Spring Boot 初识: 【Spring Boot 初识丨一】入门实战 【Spring Boot 初识丨二】maven 【Spring Boot 初识丨三】starter 【Spring Boot 初识丨四】主应用类   启动器是一组方便的依赖关系描述符,它包含了一系列可以集成到应用里

    2024年02月09日
    浏览(9)
  • 46、如何理解Spring Boot中的Starter

    使用spring + springmvc,如果需要引入mybatis等框架,需要到xml中定义mybatis需要的bean starter就是定义一个starter的iar包,写一个@Configuration配置类、将这些bean定义在里面,然后在starter包的META-INF/spring.factories中写入该配置类,springboot会按照约定来加载该配置类 开发人员只需要将相应

    2024年02月16日
    浏览(9)
  • 如何自己实现一个Spring Boot Starter

    如何自己实现一个Spring Boot Starter

    现在很多开源的组件都会提供对应的 springboot-starter 包给我们去用,要做一个 starter 包并不难。参照Spring内置的实现就好了: 1、在工程里引入 starter 打包相关的依赖。 2、在我们工程内建 spring.factories 文件,编写我们配置类的全限类名。 使用AOP实现拦截方法执行和打印日志的

    2024年01月22日
    浏览(8)
  • Spring Boot中的Actuator是什么?Spring Boot中的Starter依赖是什么?

    在Spring Boot中,Actuator是一种用于监控和管理应用程序的工具。它提供了一些额外的端点和功能,使开发人员能够更好地了解和控制他们的应用程序。 Actuator提供了以下功能: 指标收集:Actuator可以收集并显示有关应用程序的指标,例如内存使用情况、线程数、请求处理时间等

    2024年02月09日
    浏览(10)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包