문제

하나의 공개 방법, 몇 가지 개인 방법 및 일부 인스턴스 레벨 변수가 포함 된 무국적 세션 Bean이 있습니다. 아래는 의사 코드 예제입니다.

private int instanceLevelVar

public void methodA(int x) { 
  this.instanceLevelVar = x;
  methodB();
}

private void methodB() {
  System.out.println(instanceLevelVar);
}

내가보고있는 것은 Methodb가 Methoda로 전달되지 않은 값을 인쇄한다는 것입니다. 최선을 다하면 같은 콩의 다른 인스턴스에서 인쇄 값을 인쇄한다고 말할 수 있습니다. 이게 원인은 무엇입니까?

코드가 예상 99.9%의 시간으로 작동한다고 지적해야합니다. 그러나 .01%는 몇 가지 심각한 문제 / 우려를 유발하고 있습니다.

나는 다른 공개 방법이 있다면 전화 사이에 같은 콩을 얻지 못할 수도 있다는 것을 이해합니다. 그러나이 경우 유일한 호출은 단일 공개 방법입니다. 컨테이너 (이 경우 Glassfish)가 여전히 개인 방법 호출 사이에서 콩을 교체합니까?

(편집) 나는 "클래스 레벨"을 "인스턴스 레벨"으로 바꾸어 혼란을 일으켰습니다.

도움이 되었습니까?

해결책

나는 무국적 세션 bean에서 인스턴스 변수를 사용하는 것을 귀찮게하지 않을 것입니다. 문제의 원인에 관계없이 어쨌든 당신이하고 싶은 일이 아닐 것입니다. Stateless 세션 Bean 비즈니스 방법에서 호출하는 도우미 클래스에서 인스턴스 변수를 정의하거나 인스턴스 변수를 정의하십시오.

다른 팁

내가 읽을 때 세션 콩이란 무엇입니까? J2EE 1.4 튜토리얼 섹션 :

무국적 세션 콩

무국적 세션 Bean은 특정 클라이언트의 대화 상태를 유지하지 않습니다. 클라이언트가 무국적 콩의 방법을 호출하면 콩의 인스턴스 변수에는 상태가 포함될 수 있지만 호출 기간 동안 만 포함 할 수 있습니다. 방법이 완료되면 상태는 더 이상 유지되지 않습니다. 메소드 호출 중에 제외하고, 무 상태의 모든 인스턴스는 동일하므로 EJB 컨테이너가 인스턴스를 클라이언트에 할당 할 수 있습니다.

귀하의 경우, 전화 methodB() ~에서 methodA() 동일한 인스턴스에 있으며 this.methodB(). 따라서 나는 그렇게 말하는 경향이 있습니다 methodB() 전달 된 가치가 다른 것을 출력 할 수 없습니다. methodA().

이것은 7.11.8 절에서 첫 번째 문장으로 확인됩니다. EJB 2.0 사양: "컨테이너는 언제든지 하나의 스레드 만 인스턴스를 실행할 수 있도록해야합니다." 즉, 다른 클라이언트 (스레드)의 데이터 (인스턴스 변수)가 혼합되는 상황에 도달 할 수 없습니다. 인스턴스 변수에 대한 고유 한 액세스가 발생할 때까지 methodA() 돌아왔다!

즉, 나는 당신이 어딘가에 문제가 없다고 말하는 것이 아닙니다. 그러나 나는 당신의 의사 코드가 동등하다고 생각하지 않습니다.

(편집 : OP의 질문에 대한 의견을 읽은 후, 이제 의사 코드와 시맨틱에 대한 의심의 여지가 있습니다. 아래에서 가능한 결과를 명확히하고 있습니다.)

로켓 외과 의사가 밑줄을 긋는 것처럼, 정확히 무엇을 의미합니까? 클래스 변수? 당신은 정말로 의미합니까? 클래스 변수 반대로 인스턴스 변수? 그렇다면 의사 코드는이를 반영하지 않지만 이것은 예측할 수없는 행동으로 이어질 것입니다. 실제로, EJB 2.0 사양의 24.1.2 (및 첫 번째 지점)에서 클래스 변수에 데이터를 작성할 수는 없다는 것이 분명합니다 (할 수는 있지만). 이것에 대한 좋은 이유가 있어야합니다 :)

문제의 원인은 컨테이너가 동시에 두 개의 요청 (따라서 2 개의 스레드)에서 동일한 객체를 사용하고 있기 때문입니다. 따라서 첫 번째 스레드는 MethodB를 호출하는 줄에 연결됩니다. 다음 스레드는 MethodB를 호출하는 코드로 이동 한 다음 첫 번째 스레드가 MethodB로 호출을 실행하여 문제를 일으 킵니다. 그것은 어쨌든 행동을 설명 할 것입니다. 사양에 맞지 않는 것 같지만 버그 일 수 있습니다.

일반적으로 허용 되더라도 콩에 상태를 유지하는 것은 좋은 생각이 아닙니다. 그것은 혼란 코드로 이어지고 모든 방법 호출에서 모든 주를 다시 시작하는 것을 잊어 버리는 버그로 이어질 수 있습니다.

방법들 사이에 해당 객체를 전달하는 것이 훨씬 낫고 모든 문제를 피할 수 있습니다.

아마도 인스턴스 변수를 올바르게 재 구체화하지 않을 것입니다.

인스턴스 변수

