Question

J'ai un bean session sans état qui contient une méthode publique, plusieurs méthodes privées et des variables de niveau instance. Vous trouverez ci-dessous un exemple de pseudo-code.

private int instanceLevelVar

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

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

Ce que je vois, c'est que methodB imprime des valeurs qui n'ont pas été transmises à MethodA. Autant que je sache, il affiche des valeurs d'autres instances du même bean. Quelle en serait la cause?

Je dois signaler que le code fonctionne comme prévu 99,9% du temps. Cependant, le taux de 0,01% me cause de graves problèmes.

Je comprends que si j’avais différentes méthodes publiques, il se peut que je n’obtienne pas le même haricot entre les appels, ce qui entraînerait ce problème. Cependant, dans ce cas, le seul appel concerne la méthode publique unique. Le conteneur (Glassfish dans ce cas) continuera-t-il à échanger les fèves entre des appels de méthode privés?

(modifier) ??J'ai renommé "niveau de classe". vers " niveau d'instance " car cela causait une certaine confusion.

Était-ce utile?

La solution

Je ne voudrais tout simplement pas utiliser la variable d'instance dans le bean session sans état. Quelle que soit la cause du problème rencontré, ce n'est probablement pas quelque chose que vous voudriez faire de toute façon. Essayez simplement d’utiliser des variables locales ou de définir des variables d’instance dans les classes d’appel que vous appelez à partir des méthodes commerciales du bean session sans état.

Autres conseils

Quand je lis Qu'est-ce qu'un bean de session? section du didacticiel J2EE 1.4:

  

Beans Session sans état

     

Un bean session sans état ne conserve pas l'état conversationnel d'un client particulier. Lorsqu'un client appelle la méthode d'un bean sans état, les variables d'instance du bean peuvent contenir un état, mais uniquement pour la durée de l'appel. Lorsque la méthode est terminée, l'état n'est plus conservé. Sauf lors de l'appel de la méthode, toutes les instances d'un bean sans état sont équivalentes, ce qui permet au conteneur EJB d'attribuer une instance à n'importe quel client.

Dans votre cas, l'appel à methodB () à partir de methodA () sera sur la même instance et équivaut à this.methodB () . J'ai donc tendance à dire que methodB () ne peut pas afficher autre chose que la valeur transmise à methodA () .

Ceci est confirmé par la première phrase de la section 7.11.8 du Spécification EJB 2.0 : " Le conteneur doit garantir qu'un seul thread peut exécuter une instance à tout moment ". Cela signifie que vous ne pouvez pas arriver à une situation où les données (dans vos variables d'instance) provenant de différents clients (threads) seront mélangées. Vous êtes assuré d’un accès unique aux variables d’instance jusqu’à ce que methodA () soit renvoyé!

Cela dit, je ne dis pas que vous n’avez pas de problème quelque part. Mais je ne pense pas que votre pseudo-code soit équivalent.

(EDIT: Après avoir lu certains commentaires sur la question du PO, il existe désormais clairement un doute sur le pseudo-code et la sémantique utilisés. Je clarifie les conséquences possibles ci-dessous.)

Comme le souligne Rocket Surgeon, qu'entendez-vous exactement par variable de classe ? Voulez-vous vraiment dire variable de classe par opposition à variable d'instance ? Si oui, le pseudo-code ne le reflète pas, mais cela conduira clairement à un comportement imprévisible. En fait, dans la section 24.1.2 (et le premier point) de la spécification EJB 2.0, il est clair que vous n’êtes pas autorisé à écrire des données dans une variable de classe (bien que vous puissiez le faire). Il doit y avoir une bonne raison à cela:)

La cause probable du problème est que le conteneur utilise le même objet dans deux requêtes (donc deux threads) en même temps. Ainsi, le premier thread atteint la ligne qui appelle la méthode B, puis le thread suivant, le code qui appelle la méthode B, puis le premier exécute l'appel à la méthode B, ce qui provoque le problème. Cela expliquerait le comportement, en tout cas. Cela ne semble pas correspondre à la spécification, mais cela pourrait simplement être un bug.

En général, même si cela est autorisé, le maintien de l'état dans le haricot n'est pas une bonne idée. Cela conduit à un code de confusion et peut facilement conduire à des bugs où vous oubliez de recommencer avec tout votre état à chaque appel de méthode.

Il serait bien préférable de simplement passer ces objets entre les méthodes pour éviter tous les problèmes.

