You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JDK17迁移后Spring+PowerMock测试套件加载ApplicationContext失败

JDK11迁移至JDK17后PowerMock+Spring测试套件执行失败问题

问题详情

应用从JDK11迁移到JDK17后,使用PowerMockRunner和PowerMockito.mockStaticMock静态final方法时,单测运行正常,但执行整个项目测试套件时失败。服务在Tomcat上运行无异常,仅JUnit测试抛出如下错误:

[[1;34INFO[m] 2024-01-17 16:45:27,194 ERROR TestContextManager Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener] to prepare test instance [com.project.FunctionalityTest@4cdb88d0]
java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@51fcf350 testClass = com.project.FunctionalityTest, locations = [], classes = [com.project.spring.AppTestConfig], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = [], contextCustomizers = [], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.test.context.web.WebDelegatingSmartContextLoader, parent = null]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:143)
    ... 47 common frames omitted
Caused by: org.springframework.cglib.core.CodeGenerationException: java.lang.NoSuchMethodException-->com.cisco.config.esearch.spring.AppTestConfig$$SpringCGLIB$$0.CGLIB$SET_THREAD_CALLBACKS([Lorg.springframework.cglib.proxy.Callback;)
    ... 70 common frames omitted
Caused by: java.lang.NoSuchMethodException: com.project.spring.AppTestConfig$$SpringCGLIB$$0.CGLIB$SET_THREAD_CALLBACKS([Lorg.springframework.cglib.proxy.Callback;)
    at java.base/java.lang.Class.getDeclaredMethod(Class.java:2675)
    ... 70 common frames omitted

相关配置

POM文件片段

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    ...(省略部分pom内容)
</project>

测试类注解

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { AppTestConfig.class })
@WebAppConfiguration
@PowerMockIgnore({"org.apache.http.conn.ssl.*", "javax.net.ssl.*" , "javax.crypto.*", "javax.net.*",  "org.bson.conversions.Bson","org.apache.http.*","com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "javax.management.*"})
@PrepareForTest({StopClock.class, SLIMCacheManager.class, DBConnFactory.class, ResourceDAO.class, Bson.class, PropertyUtil.class, JSONProvider.class})

注:所有内容在JDK11下运行正常,完整Maven依赖树已提供。


解决方案

1. 升级PowerMock及Mockito版本

JDK17对字节码操作和反射的限制更严格,旧版PowerMock不兼容。需升级到支持JDK17的版本:

  • 将powermock-module-junit4、powermock-api-mockito2(或对应Mockito3/4版本的依赖)升级至2.0.9+(推荐最新稳定版)
  • 同步升级Mockito到与PowerMock兼容的版本(如Mockito 4.x+,需匹配PowerMock版本要求)

2. 扩展PowerMockIgnore配置

添加Spring CGLIB相关包到忽略列表,避免PowerMock篡改Spring生成的代理类:

@PowerMockIgnore({
    // 原有配置
    "org.springframework.cglib.*",
    "net.sf.cglib.*"
})

3. 切换Spring代理方式为JDK动态代理

修改AppTestConfig,强制Spring使用JDK动态代理替代CGLIB,避免PowerMock干扰代理类生成:

@Configuration
public class AppTestConfig {
    @Bean
    public static BeanFactoryPostProcessor beanFactoryPostProcessor() {
        return beanFactory -> {
            ((DefaultListableBeanFactory) beanFactory).setProxyTargetClass(false);
        };
    }
}

或直接在测试类的@ContextConfiguration中添加属性:

@ContextConfiguration(classes = { AppTestConfig.class }, 
    properties = "spring.aop.proxy-target-class=false")

4. 清理@PrepareForTest范围

确保@PrepareForTest仅包含需要Mock的目标类,不要将Spring配置类(如AppTestConfig)加入其中,避免PowerMock不必要地代理Spring上下文类。

5. 添加JVM适配参数

在Maven Surefire插件中添加JVM参数,放宽JDK17的反射访问限制:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.1.2</version>
            <configuration>
                <argLine>
                    --add-opens java.base/java.lang=ALL-UNNAMED
                    --add-opens java.base/java.lang.reflect=ALL-UNNAMED
                    --add-opens java.base/java.util=ALL-UNNAMED
                    --add-opens org.springframework.cglib.core=ALL-UNNAMED
                    --add-opens org.springframework.cglib.proxy=ALL-UNNAMED
                </argLine>
            </configuration>
        </plugin>
    </plugins>
</build>

内容的提问来源于stack exchange,提问作者mohitkira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 06:14:53