2011-01-04 10 views
5

Mam dwie instancje bean komponentu CDI SessionScoped dla tej samej sesji. Miałem wrażenie, że CDI wygeneruje jedną instancję, ale wygeneruje dwa. Czy nie rozumiem, jak działa CDI, czy też znalazłem błąd?SessionScoped Bean CDI powoduje dwie instancje w tej samej sesji

Oto kod fasola:

package org.mycompany.myproject.session; 

import java.io.Serializable; 
import javax.enterprise.context.SessionScoped; 
import javax.faces.context.FacesContext; 
import javax.inject.Named; 
import javax.servlet.http.HttpSession; 

@Named @SessionScoped public class MyBean implements Serializable { 
    private String myField = null; 

    public MyBean() { 
     System.out.println("MyBean constructor called"); 

     FacesContext fc = FacesContext.getCurrentInstance(); 
     HttpSession session = (HttpSession)fc.getExternalContext().getSession(false); 
     String sessionId = session.getId(); 
     System.out.println("Session ID: " + sessionId); 
    } 

    public String getMyField() { 
     return myField; 
    } 

    public void setMyField(String myField) { 
     this.myField = myField; 
    } 
} 

Oto kod Facelet:

<?xml version='1.0' encoding='UTF-8' ?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html" 
    xmlns:f="http://java.sun.com/jsf/core"> 
<f:view contentType="text/html" encoding="UTF-8"> 
    <h:head> 
     <title>Test</title> 
    </h:head> 
    <h:body> 
     <h:form id="form"> 
      <h:inputText value="#{myBean.myField}"/> 
      <h:commandButton value="Submit"/> 
     </h:form> 
    </h:body> 
</f:view> 
</html> 

Tutaj jest wyjście z wdrożeniem i przechodząc do strony:

INFO: Loading application org.mycompany_myproject_war_1.0-SNAPSHOT at /myproject 
INFO: org.mycompany_myproject_war_1.0-SNAPSHOT was successfully deployed in 8,237 milliseconds. 
INFO: MyBean constructor called 
INFO: Session ID: 175355b0e10fe1d0778238bf4634 
INFO: MyBean constructor called 
INFO: Session ID: 175355b0e10fe1d0778238bf4634 

Korzystanie GlassFish 3.0.1

+1

I faktycznie zaalarmowane powyższego problemu przez związane z nich: wywołanie nie Ostatnia metoda konstruktor (lub blok inicjatora) powoduje niezamierzone działania CDI. Odtąd czytałem, że nie zaleca się korzystania z metody nie ostatecznej (http://download.oracle.com/javase/tutorial/java/javaOO/initial.html). Jeśli użyję nieostatecznej metody do zainicjowania listy w komponencie CDI, to inicjator zostanie wywołany dwa razy! Uwaga: CDI nie zezwala na ostateczne metody i rzuci wyjątek Runtime stwierdzający, że komponent bean nie jest proxy. "Poprawka" nie wywołuje metody nieostatecznej i wykonuje wszystkie prace w bloku initilizer. – Ryan

+0

Zauważyłem, że jeśli zdefiniuję metodę init opisaną przez @PostConstruct, to jest ona wywoływana tylko raz (pomimo dwóch instancji tworzonego komponentu bean). Zgaduję, że CDI tworzy pulę instancji mojego komponentu bean i wywołuje konstrukcję postu, ponieważ wyciąga je z puli. Przypuszczam, że powiązanie instancji komponentu bean, który nadal znajduje się w puli z bieżącą sesją HTTP, jest bez znaczenia. – Ryan

+1

Zobacz moją odpowiedź poniżej. 2 instancje 1 to instancja kontekstualna, 2 to proxy. @PostConstruct będzie oczywiście wywoływany tylko dla kontekstowej instancji i _nie_ dla proxy. – struberg

Odpowiedz

4

Ryan, jak już napisał pisarz, również zostanie wywołany dla każdego proxy tego komponentu. Jest to standardowe zachowanie wszystkich mechanizmów proxy, które zapewniają nie tylko interfejs-proxy (jak rzeczy java.lang.reflect.proxy), ale prawdziwe proxy klasy.

Wyobraź sobie również, że ct będzie również wywoływany dla każdej serii. Więc jeśli pracujesz na silnie zbalansowanym klastrze, zobaczysz to wiele razy. Więc używaj @PostConstruct w ogóle dla fasoli.

LieGrue, Strub

+0

Dzięki za odpowiedź. Wciąż jednak nie rozumiem żadnego z was. Jestem świadomy alternatywnych sposobów przygotowania komponentu bean do użycia, takiego jak wywołanie zwrotne cyklu życia PostConstruct i zdarzenie systemowe preRenderView; i to jest świetne. Moje pytanie brzmi: dlaczego dwa wystąpienia komponentu o ustalonym zakresie są tworzone w bardzo prostym teście, gdy tylko jeden użytkownik trafia na serwer. Nie mówię o obciążonym serwerze z równoważeniem obciążenia i klastrami. Czy tworzonych jest więcej niż jedno proxy? Jeśli tak, dlaczego? A może każdy serwer proxy jest wspierany przez dwa wystąpienia komponentu bean? Jeśli tak, dlaczego? – Ryan

+2

Ryan, wiem, że wszystkie funkcje proxy nie są takie proste, ale uzyskanie 2 wywołań konstruktora jest całkowicie w porządku. Pierwsza instancja jest samą instancją kontekstową.To jest komponent bean, który zostanie zapisany w SessionContext. Druga instancja to serwer proxy. Jeśli przyjrzysz się uważnie debuggerowi lub wydrukujesz klasę w konstruktorze, zobaczysz, że jest to w rzeczywistości podklasa twojej fasoli! – struberg

+0

Jeden to proxy, a drugi to rzeczywisty komponent bean. Rozumiem. Wygląda na to, że musisz uważać na to, co wstawisz do konstruktora podczas korzystania z CDI. – Ryan

3

Jest prawdopodobne, że twoja implementacja CDI wywołuje domyślny konstruktor fasoli podczas tworzenia proxy dla punktów wtrysku - jest to domyślne zachowanie javassist, które jest używane w spawach i openwebbeans.

Unikaj podnoszenia ciężarów w domyślnym konstruktorze, przenosząc go na @PostConstruct, jeśli możesz!

+0

OK, ale dlaczego są tworzone dwie? Czy CDI tworzy pulę? – Ryan