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

Dan*_*ici 4 java reflection jsf cdi managed-bean

我正在尝试通过反射从JSF页面的后备bean访问某些字段的值。问题是,当我使用getter时,我会得到正确的值,但是当我使用必要字段的get(obj)方法时,我总是会返回空值。

获取beanObject:

ELContext elcontext = FacesContext.getCurrentInstance().getELContext();
Object beanObject = FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(elcontext, null, beanName);
Run Code Online (Sandbox Code Playgroud)

要在不使用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

}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

要使用getter获取值,请执行以下操作:

private ClassX getValue(Object beanObject, Class<?> beanClass) throws Exception {

    Method getter = beanClass.getDeclaredMethod("myMethod",(Class<?>[]) null);

    return (ClassX)getter.invoke(beanObject, (Object[])null);
}
Run Code Online (Sandbox Code Playgroud)

我可以进一步提到的是,我尝试访问的字段是通过@Inject注释注入的,但是我不认为这是问题所在,因为其他未注入的实例字段也具有相同的影响。

通常,我会使用吸气剂,但是我要在这里做的事情对正在开发的应用程序有全局影响,这意味着返回并修改所有受影响的类以提供吸气剂是最后的解决方案。另外,此应用程序将不断修改和扩展,我不想冒其他开发人员不提供getter的机会,这将导致严重的问题。

谢谢!

Bal*_*usC 5

这确实是预期的行为。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);
    }

}
Run Code Online (Sandbox Code Playgroud)

如您所见,没有具体领域。您还应该在检查类本身时注意到自动生成的类签名。

毕竟,您将以错误的方式进行操作。您应该使用java.beans.IntrospectorAPI内省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);
}
Run Code Online (Sandbox Code Playgroud)

这个API像JSF和CDI一样遵守JavaBeans规范,因此您不需要摆弄原始的反射API并确定/猜测正确的方法名称。


具体问题无关,根据具体的功能要求,您可能会错误地认为这将是正确的解决方案,而您在问题中没有提及任何内容,可能有比这更好的方法来实现内省bean实例。