Spring Boot 3升级后JUnit测试出现javax.servlet.Filter类找不到异常
问题:Spring Boot 3.0 + Java 17升级后JUnit测试报错
javax.servlet.Filter找不到 我们原有基于Spring Boot 2.7.x、Java 11的应用,现计划升级至Spring Boot 3.0.0与Java 17。本地IntelliJ IDE完成修改后,运行如下JUnit测试类时出现异常:
import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import static org.junit.jupiter.api.Assertions.assertTrue; @SpringBootTest class MyCompanyAppRegistrationApplicationTests { @Test void contextLoads() { assertTrue(true); } }
错误信息:
java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@55a66bbf testClass = company.registration.MyCompanyAppRegistrationApplicationTests, locations = [], classes = [company.registration.MyCompanyAppRegistrationApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@1e6cc850, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@48b22fd4, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@178270b2, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@9da1, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3d36dff4, org.springframework.boot.test.context.SpringBootTestAnnotation@3954286c], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null] Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [company.registration.MyCompanyAppRegistrationApplication]: Failed to load class [javax.servlet.Filter] Caused by: java.io.IOException: Failed to load class [javax.servlet.Filter] Caused by: java.lang.ClassNotFoundException: javax.servlet.Filter
已知Spring 3.x支持JakartaEE 9和10,已将所有需要的javax包替换为Jakarta.,但仍出现需要javax.servlet.Filter的情况。当前pom.xml配置如下:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>17</java.version> <commons-lib.version>3.2.1</commons-lib.version> <datasource-lib.version>1.4.0</datasource-lib.version> <wiremock.version>2.27.2</wiremock.version> <archunit.version>1.0.1</archunit.version> <guava.version>31.1-jre</guava.version> <h2.version>1.4.200</h2.version> <jakarta.validaton.api.version>3.0.0</jakarta.validaton.api.version> </properties> <dependencies> <!-- internal dependencies--> <dependency> <groupId>my company</groupId> <artifactId>commons-lib</artifactId> <version>${commons-lib.version}</version> </dependency> <dependency> <groupId>my company</groupId> <artifactId>datasource-lib</artifactId> <version>${datasource-lib.version}</version> </dependency> <!--PROVIDED--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>${guava.version}</version> </dependency> <dependency> <groupId>jakarta.validation</groupId> <artifactId>jakarta.validation-api</artifactId> <version>${jakarta.validaton.api.version}</version> </dependency> <dependency> <groupId>jakarta.persistence</groupId> <artifactId>jakarta.persistence-api</artifactId> <version>3.1.0</version> </dependency> <!--TEST--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock</artifactId> <version>${wiremock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>${h2.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>com.tngtech.archunit</groupId> <artifactId>archunit</artifactId> <version>${archunit.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.sonarsource.scanner.maven</groupId> <artifactId>sonar-maven-plugin</artifactId> <version>3.8.0.2131</version> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.8</version> <configuration> <rules> </rules> </configuration> <executions> <execution> <id>default-prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>default-report</id> <phase>prepare-package</phase> <goals> <goal>report</goal> </goals> </execution> <execution> <id>default-check</id> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <distributionManagement> <repository> <id>central</id> <name>libs-staging-local</name> <url>https://some-url</url> </repository> <snapshotRepository> <id>snapshots</id> <name>libs-snapshot-local</name> <url>https://some-url</url> </snapshotRepository> </distributionManagement> <repositories> <repository> <id>azd-releases</id> <url>https://some-url</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>azd-snapshots</id> <url>https://some-url</url> <releases> <enabled>false</enabled> </releases> </repository> <repository> <id>apache-central</id> <url>https://repo.maven.apache.org/maven2/</url> </repository> </repositories>
请问应从哪些方向排查该问题?是否需要检查pom.xml中的依赖?
排查方向及解决方案
1. 检查传递依赖中的旧javax.servlet包
Spring Boot 3.x完全切换到Jakarta EE规范,API前缀从javax.*改为jakarta.*,但传递依赖可能偷偷引入旧的javax.servlet相关包:
- 执行
mvn dependency:tree命令,搜索输出里的javax.servlet关键词,定位是哪个依赖引入了旧包。 - 对引入旧包的依赖,要么升级到支持Jakarta EE的版本,要么在pom.xml中用
<exclusions>排除掉javax.servlet相关依赖项。
2. 验证内部依赖的兼容性
pom.xml里的commons-lib和datasource-lib两个内部依赖大概率还停留在Spring Boot 2.x版本,依赖旧的javax.servlet API:
- 确认这两个内部库是否已升级到支持Spring Boot 3.x和Jakarta EE的版本,没升级的话同步升级。
- 暂时无法升级的话,给这两个依赖添加排除规则:
<dependency> <groupId>my company</groupId> <artifactId>commons-lib</artifactId> <version>${commons-lib.version}</version> <exclusions> <exclusion> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </exclusion> </exclusions> </dependency>
3. 升级测试依赖版本
- 你用的WireMock 2.27.2依赖旧的javax.servlet API,Spring Boot 3.x环境下必须升级到WireMock 3.x及以上版本(3.0+支持Jakarta EE)。
- ArchUnit 1.0.1也存在兼容性问题,建议升级到支持Java 17和Jakarta EE的版本(比如1.2.0及以上)。
4. 确认Web Starter是否完整
如果你的应用是Web应用,检查是否引入了spring-boot-starter-web——Spring Boot 3.x的这个starter会自动引入jakarta.servlet-api,缺失的话手动添加:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
5. 清理IDE缓存
IntelliJ可能残留旧依赖缓存,导致加载错误的类:
- 执行
mvn clean install清理本地构建产物。 - 在IDE中通过
File -> Invalidate Caches...清理缓存并重启。
内容的提问来源于stack exchange,提问作者Ivajlo Iliev
相关产品推荐
相关产品推荐

