Сессионный компонент без сохранения состояния с переменными экземпляра - PullRequest
15 голосов
/ 27 октября 2009

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

private int instanceLevelVar

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

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

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

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

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

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

Ответы [ 9 ]

24 голосов
/ 27 октября 2009

Когда я читаю Что такое сессионный компонент? раздел руководства по J2EE 1.4:

Сессионные бобы без состояния

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

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

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

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

(РЕДАКТИРОВАТЬ: После прочтения некоторых комментариев к вопросу OP, теперь есть явное сомнение относительно используемого псевдокода и семантики. Я проясняю возможные последствия ниже.)

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

10 голосов
/ 27 октября 2009

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

5 голосов
/ 27 октября 2009

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

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

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

3 голосов
/ 06 января 2014

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

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

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

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

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

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

2 голосов
/ 27 октября 2009

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

  1. Создайте новый проект, используя Samples-> Java EE-> Servlet Stateless. Это создает корпоративный проект с простым компонентом без сохранения состояния и сервлетом, который его использует.
  2. Я изменил 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, может быть интересно запустить его самостоятельно.

0 голосов
/ 15 мая 2017

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

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

Надеюсь помочь кому-нибудь еще:)

0 голосов
/ 23 сентября 2016

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

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

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

Так что не стоит использовать переменные экземпляра в EJB-компонентах без сохранения состояния.

0 голосов
/ 24 августа 2011

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

@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 («переменные экземпляра поддерживаются»), так и рекомендацию Rocket Surgeon («не делайте этого, используйте вместо этого локальные переменные»).

0 голосов
/ 27 октября 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...