3.0.4

1. 使用文档

Spring Cloud CircuitBreaker 项目包含 Resilience4J 和 Spring Retry 的实现。Spring Cloud CircuitBreaker 中实现的 API 位于 Spring Cloud Commons 中。这些 API 的使用文档位于 Spring Cloud Commons 文档中。

1.1. 配置 Resilience4J 断路器

1.1.1. 启动器

Resilience4J 实现有两个启动器,一个用于响应式应用,一个用于非响应式应用。

  • org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j - 非响应式应用

  • org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j - 响应式应用

1.1.2. 自动配置

您可以通过将 spring.cloud.circuitbreaker.resilience4j.enabled 设置为 false 来禁用 Resilience4J 自动配置。

1.1.3. 默认配置

要为所有断路器提供默认配置,请创建一个 Customizer bean,该 bean 会传入一个 Resilience4JCircuitBreakerFactoryReactiveResilience4JCircuitBreakerFactory。可以使用 configureDefault 方法来提供默认配置。

@Bean
public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer() {
    return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
            .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)).build())
            .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
            .build());
}
响应式示例
@Bean
public Customizer<ReactiveResilience4JCircuitBreakerFactory> defaultCustomizer() {
    return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
            .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
            .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)).build()).build());
}
自定义 ExecutorService

如果您想配置执行断路器的 ExecutorService,可以使用 Resilience4JCircuitBreakerFactory 来完成。

例如,如果您想使用一个感知上下文的 ExecutorService,可以执行以下操作。

@Bean
public Customizer<ReactiveResilience4JCircuitBreakerFactory> defaultCustomizer() {
    return factory -> {
        ContextAwareScheduledThreadPoolExecutor executor = ContextAwareScheduledThreadPoolExecutor.newScheduledThreadPool().corePoolSize(5)
            .build();
        factory.configureExecutorService(executor);
    };
}

1.1.4. 特定断路器配置

与提供默认配置类似,您可以创建一个 Customizer bean,该 bean 会传入一个 Resilience4JCircuitBreakerFactoryReactiveResilience4JCircuitBreakerFactory

@Bean
public Customizer<Resilience4JCircuitBreakerFactory> slowCustomizer() {
    return factory -> factory.configure(builder -> builder.circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
            .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(2)).build()), "slow");
}

除了配置创建的断路器之外,您还可以在断路器创建后、返回给调用者之前对其进行自定义。为此,您可以使用 addCircuitBreakerCustomizer 方法。这对于为 Resilience4J 断路器添加事件处理程序非常有用。

@Bean
public Customizer<Resilience4JCircuitBreakerFactory> slowCustomizer() {
    return factory -> factory.addCircuitBreakerCustomizer(circuitBreaker -> circuitBreaker.getEventPublisher()
    .onError(normalFluxErrorConsumer).onSuccess(normalFluxSuccessConsumer), "normalflux");
}
响应式示例
@Bean
public Customizer<ReactiveResilience4JCircuitBreakerFactory> slowCustomizer() {
    return factory -> {
        factory.configure(builder -> builder
        .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(2)).build())
        .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults()), "slow", "slowflux");
        factory.addCircuitBreakerCustomizer(circuitBreaker -> circuitBreaker.getEventPublisher()
            .onError(normalFluxErrorConsumer).onSuccess(normalFluxSuccessConsumer), "normalflux");
     };
}

1.1.5. 断路器属性配置

您可以在应用程序的配置文件中配置 CircuitBreakerTimeLimiter 配置或实例。属性配置的优先级高于 Java Customizer 配置。

优先级从高到低排列如下:

  • 方法(id) 配置 - 针对特定方法或操作

  • 服务(group) 配置 - 针对特定应用程序服务或操作

  • 全局默认配置

ReactiveResilience4JCircuitBreakerFactory.create(String id, String groupName)
Resilience4JCircuitBreakerFactory.create(String id, String groupName)
全局默认属性配置
resilience4j.circuitbreaker:
    configs:
        default:
            registerHealthIndicator: true
            slidingWindowSize: 50

