Подтвердить что ты не робот

Сеанс без учета состояния bean с переменными экземпляра

У меня есть сеанс без состояния bean, который содержит один открытый метод, несколько частных методов и некоторые переменные уровня экземпляра. Ниже приведен пример псевдокода.

private int instanceLevelVar

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

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

Что я вижу, так это то, что метод B печатает значения, которые не были переданы в MethodA. Насколько я могу сказать, он печатает значения из других экземпляров одного и того же bean. Что может вызвать это?

Я должен указать, что код работает как ожидалось в 99,9% случаев. Однако 0,01% вызывает некоторые серьезные проблемы/проблемы для меня.

Я понимаю, что если бы у меня были разные общедоступные методы, я мог бы не возвращать те же bean между вызовами, что могло бы привести к такому поведению. Однако в этом случае единственным вызовом является единственный публичный метод. Будет ли контейнер (Glassfish в этом случае) поменять beans между вызовами частных методов?

(edit) Я переименовал "уровень класса" в "уровень экземпляра", поскольку это вызывало некоторую путаницу.

4b9b3361

Ответ 1

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

Ответ 2

Когда я читаю Что такое раздел сеанса Bean? учебника J2EE 1.4:

Сеанс без учета состояния Beans

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

В вашем случае вызов methodB() из methodA() будет в том же экземпляре и эквивалентен this.methodB(). Поэтому я склонен говорить, что methodB() не может выводить что-то еще, что значение, которое было передано methodA().

Это подтверждается первым предложением в разделе 7.11.8 в Спецификация EJB 2.0: "Контейнер должен гарантировать, что только один поток может выполнять экземпляр в любое время". Это означает, что вы не можете столкнуться с ситуацией, когда данные (в ваших переменных экземпляра) из разных клиентов (потоков) будут перемешаны. Вам предоставляется уникальный доступ к переменным экземпляра до тех пор, пока methodA() не вернется!

Тем не менее, я не говорю, что у вас нет проблемы где-то. Но я не думаю, что ваш псевдо-код эквивалентен.

(EDIT: прочитав некоторые комментарии к вопросу OP, теперь явно сомневается в использовании псевдокода и семантики. Я уточняю возможные последствия ниже.)

Как подчеркивается Rocket Surgeon, что вы имеете в виду именно по переменной класса? Вы действительно имеете в виду переменную класса, а не переменную экземпляра? Если да, псевдокод не отражает это, но это явно приведет к непредсказуемому поведению. Фактически, из раздела 24.1.2 (и первой точки) в спецификации EJB 2.0 ясно, что вам не разрешено записывать данные в переменную класса (хотя вы можете это сделать). Для этого должна быть веская причина:)

Ответ 3

Вероятная причина проблемы заключается в том, что контейнер использует один и тот же объект в двух запросах (поэтому два потока) одновременно. Таким образом, первый поток получает строку, которая вызывает метод B, а затем следующий поток получает код, который вызывает метод B, а затем первый поток выполняет вызов метода B, вызывая проблему. Во всяком случае, это объясняет поведение. Кажется, это не соответствует спецификации, но это может быть просто ошибкой.

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

Было бы намного лучше просто передать эти объекты между методами, и это позволит избежать всех проблем.

Ответ 4

Вероятно, вы не повторно инициализируете переменную экземпляра.

Переменные экземпляра

В общем случае мы не должны сохранять состояние в нашей сессии без состояния bean. Объекты, на которые ссылаются переменные экземпляра, если они не нулевые после их использования, сохраняются в памяти до конца запроса и даже дольше, если наш контейнер EJB пуст сеанс beans для повторного использования. В последнем случае нам нужно убедиться, что переменная экземпляра будет правильно повторно инициализирована во время последующего запроса. Поэтому использование переменных экземпляра может привести к следующим проблемам:

  • во время одного и того же запроса переменная экземпляра, разделяемая между различными методами, может легко привести к ошибкам, в которых мы забываем начать с правильного состояния при каждом вызове метода
  • в случае, если контейнер контейнеров EJB сеанса beans, и мы можем, чтобы наш код не смог правильно инициализировать переменные экземпляра, мы можем повторно использовать устаревшее состояние в предыдущем запросе
  • переменные экземпляра имеют область экземпляра, что может вызвать проблемы с утечкой памяти, когда пространство в куче используется для хранения объектов, которые больше не используются (или не должны) использоваться.

Переменные класса

Как и для переменных переменных, переменные класса не должны использоваться для сохранения общего состояния в сеансе без состояния bean. Это не означает, что мы не должны использовать статическое ключевое слово, но мы должны использовать его с осторожностью (например, определить неизменяемые константы, некоторый статический класс factory и т.д.)

Ответ 5

Поскольку это очень странно, я провел быстрый тест с Netbeans и моей локальной Glassfish 2.1.

  • Создайте новый проект, используя Samples- > Java EE- > Servlet Stateless. Это создает корпоративный проект с простым безстоящим bean и сервлетом, который его использует.
  • Я изменил безгражданство bean, чтобы выглядеть так, как можно ближе к вашему примеру.

    @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, может быть интересно запустить его самостоятельно.

Ответ 6

Все зависит от того, что вы подразумеваете под "переменной уровня класса". Переменная класса должна иметь статический модификатор. Если clName нет, то каждый экземпляр вашего сеанса без состояния bean имеет свою собственную копию clName. Возможно, ваш сервер Java EE создал пул из двух или более экземпляров сеанса без состояния bean, и каждый из ваших вызовов на testNa() и sayHello() отправляется на произвольный экземпляр.

Ответ 7

Я наткнулся на этот вопрос, когда испытал ту же проблему. В моем случае частный метод фактически устанавливает переменную экземпляра. Я заметил, что иногда переменная экземпляра уже была установлена, очевидно, из предыдущего запроса.

@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";
  }
}

Думаю, это имеет смысл. Когда контейнер повторно использует кешированный экземпляр, как он должен знать, как очистить переменные...

Для меня это соответствует и подтверждает как ссылку Pascal на спецификацию EJB ( "поддерживаются переменные экземпляра" ), так и рекомендация Rocket Surgeon ( "не делайте этого, вместо этого используйте локальные переменные" ).

Ответ 8

Проблема с использованием переменных экземпляра в stateless Beans.

В соответствии со спецификацией JEE тот же экземпляр EJB без гражданства может быть передан другому клиенту. Правило большого пальца заключается в том, чтобы не создавать переменные экземпляра в EJB без сохранения.

Возможно, двум клиентам, обращающимся к приложению одновременно, предоставлен один и тот же экземпляр EJB, который создаст проблемы, поскольку существует некорректность данных.

Таким образом, не рекомендуется использовать переменные экземпляра в безэлементном EJB beans.

Ответ 9

У меня была аналогичная проблема, потому что я использовал глобальную переменную статического класса в моем классе ejb, и когда у меня был параллельный запуск EJB без сохранения состояния, переменная была перезаписана другими экземплярами.

Поля статического класса распределяются между всеми экземплярами определенного класса, но только внутри одной виртуальной машины Java (JVM). Обновление поля статического класса подразумевает намерение использовать значение поля для всех экземпляров класса.

Надежда помочь кому-то еще:)