web-dev-qa-db-fra.com

Beans Enterprise Java sans état et avec état

Je suis en train de suivre le didacticiel Java EE 6 et j'essaie de comprendre la différence entre les beans de 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 comme il est?

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 rechargements de la servlet dans mon navigateur l’augmentent davantage. Le problème vient de ma compréhension du fonctionnement des beans de session sans état et non pas 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 de session sans état qui se comporte différemment lorsque vous le changez en stateful?

87
Stanley kelly

La différence importante n'est pas les variables des membres privés, mais l'association d'un état à un utilisateur particulier (pensez au "panier").

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

Un haricot de session avec état doit être distribué à l'utilisateur qui l'a obtenu en premier lieu, car les informations relatives à leur panier d'achat ne doivent être connues que de lui. Le serveur d'applications s'assure que c'est bien le cas. Imaginez à quel point votre application serait populaire si vous pouviez commencer à magasiner, puis le serveur de l'application m'a donné votre bean stateful de session lorsque je suis arrivé!

Donc, votre membre de données privé 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.

89
duffymo

Stateless Session Beans (SLSB) sont non liés à un client et il y a aucune garantie pour qu'un client obtienne la même instance à chaque invocation de méthode (certains conteneurs peuvent créer et détruire des beans avec chaque session d’appel de méthode, il s’agit d’une décision spécifique à l’implémentation, mais les instances sont généralement mises en pool (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. Par conséquent, Ne les utilise pas entre les appels distants. 

En revanche, les Stateful Session Beans (SFSB) sont dédiés à un client toute leur vie, il n'y a pas d'échange ni de regroupement d'instances (il peut être supprimé de la mémoire après la passivation pour économiser des ressources, mais c'est une autre histoire) et maintenir l'état conversationnel. Cela signifie que les variables d'instance du bean peuvent conserver des données relatives au client entre les invocations de méthodes. 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'inscription, un panier d'achat, un processus de réservation, etc.) sont des cas d'utilisation typiques pour SFSB.

Une dernière chose. Si vous utilisez SFSB, vous (vous devez éviter de les injecter} dans des classes à plusieurs ressources, telles que les servlets et les beans gérés JSF (vous ne voulez pas que tous les clients la partagent). Si vous souhaitez utiliser SFSB dans votre application Web, vous devez effectuer une recherche JNDI et stocker l'instance EJB renvoyée dans l'objet HttpSession 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
}
130
Pascal Thivent

Dans ce contexte, sans état et sans état ne signifie pas exactement ce à quoi vous pouvez vous attendre.

L'état dynamique avec les EJB fait référence à ce que j'appelle é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 chargée
  • Billet d'émission

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

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

Les variables globales à l'intérieur d'un bean de session (sans état ou avec état) sont complètement différentes. Un pool de haricots sera créé dans les beans de session avec état (puisqu'un bean ne peut être utilisé que dans une conversation à la fois), alors que les beans sans état n'ont souvent qu'un seul exemple, ce qui fera que la variable globale fonctionne c'est forcément garanti.

16
cletus

Cela se produit parce que 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 obtiendrez un résultat différent car le conteneur créera plus d'instances de beans dans le pool. 

4
Neyma

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

Haricots Apatrides

  1. Stateless Session Beans sont ceux qui n'ont pas d'état conversationnel avec le client qui a appelé ses méthodes. Pour cette raison, ils peuvent créer un pool d'objets pouvant être utilisé pour interagir avec plusieurs clients.
  2. Les haricots sans état sont plus performants 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 conversationnel 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 stale pour une utilisation ultérieure. Ceci est fait pour économiser les ressources du serveur d'applications et pour supporter les échecs de bean. 
3
Pritam Banerjee

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

2
malatesh

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());
    }

}

case: 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

0
ZURA Tikaradze