Bean de session sem estado com variables ​​de instância

Eu tenho um bean de session sem estado que contém um método público, vários methods privados e algumas variables ​​de nível de instância. Abaixo está um exemplo de pseudo-código.

private int instanceLevelVar public void methodA(int x) { this.instanceLevelVar = x; methodB(); } private void methodB() { System.out.println(instanceLevelVar); } 

O que estou vendo é que methodB está imprimindo valores que não foram passados ​​para o MethodA. O melhor que posso dizer é que imprime valores de outras instâncias do mesmo bean. O que causaria isso?

Gostaria de salientar o código funciona como esperado 99,9% do tempo. No entanto, o .01% está causando alguns problemas / preocupações sérios para mim.

Eu entendo que, se eu tivesse diferentes methods públicos, talvez eu não obtivesse o mesmo bean de volta entre as chamadas, o que resultaria nesse comportamento. No entanto, neste caso, a única chamada é para o método público único. O contêiner (Glassfish, neste caso) ainda irá trocar os beans entre as chamadas de methods privados?

(edit) Eu renomeei “nível de class” para “nível de instância”, pois isso estava causando alguma confusão.

Eu simplesmente não me incomodaria em usar a variável de instância no bean de session stateless. Independentemente da causa do problema que você encontrou, provavelmente não é algo que você gostaria de fazer de qualquer maneira. Apenas tente usar variables ​​locais por toda parte ou defina variables ​​de instância em classs auxiliares que você está chamando dos methods de negócios de bean de session sem preservação de estado.

Quando eu li O que é um bean de session? seção do tutorial do J2EE 1.4:

Feijão de Sessão sem Estado

Um bean de session sem estado não mantém um estado conversacional para um cliente específico. Quando um cliente chama o método de um bean sem estado, as variables ​​de instância do bean podem conter um estado, mas apenas pela duração da chamada. Quando o método é concluído, o estado não é mais retido. Exceto durante a invocação do método, todas as instâncias de um bean sem monitoração de estado são equivalentes, permitindo que o contêiner EJB atribua uma instância a qualquer cliente.

No seu caso, a chamada para methodB() de methodA() estará na mesma instância e é equivalente a this.methodB() . Portanto, estou tendendo a dizer que methodB() não pode produzir algo mais que o valor que foi passado para methodA() .

Isso é confirmado pela primeira sentença da seção 7.11.8 na especificação do EJB 2.0 : “O contêiner deve garantir que apenas um encadeamento possa estar executando uma instância a qualquer momento”. Isso significa que você não pode chegar a uma situação em que os dados (em suas variables ​​de instância) de diferentes clientes (segmentos) serão misturados. Você tem access exclusivo às variables ​​da instância até que o methodA() tenha retornado!

Dito isto, não estou dizendo que você não tenha um problema em algum lugar. Mas eu não acho que o seu pseudo código seja equivalente.

(EDIT: Depois de ler alguns comentários para a questão do OP, agora há claramente uma dúvida sobre o pseudo-código e a semântica usada. Estou esclarecendo possíveis conseqüências abaixo.)

Como sublinhado pelo Rocket Surgeon, o que você quer dizer exatamente por variável de class ? Você realmente quer dizer variável de class em vez de variável de instância ? Se sim, o pseudocódigo não reflete, mas isso levará claramente a um comportamento imprevisível. Na verdade, da seção 24.1.2 (e primeiro ponto) na especificação do EJB 2.0, é claro que você não tem permissão para gravar dados em uma variável de class (embora você possa fazer isso). Deve haver uma boa razão para isso 🙂

A causa provável do problema é que o contêiner está usando o mesmo object em duas solicitações (portanto, dois encadeamentos) ao mesmo tempo. Assim, o primeiro thread chega à linha que chama methodB e, em seguida, o próximo thread chega ao código que chama methodB e, em seguida, o primeiro thread executa a chamada para methodB, causando o problema. Isso explicaria o comportamento, pelo menos. Não parece se encheckboxr na especificação, mas isso pode ser apenas um bug.

Em geral, mesmo se permitido, manter o estado no bean não é uma ótima ideia. Isso leva a um código confuso e pode levar facilmente a erros nos quais você se esquece de começar tudo de novo com todo o seu estado em todas as chamadas de método.