일반적으로 우리는 무국적 세션 Bean에 상태를 유지해서는 안됩니다. 인스턴스 변수에 의해 참조 된 객체는 사용 후 무효화되지 않은 경우, 요청이 끝날 때까지 살아남은 상태로 유지되며 EJB 컨테이너가 세션 Bean을 재사용하는 경우 더 길어집니다. 후자의 경우, 인스턴스 변수가 후속 요청 중에 올바르게 재 구선되도록해야합니다. 따라서 인스턴스 변수를 사용하면 다음과 같은 문제가 발생할 수 있습니다.

  • 동일한 요청 중에 다른 방법간에 공유되는 인스턴스 변수 모든 메소드 호출에서 올바른 상태로 다시 시작하는 것을 잊어 버리십시오.
  • EJB 컨테이너 풀 세션 Beans와 코드가 인스턴스 변수를 올바르게 재 구체화하지 못할 수 있습니다. 이전 요청에서 오래된 상태를 재사용하십시오
  • 인스턴스 변수가 있습니다 인스턴스 범위 힙의 공간이 더 이상 사용되지 않는 물체를 유지하는 데 사용되는 메모리 누출 문제를 일으킬 수 있습니다.

클래스 변수

예를 들어 변수와 마찬가지로 클래스 변수를 사용해서는 안됩니다. 그렇다고해서 정적 키워드를 사용해서는 안되지만주의해서 사용해야한다는 의미는 아닙니다 (예 : 불변의 상수, 일부 정적 공장 클래스 등을 정의합니다).

이것은 매우 이상하기 때문에 Netbeans와 현지 Glassfish 2.1에서 빠른 테스트를 수행했습니다.

  1. 샘플-> java ee-> servlet sationeles를 사용하여 새 프로젝트를 만듭니다. 이것은 간단한 무국적 콩과 그것을 사용하는 서블릿이있는 엔터프라이즈 프로젝트를 만듭니다.
  2. 나는 가능한 한 당신의 예에 가까운 상태로 보이도록 정찰 콩을 수정했습니다.

    @Stateless
    public class StatelessSessionBean implements StatelessSession {
    
       String clName;
    
       private void testNa() {
          System.out.println(clName);
       }
    
       public String sayHello(String name) {
          this.clName = name;
          testNa();
          return "Testcase";
       }
    }
    

이것은해야 할대로 작동합니다. 어떤 편집기를 사용하고 있는지 모르겠지만 NetBeans라면 직접 실행하는 것이 흥미로울 수 있습니다.

그것은 모두 "클래스 레벨 변수"가 의미하는 바에 달려 있습니다. 클래스 변수에는 정적 수정자가 있어야합니다. 만약에 clName 그렇지 않습니다 사례 무국적 세션 Bean의 자체 사본이 있습니다. clName. Java EE 서버는 아마도 2 개 이상의 인스턴스 인스턴스의 풀을 만들었을 것입니다. testNa() 그리고 sayHello() 임의 인스턴스로 전송됩니다.

나는 같은 문제를 경험했을 때이 질문을 우연히 발견했습니다. 필자의 경우 개인 메소드는 실제로 인스턴스 변수를 설정합니다. 내가 알아 차린 것은 때때로 인스턴스 변수가 이미 이전 요청에서 이미 설정되었다는 것입니다.

@Stateless
public class MyBean {
  String someString;

  public void doSomething() {
    internalDoSomething();
  }

  private void internalDoSomething() {
    if (someString != null) {
      System.out.println("oops, someString already contained old data");
    }

    this.someString = "foo";
  }
}

나는 그것이 의미가 있다고 생각합니다. 컨테이너가 캐시 된 인스턴스를 재사용 할 때 변수를 지우는 방법을 어떻게 알아야합니까 ...

나에게 이것은 EJB 사양에 대한 Pascal의 참조 ( "인스턴스 변수가 지원됩니다")와 Rocket Surgeon 's Rocket Surgeon의 권장 사항 ( "하지 말고 로컬 변수를 대신 사용하십시오")과 함께 인라인이며 확인합니다.

상태가없는 콩에서 인스턴스 변수를 사용하는 데 문제가 있습니다.

JEE 사양에 따르면 동일한 무국적 EJB 인스턴스가 다른 클라이언트와 공유 될 수 있습니다. 엄지 규칙은 Stateless EJBS에서 인스턴스 변수를 만드는 것이 아닙니다.

응용 프로그램에 동시에 액세스하는 두 클라이언트는 동일한 EJB 인스턴스가 제공되므로 데이터 불일치가 있기 때문에 문제가 발생할 수 있습니다.

따라서 상태가없는 EJB 콩에서 인스턴스 변수를 사용하는 것은 좋은 생각이 아닙니다.

EJB 클래스에서 글로벌 정적 클래스 변수를 사용했고 동시 상태의 EJB가 실행되었을 때 다른 인스턴스에 의해 변수가 덮어 쓰기 때문에 비슷한 문제가있었습니다.

정적 클래스 필드는 특정 클래스의 모든 인스턴스 중 하나이지만 단일 Java Virtual Machine (JVM) 내에서만 공유됩니다. 정적 클래스 필드를 업데이트한다는 것은 클래스의 모든 사례들 사이에서 필드의 가치를 공유하려는 의도를 의미합니다.

희망은 다른 사람을 도와줍니다 :)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top