Beans Java d'entreprise sans état et avec état

93

Je suis en train de suivre le didacticiel Java EE 6 et j'essaie de comprendre la différence entre les beans session sans état et avec état. Si les beans session sans état ne conservent pas leur état entre les appels de méthode, pourquoi mon programme agit-il ainsi?

package mybeans;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;

@LocalBean
@Stateless
public class MyBean {

    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void increment() {
        this.number++;
    }
}

Le client

import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
import mybeans.MyBean;
import java.io.PrintWriter;

@WebServlet(name = "ServletClient", urlPatterns = { "/ServletClient" })
public class ServletClient extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @EJB
    MyBean mybean;

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        PrintWriter out = response.getWriter();
        mybean.increment();
        out.println(mybean.getNumber());
    }

}

Je m'attendais à ce que getNumber renvoie 0 à chaque fois, mais il renvoie 1 et les recharges du servlet dans mon navigateur l'augmentent davantage. Le problème vient de ma compréhension du fonctionnement des beans session sans état et non des bibliothèques ou du serveur d'applications, bien sûr. Quelqu'un peut-il me donner un exemple simple de type hello world d'un bean session sans état qui se comporte différemment lorsque vous le changez en stateful?

Stanley Kelly
la source
6
Connexes: stackoverflow.com/questions/8887140/… Cette réponse est peut-être plus simple à comprendre. Notez que les servlets sont essentiellement axés sur l'application (il n'y a qu'une seule instance de servlet dans toute l'application qui est partagée / réutilisée entre toutes les requêtes / sessions HTTP.
BalusC
salut, vous faites d'abord incrémenter, puis obtenez la valeur .... donc vous ne pouvez pas vous attendre à une valeur de 0.
rzur2004
Je veux juste vous remercier d'avoir posé cette question, cela résout mon problème pour le moment. Je n'aurais pas pu mieux
demander

Réponses:

93

La différence importante ne réside pas dans les variables de membre privé, mais dans l'association d'un état à un utilisateur particulier (pensez à «panier»).

Le morceau avec état du bean session avec état est comme la session dans les servlets. Les beans session avec état permettent à votre application de conserver cette session même s'il n'y a pas de client Web. Lorsque le serveur d'application extrait un bean session sans état du pool d'objets, il sait qu'il peut être utilisé pour satisfaire N'IMPORTE QUELLE requête, car il n'est associé à aucun utilisateur particulier.

Un bean session avec état doit être distribué à l'utilisateur qui l'a obtenu en premier lieu, car les informations de son panier ne doivent être connues que de lui. Le serveur d'application garantit qu'il en est ainsi. Imaginez à quel point votre application serait populaire si vous pouviez commencer à magasiner, puis le serveur de l'application m'a donné votre bean session avec état lorsque je suis arrivé!

Donc, votre membre de données privées est bien "état", mais ce n'est pas "panier". Essayez de refaire votre (très bon) exemple pour que la variable incrémentée soit associée à un utilisateur particulier. Incrémentez-le, créez un nouvel utilisateur et voyez s'il peut toujours voir la valeur incrémentée. Si cela est fait correctement, chaque utilisateur ne devrait voir que sa version du compteur.

duffymo
la source
Pouvez-vous fournir dans un commentaire une réponse explicite? Pourquoi le bean sans état de cet exemple contient-il toujours la valeur et l'augmente-t-elle à chaque fois? Parce qu'il n'y a qu'un seul utilisateur?
arjacsoh
2
Le compteur s'incrémentera quel que soit le nombre d'utilisateurs. Donc, si l'utilisateur1 entre et incrémente le compteur à 1 et que l'utilisateur2 entre et l'incrémente simultanément, la valeur sera 2. Il devrait en fait montrer que l'utilisateur1 a 1 et l'utilisateur2 a 1 (si c'est ce que vous avez l'intention de faire. exemple comme ci-dessus).
Krishna
137

Stateless Session Beans (SLSB) ne sont pas liés à un client et il n'y a aucune garantie pour un client d'obtenir la même instance avec chaque appel de méthode (certains conteneurs peuvent créer et détruire des beans avec chaque session d'invocation de méthode, il s'agit d'une décision spécifique à l'implémentation , mais les instances sont généralement regroupées - et je ne mentionne pas les environnements en cluster). En d'autres termes, bien que les beans sans état puissent avoir des variables d'instance, ces champs ne sont pas spécifiques à un client, donc ne vous fiez pas à eux entre les appels distants.

En revanche, les Stateful Session Beans (SFSB) sont dédiés à un client pour toute sa vie, il n'y a pas d'échange ou de regroupement d'instances (il peut être expulsé de la mémoire après passivation pour économiser des ressources mais c'est une autre histoire) et maintenir l'état de conversation . Cela signifie que les variables d'instance du bean peuvent conserver des données relatives au client entre les appels de méthode. Et cela permet d'avoir des appels de méthode interdépendants (les modifications apportées par une méthode affectent les appels de méthode suivants). Les processus en plusieurs étapes (un processus d'enregistrement, un panier d'achat, un processus de réservation ...) sont des cas d'utilisation typiques de SFSB.

