2012-05-16 3 views
7

Я использую Spring и JSF 2 для создания веб-приложения. Бизнес-объекты хранятся в контейнере Spring, и я впрыснуть их в Managed Beans с использованием @ManagedProperty, как это:Как повторно ввести переходный @ManagedProperty при десериализации?

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Getter @Setter 
    @ManagedProperty("#{someService}") 
    private SomeService someService; 
    // ... 

Проблема заключается в том, я продолжаю получать NotSerializableException для класса с весны (ServiceLocatorFactoryBean), что он используется SomeService bean.

Если я делаю это transient, как я могу сделать повторную инъекцию после десериализации?

Или, что было бы другими способами решить эту проблему?

Я читал несколько других вопросов, подобных здесь, но не смог найти ничего, что касалось именно этой проблемы.

+2

FYI: эту проблему не существует, когда вы просто используете собственный EJB Java EE вместо Spring. – BalusC

+0

@BalusC Да, я читал об этом в других вопросах, к сожалению, я недостаточно знаю об EJB, чтобы использовать его еще (и я не знаю, смогу ли я убедить коллег, чтобы я попробовал его в этом проекте) , Не могли бы вы указать мне хороший ресурс, чтобы узнать об этом, кстати? – elias

+0

Это не так сложно. Просто убедитесь, что ваш контейнер поддерживает EJB уже (Glassfish, JBoss, Weblogic и т. Д.). Аннотировать класс обслуживания с '@ Stateless' или' @ Stateful' и ввести его '@ EJB'. Вот и все. Не требуется геттер/сеттер. – BalusC

ответ

3

Вместо того, чтобы впрыскивать Spring бобы через EL в @ManagedProperty аннотации (выполненной на ManagedBean инициализации), получают бобы, оценивающие EL во время выполнения.

При таком подходе, это то, что бобы JSF должен выглядеть следующим образом:

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    private static SomeService someService() { 
     return SpringJSFUtil.getBean("someService"); 
    } 
    // ... 

и утилита класса SpringJSFUtil.java, который получает боб через EL:

import javax.faces.context.FacesContext; 

public class SpringJSFUtil { 

    public static <T> T getBean(String beanName) { 
     if (beanName == null) { 
      return null; 
     } 
     return getValue("#{" + beanName + "}"); 
    } 

    @SuppressWarnings("unchecked") 
    private static <T> T getValue(String expression) { 
     FacesContext context = FacesContext.getCurrentInstance(); 
     return (T) context.getApplication().evaluateExpressionGet(context, 
       expression, Object.class); 
    } 
} 

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

Тот же подход, использующий OmniFaces:

В моем реальном коде, я использую evaluateExpressionGet(String expression) метод в utility class доступном от OmniFaces. Итак, для тех из вас, кто использует его тоже, это то, что мой код действительно выглядит следующим образом:

import static org.omnifaces.util.Faces.evaluateExpressionGet; 

@ManagedBean 
@ViewScoped 
public class SomeMB implements Serializable { 
    private static final long serialVersionUID = 1L; 

    private static SomeService someService() { 
     return evaluateExpressionGet("#{someService}"); 
    } 
    // ... 

Обратите внимание, что здесь метод получает полный EL («# {выражение}»), а не только весной bean name (или вы получите ClassCastException).

-1

Ну имейте это в виду от Spring руководстве ( link to spring):

Конструктор на основе или сеттер на основе DI?

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

Команда Spring обычно защищает инъекцию сеттера, поскольку большое количество аргументов конструктора может стать громоздким, особенно если свойства являются необязательными. Методы Setter также делают объекты этого класса пригодными для реконфигурации или повторной инъекции позже. Управление через JMX MBeans является убедительным прецедентом.

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

Используйте DI, который наиболее подходит для определенного класса. Иногда, когда вы имеете дело со сторонними классами, к которым у вас нет источника, выбор сделан для вас. Унаследованный класс не может выставлять какие-либо методы setter, и поэтому инъекция конструктора является единственным доступным DI.

+0

Извините, я не следую ... Что делает основанный на конструкторе vs setter-based DI связанным с моей проблемой? – elias

2

Попробуйте @Scope (значение = BeanDefinition.SCOPE_SINGLETON, proxyMode = ScopedProxyMode.INTERFACES) в вашей Spring @Service. Это должно вводить сериализуемый прокси-объект в управляемый компонент, который будет перемещать службу после доступа после десериализации.

0

Для тех, кому нужно следовать - у меня была аналогичная проблема с вложенным ResourceBundle. Используя часть ответа BalusC, я сделал следующее:

@ManagedProperty(value="#{myBundle}") 
private transient ResourceBundle myBundle; 

private Object readResolve() { 
    myBundle = FacesContext.getCurrentInstance().getApplication() 
     .evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{myBundle}", 
     ResourceBundle.class); 
    return this; 
} 

Таким образом, EL вычисляется только тогда, когда управляемый компонент десериализации.

Смежные вопросы