resilience4j.timelimiter:
    configs:
        default:
            timeoutDuration: 5s
            cancelRunningFuture: true
Configs 属性配置
resilience4j.circuitbreaker:
    configs:
        groupA:
            registerHealthIndicator: true
            slidingWindowSize: 200

resilience4j.timelimiter:
    configs:
        groupC:
            timeoutDuration: 3s
            cancelRunningFuture: true
Instances 属性配置
resilience4j.circuitbreaker:
 instances:
     backendA:
         registerHealthIndicator: true
         slidingWindowSize: 100
     backendB:
         registerHealthIndicator: true
         slidingWindowSize: 10
         permittedNumberOfCallsInHalfOpenState: 3
         slidingWindowType: TIME_BASED
         recordFailurePredicate: io.github.robwin.exception.RecordFailurePredicate

resilience4j.timelimiter:
 instances:
     backendA:
         timeoutDuration: 2s
         cancelRunningFuture: true
     backendB:
         timeoutDuration: 1s
         cancelRunningFuture: false
  • ReactiveResilience4JCircuitBreakerFactory.create("backendA")Resilience4JCircuitBreakerFactory.create("backendA") 将应用 instances backendA properties

  • ReactiveResilience4JCircuitBreakerFactory.create("backendA", "groupA")Resilience4JCircuitBreakerFactory.create("backendA", "groupA") 将应用 instances backendA properties

  • ReactiveResilience4JCircuitBreakerFactory.create("backendC")Resilience4JCircuitBreakerFactory.create("backendC") 将应用 global default properties

  • ReactiveResilience4JCircuitBreakerFactory.create("backendC", "groupC")Resilience4JCircuitBreakerFactory.create("backendC", "groupC") 将应用 global default CircuitBreaker properties 和 config groupC TimeLimiter properties

有关 Resilience4j 属性配置的更多信息,请参阅 Resilience4J Spring Boot 2 配置

1.1.6. 支持 Bulkhead(舱壁)模式

如果 classpath 中存在 resilience4j-bulkhead,Spring Cloud CircuitBreaker 将使用 Resilience4j Bulkhead 包装所有方法。您可以通过将 spring.cloud.circuitbreaker.bulkhead.resilience4j.enabled 设置为 false 来禁用 Resilience4j Bulkhead。

Spring Cloud CircuitBreaker Resilience4j 提供了两种 Bulkhead 模式的实现

  • 一个使用信号量的 SemaphoreBulkhead

  • 一个使用有界队列和固定线程池的 FixedThreadPoolBulkhead

默认情况下,Spring Cloud CircuitBreaker Resilience4j 使用 FixedThreadPoolBulkhead。要修改默认行为以使用 SemaphoreBulkhead,请将属性 spring.cloud.circuitbreaker.resilience4j.enableSemaphoreDefaultBulkhead 设置为 true

有关 Bulkhead 模式实现的更多信息,请参阅 Resilience4j Bulkhead

可以使用 Customizer<Resilience4jBulkheadProvider> 来提供默认的 BulkheadThreadPoolBulkhead 配置。

@Bean
public Customizer<Resilience4jBulkheadProvider> defaultBulkheadCustomizer() {
    return provider -> provider.configureDefault(id -> new Resilience4jBulkheadConfigurationBuilder()
        .bulkheadConfig(BulkheadConfig.custom().maxConcurrentCalls(4).build())
        .threadPoolBulkheadConfig(ThreadPoolBulkheadConfig.custom().coreThreadPoolSize(1).maxThreadPoolSize(1).build())
        .build()
);
}

1.1.7. 特定 Bulkhead 配置

与提供默认的 'Bulkhead' 或 'ThreadPoolBulkhead' 配置类似,您可以创建一个 Customizer bean,该 bean 会传入一个 Resilience4jBulkheadProvider

@Bean
public Customizer<Resilience4jBulkheadProvider> slowBulkheadProviderCustomizer() {
    return provider -> provider.configure(builder -> builder
        .bulkheadConfig(BulkheadConfig.custom().maxConcurrentCalls(1).build())
        .threadPoolBulkheadConfig(ThreadPoolBulkheadConfig.ofDefaults()), "slowBulkhead");
}

