Field.get(obj) 在注入的 CDI 托管 bean 上返回所有空值,同时手动调用 getter 返回正确的值
Posted
技术标签:
【中文标题】Field.get(obj) 在注入的 CDI 托管 bean 上返回所有空值,同时手动调用 getter 返回正确的值【英文标题】:Field.get(obj) returns all nulls on injected CDI managed beans, while manually invoking getters return correct values 【发布时间】:2015-06-21 11:02:29 【问题描述】:我正在尝试通过反射从 JSF 页面的支持 bean 访问某些字段的值。问题是,当我使用 getter 时,我得到了正确的值,但是当我使用必要字段的 get(obj) 方法时,我总是得到一个 null 值返回。
获取beanObject:
ELContext elcontext = FacesContext.getCurrentInstance().getELContext();
Object beanObject = FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(elcontext, null, beanName);
要在不使用 getter 的情况下获取字段值,我执行以下操作:
List<Field> fields = new ArrayList<Field>();
ParamsBuilder.getAllFields(fields, beanClass);
for(Field field: fields)
field.setAccessible(true);
System.out.println(field.getName() + ": " + field.get(beanObject)); //just to see if it works
getAllFields 方法有这个实现:
public static List<Field> getAllFields(List<Field> fields, Class<?> type)
for (Field field: type.getDeclaredFields())
fields.add(field);
if (type.getSuperclass() != null)
fields = getAllFields(fields, type.getSuperclass());
return fields;
要使用 getter 获取值,我执行以下操作:
private ClassX getValue(Object beanObject, Class<?> beanClass) throws Exception
Method getter = beanClass.getDeclaredMethod("myMethod",(Class<?>[]) null);
return (ClassX)getter.invoke(beanObject, (Object[])null);
我可以进一步提到的是,我尝试访问的字段是用 @Inject 注释注入的,但我不认为这是问题,因为其他实例字段(未注入)会受到同样的影响。
通常我会使用 getter,但我在这里尝试做的事情会对我正在开发的应用程序产生全局影响,这意味着返回并修改所有受影响的类以提供 getter 是最后的解决方案。此外,这个应用程序会不断地修改和扩展,我不想冒其他开发人员不提供 getter 的机会,这会导致严重的问题。
谢谢!
【问题讨论】:
Java 被滥用最多的 API 反射很多时候只是特定于框架的。如果不是真的需要,请避免使用它。 如果是通过注入的方式,您如何获取 beanObject,它可能已被代理,因此您可能无法获得任何字段值。 我以获取 beanObject 的方式进行了编辑,我认为您使用代理是正确的。有什么办法可以克服吗? 【参考方案1】:这确实是预期的行为。 CDI 托管 bean 实例本质上是自动生成类的可序列化代理实例,它扩展了原始支持 bean 类并通过公共方法将所有公共方法进一步委托给实际实例(就像 EJB 的工作方式一样)。自动生成的类大致如下:
public CDIManagedBeanProxy extends ActualManagedBean implements Serializable
public String getSomeProperty()
ActualManagedBean instance = CDI.resolveItSomehow();
return instance.getSomeProperty();
public void setSomeProperty(String someProperty)
ActualManagedBean instance = CDI.resolveItSomehow();
instance.setSomeProperty(someProperty);
如您所见,没有具体的字段。您还应该在检查类本身时注意到自动生成的类签名。
毕竟,你这样做是错误的。您应该使用 java.beans.Introspector
API 来内省 bean 并在 bean 实例上调用 getter/setter。
这是一个启动示例:
Object beanInstance = getItSomehow();
BeanInfo beanInfo = Introspector.getBeanInfo(beanInstance.getClass());
for (PropertyDescriptor property : beanInfo.getPropertyDescriptors())
String name = property.getName();
Method getter = property.getReadMethod();
Object value = getter.invoke(beanInstance);
System.out.println(name + "=" + value);
此 API 像 JSF 和 CDI 一样尊重 JavaBeans spec,因此您无需摆弄原始反射 API 并计算/猜测正确的方法名称。
与具体问题无关,取决于具体的功能要求,您可能错误地认为这一切都是正确的解决方案,您在问题中没有说明任何内容,那里实现它的方法可能比内省 bean 实例更好。
【讨论】:
【参考方案2】:我怀疑 bean 正在被 CDI 和/或 JSF 实现代理。
没有可靠的方法来解决这个问题,因为代理实现是特定于服务器的。代理是在运行时或应用程序部署时间生成的,并且至少对于某些实现(例如焊接)代理没有对 bean 本身的引用,但对获取 bean 并调用相应方法所需的内部类有引用。
关于我能想到的唯一方法是放宽您的属性的安全性,并希望将属性复制到可靠的代理中。
所有这些都违背了 JavaEE 的精神,并且违反了面向对象的所有规则,所以我强烈建议不要这样做。
【讨论】:
以上是关于Field.get(obj) 在注入的 CDI 托管 bean 上返回所有空值,同时手动调用 getter 返回正确的值的主要内容,如果未能解决你的问题,请参考以下文章
具有 CDI 注入属性的 ViewScoped ManagedBean 上的 NotSerializableException