Внедрение несериализуемого bean-объекта области применения как управляемое свойство сериализуемого bean-объекта области действия в кластере
У меня есть следующие управляемые бины:
@ApplicationScoped
public class ApplicationBean {
// ...
}
@SessionScoped
public class SessionBean implements Serializable {
@ManagedProperty("#{applicationBean}")
private ApplicationBean applicationBean;
// ...
}
Это развертывается в кластер серверов с несколькими узлами. Что произойдет, когда сеанс HTTP будет сериализован на другом узле?
ApplicationBean
не сериализуется, потому что не реализует Serializable
, Будет ли он повторно введен @ManagedProperty
? Или это будет как-то сериализовано?
1 ответ
Что произойдет, когда сеанс HTTP будет сериализован на другом узле?
Все атрибуты сеанса HTTP также будут сериализованы, включая управляемые bean-компоненты JSF. Любые свойства компонента, которые не сериализуются, будут пропущены. Во время десериализации на другом узле вы столкнетесь с NotSerializableException
на все свойства бина, которые не сериализуются. Маркировка недвижимости transient
исправит это исключение, но свойство все равно останется null
после десериализации.
Будет ли он повторно введен @ManagedProperty? Или это будет как-то сериализовано?
Нету. Это не будет повторно введено. Вы должны позаботиться об этом вручную в случае @ManagedProperty
,
Один несколько наивный и подверженный ошибкам способ избавиться от @ManagedProperty
и выполняем ленивую загрузку в геттере (таким образом, ведите себя как прокси):
private transient ApplicationBean applicationBean;
public ApplicationBean getApplicationBean() {
if (applicationBean == null) {
FacesContext context = FacesContext.getCurrentInstance();
applicationBean = context.getApplication().evaluateExpressionGet(context, "#{applicationBean}", ApplicationBean.class);
}
return applicationBean;
}
и используйте метод получения по всему коду вместо ссылки на свойство напрямую.
Лучший способ - сделать его EJB-компонентом или управляемым компонентом CDI. Они полностью прозрачно созданы и внедрены как сериализуемые прокси, и вам не нужно беспокоиться о сериализации на них.
Таким образом, либо сделайте это EJB:
import javax.ejb.Singleton;
@Singleton
public class ApplicationBean {
// ...
}
import javax.ejb.EJB;
import.javax.faces.bean.ManagedBean;
import.javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class SessionBean implements Serializable {
@EJB
private ApplicationBean applicationBean;
// ... (no setter/getter necessary!)
}
Или сделайте так, чтобы они оба управляли компонентами CDI:
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
@Named
@ApplicationScoped
public class ApplicationBean {
// ...
}
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
@Named
@SessionScoped
public class SessionBean implements Serializable {
@Inject
private ApplicationBean applicationBean;
// ... (also here, no setter/getter necessary!)
}
У меня аналогичная проблема.
У меня есть планировщик Quartz, который запускается каждые x секунд.
Quartz's Job обновляет список элементов в моем bean-компоненте с областью действия приложения.
Я получаю компонент приложения с помощью статического метода, показанного ниже.
Когда я развертываю приложение, оно работает нормально, но когда пользователь входит в веб-приложение, FacesContext.getCurrentInstance() всегда возвращает значение null.
public static <T> T getBean(final String beanName, final Class<T> beanClass) {
FacesContext facesContext = FacesContext.getCurrentInstance();
T result = null;
if (facesContext != null) {
Application application = facesContext.getApplication();
if (application != null) {
String expresionBeanName = "#{" + beanName + "}";
result = application.evaluateExpressionGet(facesContext, expresionBeanName, beanClass);
}
}
return result;
}
@ManagedBean
@ApplicationScoped
public class ApplicationBean implements Serializable { ... }