除了配置创建的 Bulkhead 之外,您还可以在 Bulkhead 和线程池 Bulkhead 创建后、返回给调用者之前对其进行自定义。为此,您可以使用 addBulkheadCustomizeraddThreadPoolBulkheadCustomizer 方法。

Bulkhead 示例
@Bean
public Customizer<Resilience4jBulkheadProvider> customizer() {
    return provider -> provider.addBulkheadCustomizer(bulkhead -> bulkhead.getEventPublisher()
        .onCallRejected(slowRejectedConsumer)
        .onCallFinished(slowFinishedConsumer), "slowBulkhead");
}
线程池 Bulkhead 示例
@Bean
public Customizer<Resilience4jBulkheadProvider> slowThreadPoolBulkheadCustomizer() {
    return provider -> provider.addThreadPoolBulkheadCustomizer(threadPoolBulkhead -> threadPoolBulkhead.getEventPublisher()
        .onCallRejected(slowThreadPoolRejectedConsumer)
        .onCallFinished(slowThreadPoolFinishedConsumer), "slowThreadPoolBulkhead");
}

1.1.8. Bulkhead 属性配置

您可以在应用程序的配置文件中配置 ThreadPoolBulkhead 和 SemaphoreBulkhead 实例。属性配置的优先级高于 Java Customizer 配置。

resilience4j.thread-pool-bulkhead:
    instances:
        backendA:
            maxThreadPoolSize: 1
            coreThreadPoolSize: 1
resilience4j.bulkhead:
    instances:
        backendB:
            maxConcurrentCalls: 10

有关 Resilience4j 属性配置的更多信息,请参阅 Resilience4J Spring Boot 2 配置

1.1.9. 收集指标

只要 classpath 中包含正确的依赖项,Spring Cloud Circuit Breaker Resilience4j 就包含用于设置指标收集的自动配置。要启用指标收集,您必须包含 org.springframework.boot:spring-boot-starter-actuatorio.github.resilience4j:resilience4j-micrometer。有关存在这些依赖项时生成的指标的更多信息,请参阅 Resilience4j 文档

您无需直接包含 micrometer-core,因为它由 spring-boot-starter-actuator 引入

1.2. 配置 Spring Retry 断路器

Spring Retry 为 Spring 应用提供声明性重试支持。该项目的一部分包括实现断路器功能的能力。Spring Retry 通过结合其 CircuitBreakerRetryPolicy状态化重试 来提供断路器实现。使用 Spring Retry 创建的所有断路器都将使用 CircuitBreakerRetryPolicyDefaultRetryState 创建。这两个类都可以使用 SpringRetryConfigBuilder 进行配置。

1.2.1. 默认配置

要为所有断路器提供默认配置,请创建一个 Customizer bean,该 bean 会传入一个 SpringRetryCircuitBreakerFactory。可以使用 configureDefault 方法来提供默认配置。

@Bean
public Customizer<SpringRetryCircuitBreakerFactory> defaultCustomizer() {
    return factory -> factory.configureDefault(id -> new SpringRetryConfigBuilder(id)
        .retryPolicy(new TimeoutRetryPolicy()).build());
}

1.2.2. 特定断路器配置

与提供默认配置类似,您可以创建一个 Customizer bean,该 bean 会传入一个 SpringRetryCircuitBreakerFactory

@Bean
public Customizer<SpringRetryCircuitBreakerFactory> slowCustomizer() {
    return factory -> factory.configure(builder -> builder.retryPolicy(new SimpleRetryPolicy(1)).build(), "slow");
}

除了配置创建的断路器之外,您还可以在断路器创建后、返回给调用者之前对其进行自定义。为此,您可以使用 addRetryTemplateCustomizers 方法。这对于为 RetryTemplate 添加事件处理程序非常有用。

