引言

关于 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,如以下示例:

Spring Boot Starter 剖析与实践 | 京东云技术团队

Spring Boot Starter 剖析与实践 | 京东云技术团队

从上面示例来看,咱们运用了相当少的代码创立了一个 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依托内容

    Spring Boot Starter 剖析与实践 | 京东云技术团队

    <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.xmlmvc.xml。在 Spring Boot 呈现之前,Spring 通常运用 XML 装备办法描绘 Bean,或者在 XML 中装备注解驱动和上下文扫描办法解析 Bean。因而,咱们能够看出这儿有两个 XML 文件。经过剖析源代码,咱们整理出了以下 XML 标签解析到 Bean 解析的流程。如下:

Spring Boot Starter 剖析与实践 | 京东云技术团队

  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办法,内容如下:
    Spring Boot Starter 剖析与实践 | 京东云技术团队

    Spring Boot Starter 剖析与实践 | 京东云技术团队

    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依托内容

    Spring Boot Starter 剖析与实践 | 京东云技术团队

    <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,经过源代码剖析得出以下流程:

Spring Boot Starter 剖析与实践 | 京东云技术团队

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

    • @SpringBootConfiguration,标注当时类为一个装备类,与@Configuration注解功用共同 ,被@Configuration注解的类对应 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

    Spring Boot Starter 剖析与实践 | 京东云技术团队

  3. configurations通曩昔重与声明式扫除后,会进行以下进行过滤主动安装:

    configurations = getConfigurationClassFilter().filter(configurations)
    

    分红两部分:获取过滤器和履行过滤。

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

    • 履行过滤,会依据装备类上含有@ConditionOnBean@ConditionalOnClass@ConditionalOnWebApplication等等条件注解来过滤掉部分装备类。比方WebMvcAutoConfiguration指定需求在@ConditionOnWebApplication下才收效。

      Spring Boot Starter 剖析与实践 | 京东云技术团队

  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 内置的一种服务供给发现机制,进步结构的扩展性。

Spring Boot Starter 剖析与实践 | 京东云技术团队

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 来完结开发。这样,咱们能够兼容多种分布式锁完结,并且在不同环境下运用不同的分布式锁完结,而无需修正事务代码。

作者:京东零售 陈炎清

来历:京东云开发者社区