Probablement que vous ne réinitialisez pas correctement la variable d'instance.

Variables d'instance

En général, nous ne devrions pas conserver cet état dans notre bean session sans état. Les objets référencés par les variables d'instance, s'ils ne sont pas annulés après leur utilisation, sont conservés en vie jusqu'à la fin de la demande et même plus longtemps si notre conteneur EJB met en pool les beans de session à réutilisés. Dans ce dernier cas, nous devons nous assurer que cette variable d'instance est correctement réinitialisée lors d'une requête ultérieure. Par conséquent, l'utilisation de variables d'instance peut entraîner les problèmes suivants:

  • au cours de la même demande, une variable d'instance partagée entre différentes méthodes peut facilement conduire à des bogues où nous oublions de recommencer avec l'état correct à chaque appel de méthode
  • dans le cas où un conteneur EJB met en pool des beans de session et que notre code ne parvient pas à réinitialiser correctement les variables d'instance, nous pouvons réutiliser un état obsolète défini dans une requête précédente
  • Les
  • variables d’instance ont étendue d’instance , ce qui pourrait entraîner des problèmes de fuite de mémoire lorsque l’espace dans le segment de mémoire est utilisé pour conserver les objets qui ne sont plus (ou ne devraient plus) être utilisés.

Variables de classe

Comme pour les variables d'instance, les variables de classe ne doivent pas être utilisées pour conserver l'état partagé dans le bean de session sans état. Cela ne signifie pas que nous ne devrions pas utiliser le mot clé static, mais que nous devons l'utiliser avec prudence (par exemple, définir des constantes immuables, une classe de fabrique statique, etc.)

Comme cela est très étrange, j’ai effectué un test rapide avec Netbeans et mon programme local Glassfish 2.1.

  1. Créez un nouveau projet à l'aide de Samples- > Java EE- > Servlet Stateless. Cela crée un projet d'entreprise avec un simple bean sans état et un servlet qui l'utilise.
  2. J'ai modifié le bean sans état pour qu'il ressemble à ceci, aussi proche de votre exemple que possible, je pense.

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

Cela fonctionne comme il se doit. Je ne sais pas quel éditeur vous utilisez, mais s'il s'agit de Netbeans, il peut être intéressant de le lancer vous-même.

Tout dépend de ce que vous entendez par "variable de niveau de classe". Une variable de classe doit avoir le modificateur static. Si nomClient ne le fait pas, chaque instance de votre bean session sans état possède sa propre copie de nomClient . Votre serveur Java EE a probablement créé un pool de deux instances ou plus du bean session sans état, et chacun de vos appels à testNa () et à sayHello () est envoyé à une instance arbitraire.

Je suis tombé sur cette question lorsque j'ai rencontré le même problème. Dans mon cas, la méthode privée définit réellement la variable d'instance. Ce que j’ai remarqué, c’est que parfois la variable d’instance était déjà définie, de toute évidence à partir d’une requête précédente.

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

Je suppose que cela a du sens. Lorsque le conteneur réutilise une instance mise en cache, comment doit-il savoir comment effacer les variables ...

Pour moi, cela est en ligne avec et confirme la référence de Pascal à la spécification EJB ("les variables d'instance sont prises en charge") et à la recommandation de Rocket Surgeon ("ne le faites pas, utilisez plutôt des variables locales").

Le problème lié à l'utilisation de variables d'instance dans des beans sans état.

Selon la spécification JEE, cette même instance EJB sans état peut également être partagée avec un autre client. La règle du pouce n’est pas de créer des variables d’instance dans des EJB sans état.

Il est possible que les deux clients accédant à l'application simultanément se voient attribuer la même instance d'EJB, ce qui créerait des problèmes en raison d'une incohérence dans les données.

Il est donc déconseillé d'utiliser des variables d'instance dans les beans EJB sans état.

J'avais un problème similaire car j'avais utilisé une variable de classe statique globale dans ma classe ejb et lorsque j'avais un EJB simultané sans état, la variable était remplacée par d'autres instances.

Les champs de classe statiques sont partagés entre toutes les instances d'une classe particulière, mais uniquement au sein d'une seule machine virtuelle Java (JVM). La mise à jour d'un champ de classe statique implique l'intention de partager la valeur du champ entre toutes les instances de la classe.

J'espère aider quelqu'un d'autre:)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top