java.lang.reflect.Field.set修改ApplicationScoped Bean字段失败求助
问题
我遇到了一个奇怪的行为差异:通过反射修改字段值时,Singleton EJB的字段值能正常更新,但ApplicationScoped CDI Bean的字段值却没有变化。不过改用AnnotatedParameter配合Method.invoke调用setter方法的方式修改值是有效的。我查了Java文档、相关规范都没找到原因,相关代码如下:
InjectionPoint ip = ....; Class bc = ip.getMember().getDeclaringClass(); Object reference = CDI.current().select(bc).get(); Annotated a = ip.getAnnotated(); Object value = ....; if (annotated instanceof AnnotatedField af) { Field f = af.getJavaMember(); try { boolean ac = f.canAccess(reference); f.setAccessible(true); f.set(reference, value); f.setAccessible(ac); } catch (IllegalAccessException e) { log.error(String.format("error updating %s with %s", f, value)); } }
原因分析
- CDI代理机制的差异:ApplicationScoped CDI Bean默认会被CDI容器创建代理类(比如基于接口的动态代理或子类代理),你通过
CDI.current().select(bc).get()获取到的是代理对象而非真实Bean实例。直接反射修改代理对象的字段,实际改的是代理类自身的字段,而非真实Bean实例的字段,后续使用Bean时自然看不到值的变化。 - Singleton EJB的实例特性:Singleton EJB由EJB容器直接管理,不会生成CDI风格的代理(或代理逻辑完全不同),你获取到的引用就是真实的Singleton实例本身,所以反射修改字段值会直接作用在真实实例上,能看到预期的变化。
- setter方法有效的原因:不管是CDI代理还是EJB代理,调用方法时都会将请求转发到真实的Bean实例上。通过
Method.invoke调用setter方法,实际是在真实实例上执行赋值操作,自然能修改到正确的字段值。
内容的提问来源于stack exchange,提问作者Eduard Drenth
相关产品推荐
相关产品推荐

