Spring多模块项目JUnit多服务端到端测试依赖冲突问题求助
解决方案
1. 为每个服务创建独立类加载器
核心思路是模拟单独启动服务时的类路径隔离,给每个服务的Spring上下文分配专属类加载器,避免依赖冲突。
示例代码:
@BeforeAll static void startServices() throws Exception { // 启动service-1,使用独立类加载器 ClassLoader service1ClassLoader = new URLClassLoader( getServiceClasspathEntries("service-1"), ClassLoader.getSystemClassLoader().getParent() ); new SpringApplicationBuilder(Service1Application.class) .classLoader(service1ClassLoader) .run("--server.port=8081"); // 同理启动service-2 ClassLoader service2ClassLoader = new URLClassLoader( getServiceClasspathEntries("service-2"), ClassLoader.getSystemClassLoader().getParent() ); new SpringApplicationBuilder(Service2Application.class) .classLoader(service2ClassLoader) .run("--server.port=8082"); // service-3按相同逻辑处理 } // 辅助方法:获取服务模块的编译类路径和依赖路径 private static URL[] getServiceClasspathEntries(String serviceModule) throws MalformedURLException { // 基础编译类路径 String classesDir = "../" + serviceModule + "/target/classes"; String testClassesDir = "../" + serviceModule + "/target/test-classes"; // 可通过Maven依赖插件将服务依赖导出到指定目录后添加 String libDir = "../" + serviceModule + "/target/dependency"; List<URL> urls = new ArrayList<>(); urls.add(new File(classesDir).toURI().toURL()); urls.add(new File(testClassesDir).toURI().toURL()); // 加载第三方依赖Jar File[] jars = new File(libDir).listFiles((dir, name) -> name.endsWith(".jar")); if (jars != null) { for (File jar : jars) { urls.add(jar.toURI().toURL()); } } return urls.toArray(new URL[0]); }
注意:需要先执行mvn dependency:copy-dependencies导出每个服务的依赖到target/dependency目录,再运行集成测试。
2. 针对性排除冲突的自动配置类
针对service-1(Spring Cloud Gateway)的MvcFoundOnClasspathException,可以手动排除Spring MVC的自动配置类,强制上下文只加载Gateway的WebFlux配置:
@SpringBootApplication( exclude = { DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, WebMvcAutoConfiguration.EnableWebMvcConfiguration.class } ) public class Service1Application { // ... }
这种方式只适合单一冲突场景,若多个服务存在多种依赖冲突,维护成本会很高。
3. 使用Testcontainers JVM模式(无Docker依赖)
Testcontainers的JVM模块可以在不启动Docker容器的前提下,为每个服务创建隔离的JVM进程,完全模拟mvn spring-boot:run的隔离效果。
首先在integration-test的POM中添加依赖:
<dependency> <groupId>org.testcontainers</groupId> <artifactId>jvm</artifactId> <version>1.19.0</version> <scope>test</scope> </dependency>
测试代码示例:
@Container static final JvmServiceContainer service1 = new JvmServiceContainer() .withWorkingDirectory("../service-1") .withMainClass("com.example.Service1Application") .withCommandLineArguments("--server.port=8081"); @Container static final JvmServiceContainer service2 = new JvmServiceContainer() .withWorkingDirectory("../service-2") .withMainClass("com.example.Service2Application") .withCommandLineArguments("--server.port=8082"); @BeforeAll static void init() { service1.start(); service2.start(); }
这种方案无需手动处理类加载器,隔离效果彻底,是最接近手动启动服务的解决方案。
内容的提问来源于stack exchange,提问作者FWidder
相关产品推荐
相关产品推荐

