Kafka TestContainer启动失败:类路径版本冲突致方法不存在
我在搭建Debezium集成测试环境(跟着示例文档步骤走)时,用默认镜像confluentinc/cp-kafka:5.2.1创建的Kafka TestContainer死活启动不起来,还抛出了方法不存在的异常。我创建KafkaContainer Bean的代码是这样的:
@Bean public KafkaContainer kafkaContainer() { if (kafkaContainer == null) { kafkaContainer = new KafkaContainer() .withNetwork(network()) .withExternalZookeeper("172.17.0.2:2181"); kafkaContainer.start(); } return kafkaContainer; }
抛出的异常信息如下:
*************************** APPLICATION FAILED TO START ***************************
描述:尝试调用一个不存在的方法。调用位置为:
org.testcontainers.containers.KafkaContainer.getBootstrapServers(KafkaContainer.java:91)
不存在的方法为:org/testcontainers/containers/KafkaContainer.getHost()Ljava/lang/String;
该类org.testcontainers.containers.KafkaContainer的来源为:
jar:file:/home/shubham/.m2/repository/org/testcontainers/kafka/1.14.3/kafka-1.14.3.jar!/org/testcontainers/containers/KafkaContainer.class
类层次加载来源:
org.testcontainers.containers.KafkaContainer: file:/home/shubham/.m2/repository/org/testcontainers/kafka/1.14.3/kafka-1.14.3.jar
org.testcontainers.containers.GenericContainer: file:/home/shubham/.m2/repository/org/testcontainers/testcontainers/1.12.5/testcontainers-1.12.5.jar
org.testcontainers.containers.FailureDetectingExternalResource: file:/home/shubham/.m2/repository/org/testcontainers/testcontainers/1.12.5/testcontainers-1.12.5.jar
操作建议:修正应用类路径,使其包含单一兼容版本的org.testcontainers.containers.KafkaContainer
2020-09-10 01:09:49.937 ERROR 72507 --- [ main] o.s.test.context.TestContextManager : 允许TestExecutionListener时捕获到异常的内容
问题根源
从异常日志里一眼就能看出来,核心问题是TestContainers各模块的版本不兼容:你用的kafka模块版本是1.14.3,但核心的testcontainers包版本却是1.12.5。KafkaContainer.getBootstrapServers()方法里调用了getHost(),但这个方法在1.12.5版本的GenericContainer里还没被添加,是后续版本才引入的,所以才会抛出“方法不存在”的错误。
解决方案
1. 统一TestContainers所有模块的版本
最稳妥的方式是用TestContainers的BOM(物料清单)来统一管理所有相关依赖的版本,避免版本不一致的问题。比如在Maven的pom.xml里添加:
<dependencyManagement> <dependencies> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers-bom</artifactId> <version>1.14.3</version> <!-- 和你用的kafka版本一致 --> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
然后引入依赖时就不需要指定版本了:
<dependency> <groupId>org.testcontainers</groupId> <artifactId>kafka</artifactId> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency>
2. 排查并排除冲突的旧版本依赖
如果你的项目里有其他第三方依赖间接引入了旧版本的testcontainers,可以用Maven命令查看依赖树:
mvn dependency:tree
找到引入旧版本的依赖后,在对应的依赖声明里排除掉旧版本的testcontainers:
<dependency> <groupId>第三方依赖组ID</groupId> <artifactId>第三方依赖ID</artifactId> <exclusions> <exclusion> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> </exclusion> </exclusions> </dependency>
3. 优化KafkaContainer的Bean配置
另外,你手动在Bean方法里调用start()的方式不太符合Spring的生命周期管理,建议改成让Spring自动管理容器的启动和停止:
@Bean(initMethod = "start", destroyMethod = "stop") public KafkaContainer kafkaContainer() { return new KafkaContainer() .withNetwork(network()) .withExternalZookeeper("172.17.0.2:2181"); }
这样Spring会在Bean初始化完成后自动调用start(),在容器销毁时调用stop(),避免手动管理可能带来的问题。
内容的提问来源于stack exchange,提问作者S. Arora

