Field.get(obj) возвращает все пустые значения для введенных управляемых bean-компонентов CDI, в то время как вызывающий вручную метод get возвращает правильные значения

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

Получение beanObject:

ELContext elcontext = FacesContext.getCurrentInstance().getELContext();
Object beanObject = FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(elcontext, null, beanName);

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

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

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

Спасибо!

2 ответа

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

Вот начальный пример:

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-интерфейсом отражения и определять / угадывать правильные имена методов.


Вне зависимости от конкретной проблемы, в зависимости от конкретного функционального требования, для которого вы, возможно, ошибочно полагали, что это все будет правильным решением, о котором вы ничего не сказали в этом вопросе, возможно, есть даже более лучшие способы достижения этого, чем анализировать экземпляры бобов.

Я подозреваю, что бины проксируются реализацией CDI и / или JSF.

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

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

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

Другие вопросы по тегам