Domanda

Ho un bean di sessione senza stato che contiene un metodo pubblico, diversi metodi privati ??e alcune variabili a livello di istanza. Di seguito è riportato un esempio di pseudo codice.

private int instanceLevelVar

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

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

Quello che vedo è che methodB sta stampando valori che non sono stati passati a MethodA. Come migliore posso dire che sta stampando valori da altre istanze dello stesso bean. Cosa potrebbe causare questo?

Devo sottolineare che il codice funziona come previsto il 99,9% delle volte. Tuttavia, lo 0,01% sta causando alcuni seri problemi / preoccupazioni per me.

Comprendo che se avessi metodi pubblici diversi, potrei non recuperare lo stesso bean tra le chiamate, il che comporterebbe questo comportamento. Tuttavia, in questo caso l'unica chiamata è al metodo pubblico singolo. Il container (Glassfish in questo caso) continuerà a scambiare i bean tra chiamate di metodo private?

(modifica) Ho rinominato " livello di classe " a "livello di istanza" poiché ciò stava creando confusione.

È stato utile?

Soluzione

Non mi preoccuperei affatto di usare la variabile di istanza nel bean di sessione senza stato. Indipendentemente da quale sia la causa del problema riscontrato, probabilmente non è qualcosa che vorresti fare comunque. Prova semplicemente a utilizzare le variabili locali in tutto o definisci le variabili di istanza nelle classi helper che stai chiamando dai metodi business bean di sessione senza stato.

Altri suggerimenti

Quando leggo Che cos'è un bean di sessione? sezione del tutorial J2EE 1.4:

  

Fagioli di sessione apolidi

     

Un bean sessione senza stato non mantiene uno stato di conversazione per un determinato client. Quando un client richiama il metodo di un bean stateless, le variabili di istanza del bean possono contenere uno stato, ma solo per la durata dell'invocazione. Al termine del metodo, lo stato non viene più mantenuto. Tranne durante l'invocazione del metodo, tutte le istanze di un bean stateless sono equivalenti, consentendo al contenitore EJB di assegnare un'istanza a qualsiasi client.

Nel tuo caso, la chiamata a methodB () da methodA () sarà sulla stessa istanza ed equivale a this.methodB () . Quindi tendo a dire che methodB () non può produrre qualcos'altro che il valore che è stato passato a methodA () .

