Field.get(obj) возвращает все значения NULL на введенном управляемом CDI beans, тогда как ручные вызовы getters возвращают правильные значения

Я пытаюсь получить доступ к значениям некоторых полей из базы bean страницы JSF через отражение. Проблема в том, что когда я использую 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;
}

Чтобы получить значения с помощью геттера, я делаю следующее:

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, но то, что я пытаюсь сделать здесь, оказывает глобальное влияние на приложение, которое я разрабатываю, а это значит, что возвращение и изменение всех затронутых классов для предоставления getters - это последнее решение. Также это приложение будет постоянно изменяться и расширяться, и я не хочу, чтобы другие разработчики не предоставляли геттеры, что приведет к серьезным проблемам.

Спасибо!

Ответы

Ответ 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 и вызвать getters/seters в экземплярах bean.

Вот пример запуска:

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.

Ответ 2

Я подозреваю, что beans получает прокси-сервер через реализацию CDI и/или JSF.

Нет надежного способа обойти это, поскольку реализация прокси-сервера зависит от сервера. Прокси генерируют время выполнения или время развертывания приложения и, по крайней мере, для некоторых реализаций (например, сварных) прокси не имеют ссылки на сам bean, но имеют ссылку на внутренние классы, необходимые для получения bean и вызова соответствующий метод.

О том, как я могу думать об этом, заключается в том, чтобы ослабить безопасность ваших свойств и надеяться, что prperty будет скопирован в прокси-сервер.

Все это противоречит духу JavaEE и нарушает все правила ориентации объектов, поэтому я настоятельно рекомендую против него.