Field.get(obj)在注入的CDI托管bean上返回所有null,而手动调用getter则返回正确的值



我正试图通过反射从JSF页面的backingbean访问一些字段的值。问题是,当我使用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,这将导致严重的问题。

谢谢!

这确实是预期的行为。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.IntrospectorAPI来内省bean,并在bean实例上调用getters/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规范,所以您不需要摆弄原始反射API和配置/猜测正确的方法名称。


与具体问题无关,取决于具体的功能需求,您可能错误地认为这一切都是正确的解决方案,而您在问题中没有告诉任何内容,可能有比内省bean实例更好的方法来实现它。

我怀疑bean正被CDI和/或JSF实现代理。

由于代理实现是特定于服务器的,因此没有可靠的方法来解决这个问题。代理是在运行时或应用程序部署时生成的,至少对于某些实现(例如weld),代理没有对bean本身的引用,但有对获取bean和调用相应方法所需的内部类的引用。

我能想到的唯一方法是放松你财产的安全性,并希望财产可靠地复制到代理中。

所有这些都违背了JavaEE的精神,也违反了面向对象的所有规则,所以我强烈建议不要这样做

最新更新