@Bean
public Customizer<SpringRetryCircuitBreakerFactory> slowCustomizer() {
    return factory -> factory.addRetryTemplateCustomizers(retryTemplate -> retryTemplate.registerListener(new RetryListener() {

        @Override
        public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
            return false;
        }

        @Override
        public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {

        }

        @Override
        public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {

        }
    }));
}

2. 构建

2.1. 基本编译和测试

要构建源代码,您需要安装 JDK 17。

Spring Cloud 使用 Maven 进行大多数构建相关活动,您应该可以通过克隆您感兴趣的项目并输入以下命令快速开始:

$ ./mvnw install
您也可以自行安装 Maven(>=3.3.3),并在以下示例中用 mvn 命令代替 ./mvnw。如果您这样做,如果您的本地 Maven 设置不包含 Spring 预发布工件的仓库声明,您可能还需要添加 -P spring
请注意,您可能需要通过设置 MAVEN_OPTS 环境变量来增加 Maven 可用的内存量,其值类似于 -Xmx512m -XX:MaxPermSize=128m。我们尝试在 .mvn 配置中涵盖这一点,因此如果您发现必须这样做才能使构建成功,请提交工单以将设置添加到源代码控制中。

需要中间件(即 Redis)进行测试的项目通常要求安装并运行本地 [Docker](www.docker.com/get-started) 实例。

2.2. 文档

spring-cloud-build 模块有一个“docs”profile,如果您打开它,它会尝试从 src/main/asciidoc 构建 asciidoc 源。作为该过程的一部分,它会查找 README.adoc 并通过加载所有 include 来处理它,但不解析或渲染它,只是将其复制到 ${main.basedir}(默认为 $/tmp/releaser-1706275114670-0/spring-cloud-circuitbreaker/docs,即项目的根目录)。如果 README 中有任何更改,它会在 Maven 构建后显示为正确位置的修改文件。只需提交并推送更改即可。

2.3. 使用代码

如果您没有 IDE 偏好,我们建议您在使用代码时使用 Spring Tools SuiteEclipse。我们使用 m2eclipse Eclipse 插件进行 Maven 支持。其他 IDE 和工具也应能正常工作,只要它们使用 Maven 3.3.3 或更高版本。

2.3.1. 激活 Spring Maven Profile

Spring Cloud 项目需要激活 'spring' Maven Profile 才能解析 Spring 里程碑和快照仓库。请使用您首选的 IDE 将此 profile 设置为活动状态,否则可能会遇到构建错误。

2.3.2. 使用 m2eclipse 导入到 Eclipse

我们建议在 Eclipse 中使用 m2eclipse 插件。如果您尚未安装 m2eclipse,可以从“eclipse marketplace”获取。

较旧版本的 m2e 不支持 Maven 3.3,因此一旦项目导入 Eclipse,您还需要告诉 m2eclipse 使用正确的 profile。如果您看到许多与项目 POM 相关的不同错误,请检查您是否安装了最新版本。如果您无法升级 m2e,请将“spring”profile 添加到您的 settings.xml 中。或者,您可以将父 pom 的“spring”profile 中的仓库设置复制到您的 settings.xml 中。

2.3.3. 不使用 m2eclipse 导入到 Eclipse

如果您 prefer 不使用 m2eclipse,可以使用以下命令生成 Eclipse 项目元数据:

$ ./mvnw eclipse:eclipse

生成的 Eclipse 项目可以通过从 file 菜单中选择 import existing projects 来导入。

3. 贡献

Spring Cloud 以非限制性的 Apache 2.0 许可发布,并遵循非常标准的 Github 开发流程,使用 Github tracker 处理问题并将 pull request 合并到 master 分支。如果您想贡献哪怕是微不足道的东西,请不要犹豫,但要遵循以下指南。

3.1. 签署贡献者许可协议

在我们接受非微不足道的补丁或 pull request 之前,我们需要您签署 贡献者许可协议。签署贡献者协议并不会赋予任何人对主仓库的提交权限,但这确实意味着我们可以接受您的贡献,并且如果我们接受了,您将获得作者署名。活跃的贡献者可能会被邀请加入核心团队,并获得合并 pull request 的权限。

3.2. 行为准则

