Внедрение несериализуемого приложения с охватом bean в качестве управляемого свойства сериализуемого сеанса bean в кластере

У меня есть следующий управляемый beans:

@ApplicationScoped
public class ApplicationBean {
    // ...
}
@SessionScoped
public class SessionBean implements Serializable {

    @ManagedProperty("#{applicationBean}")
    private ApplicationBean applicationBean;

    // ...
}

Это развертывается в кластере серверов с несколькими узлами. Что произойдет, когда сеанс HTTP будет сериализован на другом node?

ApplicationBean не сериализуется, потому что он не реализует Serializable. Будет ли он повторно инъецирован @ManagedProperty? Или это будет фактически сериализовано каким-то образом?

Ответ 1

Что произойдет, когда сеанс HTTP будет сериализован на другом node?

Все атрибуты сеанса HTTP также будут сериализованы, в том числе управляемый JSF с помощью сеанса beans. Любые свойства bean, которые не являются сериализуемыми, будут пропущены. Во время десериализации на другом node вы столкнетесь с NotSerializableException по всем свойствам bean, которые не являются сериализуемыми. Маркировка свойства transient исправит это исключение, но после десериализации это свойство останется null.


Будет ли он повторно введен в @ManagedProperty? Или это будет фактически сериализовано каким-то образом?

Неа. Он не будет повторно инъецирован. Вы должны позаботиться об этом вручную в случае @ManagedProperty.

Один наивный и подверженный ошибкам способ избавиться от @ManagedProperty и выполнять ленивую загрузку в getter (таким образом, действовать как прокси-сервер самостоятельно):

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

Таким образом, либо сделайте 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 beans:

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!)
}