Quarkus测试中使用FieldSetter设置私有字段抛出NoSuchFieldError问题求助
解决Quarkus测试中反射设置私有字段报NoSuchFieldError的问题
我之前也碰到过一模一样的问题,这其实是Quarkus的CDI代理机制在搞鬼——你@Inject拿到的TokenService实例并不是原始类对象,而是Quarkus自动生成的代理类实例。代理类虽然继承自TokenService,但不会包含原始类的私有字段,所以不管用FieldSetter还是直接反射,都会找不到someVariable这个字段。
下面给你几个靠谱的解决方案:
方案一:获取真实的Bean实例(推荐)
Quarkus的CDI代理实现了io.quarkus.arc.Unwrappable接口,你可以通过这个接口直接解包出原始的TokenService实例:
@Test public void someMethodTest(){ // 从代理对象中获取真实的业务类实例 TokenService realService = ((Unwrappable) service).unwrap(TokenService.class); // 现在用FieldSetter设置字段就不会报错了 FieldSetter.setField(realService, "someVariable", "someValue"); // 或者用原生反射方式也可行 // Field field = TokenService.class.getDeclaredField("someVariable"); // field.setAccessible(true); // field.set(realService, "someValue"); }
如果你的Quarkus版本不支持Unwrappable(1.5.2.Final是支持的),也可以通过Arc容器直接获取真实实例:
@Inject ArcContainer arcContainer; @Test public void someMethodTest(){ TokenService realService = arcContainer.instance(TokenService.class).get(); FieldSetter.setField(realService, "someVariable", "someValue"); }
方案二:添加测试专用的setter方法(更优雅)
如果允许修改TokenService的代码,我更建议给私有字段加一个包级别的setter方法,专门用于测试场景,完全不需要依赖反射:
@ApplicationScoped public class TokenService{ @Inject @RestClient RestClientInterface client; private String someVariable; public String someMethod(){} // 测试专用的setter,包级别访问权限,只供同包测试类调用 void setSomeVariable(String someVariable) { this.someVariable = someVariable; } }
然后在测试类里直接调用即可:
@Test public void someMethodTest(){ service.setSomeVariable("someValue"); }
这种方式更符合Java的封装原则,也避免了反射带来的各种潜在问题。
补充:为什么会出现NoSuchFieldError?
Quarkus对@ApplicationScoped这类作用域的Bean会生成代理类,用来处理上下文切换、方法拦截等逻辑。这个代理类继承自原始的TokenService,但原始类的私有字段不会被继承,所以当你通过代理对象service去反射查找someVariable时,自然会找不到这个字段。
内容的提问来源于stack exchange,提问作者Harsha D G
相关产品推荐
相关产品推荐