Ciò è confermato dalla prima frase nella sezione 7.11.8 in specifica EJB 2.0 : " Il contenitore deve assicurare che un solo thread possa eseguire un'istanza in qualsiasi momento " ;. Ciò significa che non puoi venire in una situazione in cui i dati (nelle variabili dell'istanza) di client (thread) diversi verranno mescolati. Ti viene garantito un accesso univoco alle variabili di istanza fino a quando methodA () non è tornato!

Detto questo, non sto dicendo che non hai problemi da qualche parte. Ma non credo che il tuo pseudo codice sia equivalente.

(EDIT: dopo aver letto alcuni commenti alla domanda del PO, ora c'è chiaramente un dubbio sullo pseudo codice e sul semantico usato. Sto chiarendo le possibili conseguenze di seguito.)

Come sottolineato da Rocket Surgeon, cosa intendi esattamente per variabile di classe ? Intendi davvero variabile di classe in contrapposizione a variabile di istanza ? Se sì, lo pseudo codice non lo riflette, ma ciò porterà chiaramente a comportamenti imprevedibili. In realtà, dalla sezione 24.1.2 (e primo punto) nelle specifiche EJB 2.0, è chiaro che non è consentito scrivere dati in una variabile di classe (sebbene sia possibile farlo). Ci deve essere una buona ragione per questo :)

La probabile causa del problema è che il contenitore sta usando lo stesso oggetto in due richieste (quindi due thread) contemporaneamente. Quindi il primo thread arriva alla linea che chiama methodB e poi il thread successivo arriva al codice che chiama methodB e quindi il primo thread esegue la chiamata a methodB, causando il problema. Ciò spiegherebbe il comportamento, in ogni caso. Non sembra adattarsi alle specifiche, ma potrebbe essere solo un bug.

In generale, anche se consentito, mantenere lo stato nel bean non è una grande idea. Porta a un codice di confusione e può facilmente portare a bug in cui ti dimentichi di ricominciare con tutto il tuo stato ad ogni chiamata di metodo.

Sarebbe molto meglio passare questi oggetti tra i metodi e ciò eviterebbe tutti i problemi.

Probabilmente non stai reinizializzando correttamente la variabile di istanza.

Variabili di istanza

In generale non dovremmo mantenere lo stato nel nostro bean di sessione senza stato. Gli oggetti a cui fanno riferimento le variabili di istanza, se non annullati dopo il loro utilizzo, vengono mantenuti in vita fino alla fine della richiesta e anche più a lungo se il nostro contenitore EJB raggruppa i bean di sessione da riutilizzare. In quest'ultimo caso, è necessario assicurarsi che la variabile di istanza venga reinizializzata correttamente durante una richiesta successiva. Pertanto l'uso di variabili di istanza può portare ai seguenti problemi:

  • durante la stessa richiesta, la variabile di istanza condivisa tra metodi diversi può facilmente portare a bug in cui dimentichiamo di ricominciare da capo con lo stato corretto ad ogni chiamata di metodo
  • nel caso in cui il contenitore EJB raccolga bean di sessione e il nostro codice non riesca a reinizializzare correttamente le variabili di istanza che potremmo riutilizzare lo stato non aggiornato impostato in una richiesta precedente
  • Le
  • variabili di istanza hanno ambito di istanza che potrebbe introdurre problemi di perdita di memoria in cui lo spazio nell'heap viene utilizzato per mantenere oggetti che non sono (o non dovrebbero più essere) utilizzati.

Variabili di classe

Come per le variabili di esempio, le variabili di classe non devono essere utilizzate per mantenere lo stato condiviso nel bean di sessione Stateless. Ciò non significa che non dovremmo usare la parola chiave statica ma che dovremmo usarla con cautela (ad esempio definire costanti immutabili, alcune classi di fabbrica statiche, ecc.)

Dato che è molto strano, ho eseguito un rapido test con Netbeans e il mio Glassfish 2.1 locale.

  1. Crea un nuovo progetto usando Samples- > Java EE- > Servlet Stateless. Questo crea un progetto aziendale con un semplice bean stateless e un servlet che lo utilizza.
  2. Ho modificato il bean stateless in modo che sia il più vicino possibile al tuo esempio.

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

Funziona come dovrebbe. Non so quale editor stai usando, ma se si tratta di Netbeans potrebbe essere interessante eseguirlo da soli.

Tutto dipende da cosa intendi per "variabile di livello di classe". Una variabile di classe deve avere il modificatore statico. Se clName no, ogni istanza del bean di sessione senza stato ha la propria copia di clName . Il tuo server Java EE ha probabilmente creato un pool di due o più istanze del bean di sessione senza stato e ciascuna delle tue chiamate a testNa () e sayHello () viene inviata a un istanza arbitraria.

Mi sono imbattuto in questa domanda quando ho riscontrato lo stesso problema. Nel mio caso, il metodo privato imposta effettivamente la variabile di istanza. Quello che ho notato è che a volte la variabile di istanza era già impostata, ovviamente da una richiesta precedente.

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

Immagino abbia senso. Quando il contenitore riutilizza un'istanza memorizzata nella cache, come dovrebbe sapere come cancellare le variabili ...

Per me, questo è in linea con e conferma sia il riferimento di Pascal alle specifiche EJB ("le variabili di istanza sono supportate") sia la raccomandazione di Rocket Surgeon ("non farlo, usa le variabili locali invece di").

Il problema con l'utilizzo delle variabili di istanza in bean stateless.

Secondo la specifica JEE, la stessa istanza EJB senza stato potrebbe essere condivisa anche con un altro client. La regola del pollice non è quella di creare variabili di istanza nei bean Stateless.

Potrebbe essere possibile che ai due client che accedono all'applicazione contemporaneamente venga fornita la stessa istanza EJB che creerebbe problemi poiché si verifica un'incoerenza dei dati.

Quindi non è una buona idea usare le variabili di istanza nei bean EJB senza stato.

Ho avuto un problema simile perché ho usato la variabile di classe statica globale nella mia classe ejb e quando avevo in esecuzione EJB senza stato simultaneo, la variabile è stata sovrascritta da altre istanze.

I campi di classe statici sono condivisi tra tutte le istanze di una particolare classe, ma solo all'interno di una singola Java Virtual Machine (JVM). L'aggiornamento di un campo di classe statica implica l'intenzione di condividere il valore del campo tra tutte le istanze della classe.

Spero di aiutare qualcun altro :)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top