Как повторно внедрить переходный процесс @ManagedProperty при десериализации?
Я использую Spring и JSF 2 для создания веб-приложения. Бизнес-объекты хранятся в контейнере Spring, и я внедряю их в управляемые компоненты с помощью @ManagedProperty, например:
@ManagedBean
@ViewScoped
public class SomeMB implements Serializable {
private static final long serialVersionUID = 1L;
@Getter @Setter
@ManagedProperty("#{someService}")
private SomeService someService;
// ...
Проблема в том, что я продолжаю получать NotSerializableException
для класса из Spring (ServiceLocatorFactoryBean), который используется компонентом SomeService.
Если я сделаю это transient
Как я могу сделать повторную инъекцию после десериализации?
Или каковы другие способы решения этой проблемы?
Я читал несколько других подобных вопросов, но не смог найти ни одного, который бы точно касался этой проблемы.
4 ответа
Вместо внедрения бобов 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);
}
}
Это устраняет свойство bean-компонента Spring (за счет выполнения нескольких дополнительных оценок EL), что позволяет избежать всех проблем сериализации, связанных с наличием свойства на первом месте.
Тот же подход, используя OmniFaces:
В моем фактическом коде я использую evaluateExpressionGet(String expression)
метод служебного класса, доступный от 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 ("#{expression}"), а не только имя bean-компонента Spring (или вы получите ClassCastException).
Попробуйте @Scope(value = BeanDefinition.SCOPE_SINGLETON, proxyMode = ScopedProxyMode.INTERFACES) на своем Spring @Service. Это должно внедрить сериализуемый прокси-объект в управляемый компонент, который будет перемещать службу при доступе после десериализации.
Для тех, чтобы следовать - у меня была похожая проблема с внедренным 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 оценивается только при десериализации управляемого компонента.
Помните об этом из руководства Spring ( ссылка на Spring):
DI на основе конструктора или сеттера?
Поскольку вы можете смешивать как DI на основе конструктора, так и на основе Setter, рекомендуется использовать аргументы конструктора для обязательных зависимостей и сеттеры для необязательных зависимостей. Обратите внимание, что использование аннотации @Required в установщике может быть использовано для установки зависимостей в зависимости от требований.
Команда Spring обычно выступает за внедрение метода установки, потому что большое количество аргументов конструктора может быть громоздким, особенно когда свойства являются необязательными. Методы установки также делают объекты этого класса пригодными для реконфигурации или повторного внедрения позже. Управление через JMX MBeans является убедительным примером использования.
Некоторые пуристы предпочитают инъекцию на основе конструктора. Предоставление всех зависимостей объекта означает, что объект всегда возвращается клиентскому (вызывающему) коду в полностью инициализированном состоянии. Недостатком является то, что объект становится менее поддающимся реконфигурации и повторному впрыску.
Используйте DI, который имеет смысл для конкретного класса. Иногда, когда имеешь дело со сторонними классами, для которых у тебя нет источника, выбор делается за тебя. Унаследованный класс может не предоставлять какие-либо методы установки, поэтому внедрение в конструктор является единственным доступным DI.