本项目遵守贡献者公约 行为准则。参与本项目,即表示您同意遵守此准则。请向 [email protected] 报告不可接受的行为。

3.3. 代码规范和整理

这些对于 pull request 来说都不是必需的,但它们都会有所帮助。它们也可以在最初的 pull request 之后但在合并之前添加。

  • 使用 Spring Framework 代码格式规范。如果您使用 Eclipse,可以使用 Spring Cloud Build 项目中的 eclipse-code-formatter.xml 文件导入格式化设置。如果使用 IntelliJ,可以使用 Eclipse Code Formatter Plugin 导入相同的文件。

  • 确保所有新的 .java 文件都有一个简单的 Javadoc 类注释,至少包含一个标识您身份的 @author 标签,最好至少有一段关于该类用途的描述。

  • 将 ASF 许可头注释添加到所有新的 .java 文件中(从项目中现有文件复制)

  • 将您自己添加为对您进行了实质性修改(不仅仅是外观更改)的 .java 文件的 @author

  • 添加一些 Javadocs,如果您更改了命名空间,还要添加一些 XSD 文档元素。

  • 一些单元测试也会有很大帮助——总得有人做。

  • 如果没有其他人使用您的分支,请将其 rebase 到当前的 master(或主项目中的其他目标分支)。

  • 编写提交消息时请遵循 这些约定,如果您正在修复现有问题,请在提交消息末尾添加 Fixes gh-XXXX(其中 XXXX 是问题编号)。

3.4. Checkstyle

Spring Cloud Build 附带一套 Checkstyle 规则。您可以在 spring-cloud-build-tools 模块中找到它们。该模块下最值得注意的文件有:

spring-cloud-build-tools/
└── src
    ├── checkstyle
    │   └── checkstyle-suppressions.xml (3)
    └── main
        └── resources
            ├── checkstyle-header.txt (2)
            └── checkstyle.xml (1)
1 默认 Checkstyle 规则
2 文件头设置
3 默认抑制规则

3.4.1. Checkstyle 配置

Checkstyle 规则默认禁用。要将 Checkstyle 添加到您的项目中,只需定义以下属性和插件即可。

pom.xml
<properties>
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError> (1)
        <maven-checkstyle-plugin.failsOnViolation>true
        </maven-checkstyle-plugin.failsOnViolation> (2)
        <maven-checkstyle-plugin.includeTestSourceDirectory>true
        </maven-checkstyle-plugin.includeTestSourceDirectory> (3)
</properties>

<build>
        <plugins>
            <plugin> (4)
                <groupId>io.spring.javaformat</groupId>
                <artifactId>spring-javaformat-maven-plugin</artifactId>
            </plugin>
            <plugin> (5)
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-checkstyle-plugin</artifactId>
            </plugin>
        </plugins>

    <reporting>
        <plugins>
            <plugin> (5)
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-checkstyle-plugin</artifactId>
            </plugin>
        </plugins>
    </reporting>
</build>
1 在 Checkstyle 错误时构建失败
2 在 Checkstyle 违规时构建失败
3 Checkstyle 也分析测试源
4 添加 Spring Java Format 插件,该插件将重新格式化您的代码以通过大多数 Checkstyle 格式化规则
5 将 checkstyle 插件添加到您的构建和报告阶段

如果您需要抑制某些规则(例如,行长需要更长),那么只需在 ${project.root}/src/checkstyle/checkstyle-suppressions.xml 下定义一个包含您的抑制的文件即可。示例:

projectRoot/src/checkstyle/checkstyle-suppresions.xml
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
        "-//Puppy Crawl//DTD Suppressions 1.1//EN"
        "https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
    <suppress files=".*ConfigServerApplication\.java" checks="HideUtilityClassConstructor"/>
    <suppress files=".*ConfigClientWatch\.java" checks="LineLengthCheck"/>
</suppressions>

建议将 ${spring-cloud-build.rootFolder}/.editorconfig${spring-cloud-build.rootFolder}/.springformat 复制到您的项目中。这样,将应用一些默认的格式化规则。您可以通过运行以下脚本来实现:

