Внедрение одного bean-компонента с областью видимости в другой bean-компонент с областью видимости приводит к его воссозданию.

Мне нужно использовать некоторые данные, сохраненные в bean-компоненте с областью видимости, в другом bean-компоненте с областью видимости.

@ManagedBean
@ViewScoped
public class Attivita implements Serializable {
    //
}

а также

@ManagedBean
@ViewScoped
public class Nota implements Serializable {

    @ManagedProperty("#{attivita}")
    private Attivita attivita;

    // Getter and setter.
}

Теперь, возможно, моя теория об этом все еще довольно плоха, я заметил, что когда вводится #{attivita}, вызывается конструктор Attivita и, таким образом, создается другой экземпляр. Это правильное поведение? А если я хочу сослаться на тот же экземпляр, а не создавать новый?


person supertarmax    schedule 09.01.2013    source источник
comment
Позаботьтесь об отметке правильных ответов. Переполнение стека касается не только вопросов, но и ответов.   -  person Xtreme Biker    schedule 09.01.2013


Ответы (2)


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

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

Для этого нет стандартного решения JSF. CDI решает эту проблему с помощью @ConversationScoped (бин живет до тех пор, пока вы явно указываете ему жить), а расширение CDI MyFaces CODI идет немного дальше с @ViewAccessScoped (бин живет до тех пор, пока на него ссылается навигационное представление).

Однако вы можете обойти это, сохранив bean-компонент как атрибут в области запроса.

@ManagedBean
@ViewScoped
public class Attivita implements Serializable {

    public String submit() {
        FacesContext.getCurrentInstance().getExternalContext()
            .getRequestMap().put("attivita", this);
        return "nota";
    }

}

а также

@ManagedBean
@ViewScoped
public class Nota implements Serializable {

    private Attivita attivita;

    @PostConstruct
    public void init() {
        attivita = (Attivita) FacesContext.getCurrentInstance().getExternalContext()
            .getRequestMap().get("attivita");
    }

}

Обратите внимание, что это довольно хакерский. В зависимости от конкретных функциональных требований могут быть лучшие решения. Также обратите внимание, что в представлении nota вы должны ссылаться на желаемый экземпляр компонента Attivita как на #{nota.attivita}, а не как на #{attivita}, потому что это даст вам новый и другой экземпляр по причинам, уже объясненным ранее.

person BalusC    schedule 09.01.2013
comment
Это работает наверняка - у меня была такая же проблема. Тем не менее, как вы сказали, он очень хакерский и является еще одним огромным недостатком JSF. Это как бы устраняет цель CDI или управляемых свойств, когда вам нужно добавить PostConstruct и вручную вытащить bean-компонент из контекста Faces. Планируется ли добавление @ViewAccessScoped в стандартный API JSF, а не только в Myfaces? - person GreenieMeanie; 18.03.2015
comment
@GreenieMeanie: JSF 2.2 добавил @FlowScoped, чтобы покрыть это, но, к сожалению, для этого все еще требуется небольшая конфигурация XML и автоматически сгенерированные параметры запроса, потому что он также должен был выдерживать запросы GET. Это, скажем, между @ViewAccessScoped и @SessionScoped. В качестве вероятного лучшего решения вы можете просто перенаправить его с идентификатором объекта в качестве параметра или условно отобразить часть того же представления с другим включением. См. также Заголовок stackoverflow.com/questions/15521451/ - person BalusC; 18.03.2015
comment
Вы предлагаете, чтобы представление a.xhtml имело фиктивное включение в b.xhtml и, возможно, обернуло все содержимое b.xhtml в один огромный ui: фрагмент, который фактически отображает что-либо только тогда, когда он перенаправляется (через b.xhtml) и НЕ, когда он включен как фиктивная ссылка из a.xhtml? - person GreenieMeanie; 18.03.2015
comment
@GreenieMeanie: см., например. stackoverflow.com/questions/7108668/ (примените идею на этой отдельной странице мастера, а не на всем сайте). - person BalusC; 19.03.2015

Ваш attivita bean-компонент - это @ViewScoped, и это не гарантирует, что ваш экземпляр будет удерживаться в сеансе. Вам нужен @SessionScoped боб. Однако, если вам нужно, чтобы attivita по какой-то причине было @ViewScoped, вы можете передавать через них параметры другими способами, например. используя viewParam или используя другой компонент @SessionScoped между ними.

Параметры страницы

http://mkblog.exadel.com/2010/07/learning-jsf2-page-params-and-page-actions/

Области управляемых компонентов JSF 2

http://balusc.blogspot.com.es/2011/09/communication-in-jsf-20.html#ManagedBeanScopes

person Xtreme Biker    schedule 09.01.2013