2011-01-04 5 views
5

동일한 세션에 대해 두 개의 SessionScoped CDI Bean 인스턴스가 있습니다. 나는 CDI가 나를 위해 생성 한 인스턴스가 하나 있다는 인상하에 있었지만, 두 개를 생성했습니다. CDI 작동 방식을 잘못 이해했거나 버그를 찾았습니까?CDI SessionScoped Bean은 동일한 세션에서 두 개의 인스턴스가됩니다.

여기
<?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> 

배치에서 출력되는 페이지를 탐색 :

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 

글래스 피시 사용

여기
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; 
    } 
} 

가 Facelet 코드 : 여기

는 빈 코드 3.0.1

+1

strub : 생성자 (또는 초기화 블록)의 비 마지막 메소드 호출은 CDI와 의도하지 않은 효과를 야기한다. 그 이후로 비 최종 메소드를 사용하는 것이 권장되지 않는다는 것을 읽었습니다 (http://download.oracle.com/javase/tutorial/java/javaOO/initial.html). 비 최종 메소드를 사용하여 CDI 빈에서리스트를 초기화하면 intitializer가 두 번 호출됩니다! 주 : CDI는 최종 메소드를 허용하지 않으며 bean이 프록시 가능하지 않다는 것을 나타내는 런타임 예외를 발생시킵니다. "수정"은 비 최종 메서드를 호출하지 않고 initilizer 블록에서 모두 작업하는 것입니다. – Ryan

+0

@PostConstruct로 주석 처리 된 init 메소드를 정의하는 경우 (생성되는 두 개의 빈 인스턴스에도 불구하고) 한 번만 호출된다는 것을 알았습니다. CDI가 빈의 인스턴스 풀을 생성하고 풀에서 풀을 생성 할 때 post 구조를 호출하는 것으로 추측합니다. 아직 풀에있는 빈 인스턴스를 현재 HTTP 세션과 연관시키는 것은 의미가 없다고 생각합니다. – Ryan

+1

아래 나의 대답을 참조하십시오. 첫 번째 인스턴스는 상황 별 인스턴스이고 두 번째 인스턴스는 프록시입니다. @PostConstruct는 당연히 컨텍스트 인스턴스에 대해서만 호출되고 프록시에 대해서는 _not_이됩니다. – struberg

답변

4

Ryan은 공동 작성자가 이미 작성 했으므로 생성자는 해당 빈의 각 프록시마다 호출됩니다. 이것은 java.lang.reflect.proxy와 같은 인터페이스 프록시뿐만 아니라 실제 클래스 프록시를 제공하는 모든 프록시 메커니즘의 표준 동작입니다.

또한 ct가 모든 직렬화마다 호출 될 것이라고 상상해보십시오. 따라서 부하가 많은로드 밸런싱 클러스터에서 작업하는 경우이 많은 시간이 표시됩니다. 그래서 @PostConstruct를 사용하여 일반적으로 콩을 사용하십시오.

LieGrue, I 실제로 관련된 하나에 의해 상기 문제점을 경고 하였다

+0

응답 해 주셔서 감사합니다. 나는 아직도 너를 이해하지 못하고있다. PostConstruct 라이프 사이클 콜백 및 preRenderView 시스템 이벤트와 같은 사용을 위해 bean을 준비하는 다른 방법을 알고 있습니다. 그리고 그것은 훌륭합니다. 내 질문은 단지 하나의 사용자가 서버를 치는 매우 간단한 테스트에서 세션 범위가 지정된 빈의 두 인스턴스가 만들어지는 이유입니다. 나는로드 밸런싱 및 클러스터링을 갖춘 무거운로드 된 서버에 대해 말하는 것이 아닙니다. 둘 이상의 프록시가 만들어지고 있습니까? 그렇다면 왜? 아니면 각 프록시가 두 개의 빈 인스턴스에 의해 뒷받침 될까요? 그렇다면 왜? – Ryan

+2

Ryan, 저는 모든 프록시 항목이 쉽지는 않지만 2 개의 생성자 호출을 얻는 것이 완벽하다는 것을 알고 있습니다. 첫 번째 인스턴스는 상황에 맞는 인스턴스 자체입니다.이것은 SessionContext에 저장 될 Bean입니다. 두 번째 인스턴스가 프록시입니다. 디버거를 자세히 살펴 보거나 생성자에서 클래스를 출력하면 이것이 실제로 빈의 하위 클래스라는 것을 알 수 있습니다! – struberg

+0

하나는 프록시이고 하나는 실제 빈입니다. 알았다. CDI를 사용할 때 생성자에 넣는 것에주의를 기울여야하는 것처럼 보입니다. – Ryan

3

주입 지점에 사용할 프록시를 새로 만들 때 CDI 구현이 기본 Bean 기본 생성자를 호출 할 가능성이 있습니다. 이것이 weld 및 openwebbeans에 사용되는 javassist의 기본 동작입니다.

가능한 경우 @PostConstruct로 이동하여 기본 생성자에서 과도한 작업을 피하십시오!

+0

좋아요,하지만 왜 두 개가 만들어 졌나요? CDI가 풀을 생성합니까? – Ryan

관련 문제