Seria muito melhor apenas passar esses objects entre os methods, e isso evitaria todos os problemas.

Provavelmente você não está reinicializando corretamente a variável de instância.

Variáveis ​​de instância

Em geral, não devemos manter o estado em nosso bean de session sem estado. Objetos referenciados por variables ​​de instância, se não forem anulados após seu uso, são mantidos vivos até o final da solicitação e ainda mais se nosso contêiner EJB agrupar os beans de session para reutilizados. No último caso, precisamos garantir que a variável de instância seja reinicializada corretamente durante uma solicitação subsequente. Portanto, o uso de variables ​​de instância pode levar aos seguintes problemas:

  • durante a mesma solicitação, a variável de instância compartilhada entre os diferentes methods pode facilmente levar a erros nos quais esquecemos de começar de novo com o estado correto em cada chamada de método
  • no caso de beans de session de pools de contêineres EJB e nosso código falhar ao reinicializar adequadamente as variables ​​de instância, poderemos reutilizar o estado obsoleto definido em uma solicitação anterior
  • As variables ​​de instância têm escopo de instância que poderia introduzir problemas de memory leaks onde o espaço no heap é usado para manter objects que não são (ou não devem ser) usados ​​mais.

Variáveis ​​de class

Quanto a variables ​​de instância, as variables ​​de class não devem ser usadas para manter o estado compartilhado no bean de session Stateless. Isso não significa que não devemos usar a palavra-chave estática, mas devemos usá-la com cuidado (por exemplo, definir constantes imutáveis, alguma class de fábrica estática, etc.)

Como isso é muito estranho, fiz um teste rápido com o NetBeans e meu Glassfish 2.1 local.

  1. Crie um novo projeto usando Samples-> Java EE-> Servlet Stateless. Isso cria um projeto corporativo com um bean stateless simples e um servlet que o utiliza.
  2. Eu modifiquei o bean sem estado para ficar assim, o mais próximo possível do seu exemplo, eu acho.

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

Isso funciona como deveria. Eu não sei qual editor você está usando, mas se for o Netbeans, pode ser interessante executá-lo você mesmo.

Tudo depende do que você entende por “variável de nível de class”. Uma variável de class deve ter o modificador estático. Se clName não, então cada instância de seu bean de session stateless possui sua própria cópia de clName . Seu servidor Java EE provavelmente criou um conjunto de duas ou mais instâncias do bean de session sem estado, e cada uma de suas chamadas para testNa() e sayHello() é enviada para uma instância arbitrária.

Eu tropecei nessa questão quando experimentei o mesmo problema. No meu caso, o método privado, na verdade, define a variável de instância. O que eu notei é que às vezes a variável de instância já estava definida, obviamente de uma solicitação anterior.

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

Eu acho que faz sentido. Quando o contêiner reutiliza uma instância em cache, como deve saber como limpar as variables ​​…

Para mim, isso está alinhado com e confirma tanto a referência de Pascal à especificação EJB (“variables ​​de instância são suportadas”) quanto a recomendação do Rocket Surgeon (“não faça isso, use variables ​​locais”).

O problema com o uso de variables ​​de instância em Beans sem estado.

De acordo com a especificação JEE, a mesma instância EJB sem estado pode ser compartilhada com outro cliente também. A regra de polegar não é criar variables ​​de instância em EJBs sem estado.

Pode ser possível que os dois clientes que acessam o aplicativo simultaneamente recebam a mesma instância de EJB, o que criaria problemas, já que há inconsistência de dados.

Portanto, não é uma boa ideia usar variables ​​de instância em beans EJB sem estado.

Eu tive problema semelhante porque eu usei a variável de class estática global na minha class ejb e quando eu tinha o EJB simultâneo sem estado, a variável foi sobrescrita por outras instâncias.

Os campos de class estática são compartilhados entre todas as instâncias de uma class específica, mas apenas dentro de uma única Java Virtual Machine (JVM). A atualização de um campo de class estática implica uma intenção de compartilhar o valor do campo entre todas as instâncias da class.

Espero ajudar alguém 🙂