$ curl https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/.editorconfig -o .editorconfig
$ touch .springformat

3.5. IDE 设置

3.5.1. Intellij IDEA

为了设置 Intellij,您应该导入我们的编码规范、检查 profile 并设置 checkstyle 插件。以下文件可在 Spring Cloud Build 项目中找到。

spring-cloud-build-tools/
└── src
    ├── checkstyle
    │   └── checkstyle-suppressions.xml (3)
    └── main
        └── resources
            ├── checkstyle-header.txt (2)
            ├── checkstyle.xml (1)
            └── intellij
                ├── Intellij_Project_Defaults.xml (4)
                └── Intellij_Spring_Boot_Java_Conventions.xml (5)
1 默认 Checkstyle 规则
2 文件头设置
3 默认抑制规则
4 适用于 Intellij 的项目默认设置,应用大多数 Checkstyle 规则
5 适用于 Intellij 的项目样式规范,应用大多数 Checkstyle 规则
Code style
图 1. 代码样式

转到 FileSettingsEditorCode style。点击 Scheme 部分旁边的图标。在那里,点击 Import Scheme 值并选择 Intellij IDEA code style XML 选项。导入 spring-cloud-build-tools/src/main/resources/intellij/Intellij_Spring_Boot_Java_Conventions.xml 文件。

Code style
图 2. 检查 profiles

转到 FileSettingsEditorInspections。点击 Profile 部分旁边的图标。在那里,点击 Import Profile 并导入 spring-cloud-build-tools/src/main/resources/intellij/Intellij_Project_Defaults.xml 文件。

Checkstyle

要使 Intellij 使用 Checkstyle,您必须安装 Checkstyle 插件。建议也安装 Assertions2Assertj 以自动转换 JUnit 断言。

Checkstyle

转到 FileSettingsOther settingsCheckstyle。点击 Configuration file 部分中的 + 图标。在那里,您需要定义 Checkstyle 规则应该从哪里获取。在上图中,我们从克隆的 Spring Cloud Build 仓库中选择了规则。但是,您可以指向 Spring Cloud Build 的 GitHub 仓库(例如,对于 checkstyle.xmlraw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle.xml)。我们需要提供以下变量:

请记住将 Scan Scope 设置为 All sources,因为我们将 Checkstyle 规则应用于生产和测试源。

3.6. Duplicate Finder

Spring Cloud Build 引入了 basepom:duplicate-finder-maven-plugin,该插件可以在 Java classpath 上标记重复和冲突的类及资源。

3.6.1. Duplicate Finder 配置

Duplicate finder 默认启用,并将在 Maven 构建的 verify 阶段运行,但只有将 duplicate-finder-maven-plugin 添加到项目 pom.xmlbuild 部分,它才会在您的项目中生效。

pom.xml
<build>
    <plugins>
        <plugin>
            <groupId>org.basepom.maven</groupId>
            <artifactId>duplicate-finder-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

对于其他属性,我们已设置了默认值,如 插件文档 中所列。

您可以通过设置所选属性的值并在其前面加上 duplicate-finder-maven-plugin 前缀来轻松覆盖它们。例如,将 duplicate-finder-maven-plugin.skip 设置为 true 以在您的构建中跳过重复检查。

如果您需要在设置中添加 ignoredClassPatternsignoredResourcePatterns,请确保将它们添加到项目插件配置部分:

<build>
    <plugins>
        <plugin>
            <groupId>org.basepom.maven</groupId>
            <artifactId>duplicate-finder-maven-plugin</artifactId>
            <configuration>
                <ignoredClassPatterns>
                    <ignoredClassPattern>org.joda.time.base.BaseDateTime</ignoredClassPattern>
                    <ignoredClassPattern>.*module-info</ignoredClassPattern>
                </ignoredClassPatterns>
                <ignoredResourcePatterns>
                    <ignoredResourcePattern>changelog.txt</ignoredResourcePattern>
                </ignoredResourcePatterns>
            </configuration>
        </plugin>
    </plugins>
</build>