Encore une chose. Si vous utilisez SFSB, vous devez éviter de les injecter dans des classes de nature multithread, telles que les servlets et les beans gérés JSF (vous ne voulez pas qu'il soit partagé par tous les clients). Si vous souhaitez utiliser SFSB dans votre application Web, vous devez effectuer une recherche JNDI et stocker l'instance EJB renvoyée dans l' HttpSessionobjet pour une activité future. Quelque chose comme ca:

try {
    InitialContext ctx = new InitialContext();
    myStateful = (MyStateful)ctx.lookup("java:comp/env/MyStatefulBean");
    session.setAttribute("my_stateful", myStateful);
} catch (Exception e) {
    // exception handling
}
Pascal Thivent
la source
Merci pour l'éclaircissement. Lorsque j'utilise un programme de ligne de commande autonome pour le client, il est évident de voir la différence.
Stanley kelly
merci pour vos commentaires, ils sont plus éclairants. Vous donnez d'abord la définition abstraite, puis spécifiez quelques cas d'utilisation pour chaque situation, puis indiquez quelques écueils. Super +1
arthur
La partie éviter d'injecter est-elle également désactivée pour EJB 3.1?
jacktrades
7
@Pascal si "Stateful Session Beans (SFSB) sont dédiés à un client pour toute sa vie", c'est-à-dire que cette capacité est intégrée à SFSB, alors pourquoi avoir besoin de les stocker sur un objet HttpSession?
user1169587
2
Pourquoi avons-nous besoin de maintenir le bean avec état en session s'il est déjà «en session»? De cette façon, nous pouvons rendre chaque objet en session. Expliquer pls
Georgy Gobozov
18

Stateless et stateful dans ce contexte ne signifient pas tout à fait ce à quoi vous pourriez vous attendre.

L'état avec les EJB fait référence à ce que j'appelle l'état conversationnel . L'exemple classique est une réservation de vol. S'il se compose de trois étapes:

  • Siège de réserve
  • Carte de crédit débitée
  • Billet d'émission

Imaginez que chacun d'entre eux soit un appel de méthode à un bean session. Un bean session avec état peut maintenir ce type de conversation afin de se souvenir de ce qui se passe entre les appels.

Les beans session sans état n'ont pas une telle capacité pour l'état conversationnel.

Les variables globales à l'intérieur d'un bean session (sans état ou avec état) sont tout autre chose. Les beans session avec état auront un pool de beans créé (puisqu'un bean ne peut être utilisé que dans une conversation à la fois) alors que les beans sesion sans état n'auront souvent qu'une seule instance, ce qui fera fonctionner la variable globale, mais je ne pense pas cela est nécessairement garanti.

cletus
la source
5

Les principales différences entre les deux principaux types de session beans sont:

Haricots apatrides

  1. Les Session Beans sans état sont ceux qui n'ont aucun état de conversation avec le client qui a appelé ses méthodes. Pour cette raison, ils peuvent créer un pool d'objets qui peuvent être utilisés pour interagir avec plusieurs clients.
  2. Les beans stateless en termes de performances sont meilleurs car ils n'ont pas d'états par client.
  3. Ils peuvent gérer plusieurs demandes de plusieurs clients en parallèle.

Stateful Beans

  1. Les beans session avec état peuvent maintenir l' état de conversation avec plusieurs clients à la fois et la tâche n'est pas partagée entre les clients.
  2. Une fois la session terminée, l'état n'est pas conservé.
  3. Le conteneur peut sérialiser et stocker l'état en tant qu'état périmé pour une utilisation future. Cela permet d'économiser les ressources du serveur d'applications et de prendre en charge les échecs de bean.
Pritam Banerjee
la source
4

Cela se produit car le conteneur n'a qu'une seule instance de bean dans le pool qui est réutilisée pour tous les appels. Si vous exécutez les clients en parallèle, vous verrez un résultat différent car le conteneur créera plus d'instances de bean dans le pool.

Neyma
la source
4

Il a de bonnes réponses. Je voudrais ajouter une petite réponse. Stateless Bean ne doit pas contenir de données client. Il doit être utilisé pour "modéliser des actions ou des processus qui peuvent être réalisés en une seule fois".

Malatesh
la source
4

Bonne question,

essayez ce code (changez MyBean Stateful / Stateless.):

import javax.ejb.LocalBean;
import javax.ejb.Stateful;
import javax.ejb.Stateless;

@LocalBean 
@Stateless 
public class MyBean {

    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void increment() {
        this.number++;
    }
}

Servlet_1

 import java.io.IOException;
    import javax.ejb.EJB;
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.WebServlet;

    import java.io.PrintWriter;

    @WebServlet(name = "ServletClient", urlPatterns = { "/ServletClient" })
    public class ServletClient extends HttpServlet {

        private static final long serialVersionUID = 1L;

        @EJB
        MyBean mybean;

        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

            PrintWriter out = response.getWriter();
            mybean.increment();
            out.println(mybean.getNumber());
        }

    }

Servlet_2

import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;

import java.io.PrintWriter;

@WebServlet(name = "NewServletClient", urlPatterns = { "/NewServletClient" })
public class NewServletClient extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @EJB
    MyBean mybean;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        PrintWriter out = response.getWriter();
        mybean.increment();
        out.println(mybean.getNumber());
    }

}

cas: MyBean - @ Stateless

http: // localhost: 8080 / MYServletDemo / ServletClient

1

http: // localhost: 8080 / MYServletDemo / ServletClient

2

http: // localhost: 8080 / MYServletDemo_war_exploded / newServletClient

3

http: // localhost: 8080 / MYServletDemo / ServletClient

4

case: MyBean - @ Stateful

http: // localhost: 8080 / MYServletDemo / ServletClient

1

http: // localhost: 8080 / MYServletDemo / ServletClient

2

http: // localhost: 8080 / MYServletDemo / newServletClient

1

http: // localhost: 8080 / MYServletDemo / ServletClient

3

ZURA Tikaradze
la source
1
Oui, c'est ça et ça marche! Explication très simple, merci!
Nesquik