SeamFramework.orgCommunity Documentation

Kapitel 1. Erste Schritte mit Web Beans

1.1. Ihr erstes Web Bean
1.2. Was ist ein Web Bean?
1.2.1. API-Typen, Binding-Typen und Dependency-Einspeisung
1.2.2. Deployment-Typen
1.2.3. Geltungsbereich
1.2.4. Web Bean Namen und Unified EL
1.2.5. Interzeptor Binding-Typen
1.3. Welche Art von Objekten können Web Beans sein?
1.3.1. Einfache Web Beans
1.3.2. Enterprise Web Beans
1.3.3. Producer-Methoden
1.3.4. JMS-Endpunkte

Können Sie es jetzt kaum erwarten Ihr erstes Web Bean zu schreiben? Oder sind Sie etwas skeptisch und fragen sich, welche Hürden Ihnen bei der Web Beans Spezifikation bevorstehen? Die gute Nachricht ist, dass Sie wahrscheinlich schon hunderte, wenn nicht tausende von Web Beans geschrieben haben. Vielleicht erinnern Sie sich nicht einmal an das erste Web Bean, das Sie je geschrieben haben.

Mit bestimmten, ganz besonderen Ausnahmen ist jede Java-Klasse mit einem Konstruktor, die keine Parameter akzeptiert ein Web Bean. Das beinhaltet jedes JavaBean. Desweiteren ist jedes EJB 3-artige Session Bean ein Web Bean. Sicher, die von Ihnen täglich geschriebenen JavaBeans und EJBs konnten die neuen, in der Web Beans Spezifikation definierten Dienste nicht nutzen, aber Sie werden diese allesamt benutzen können können, das Web Beans — diese in andere Web Beans einspeisen, diese via der Web Beans XML-Konfigurationseinrichtung konfigurieren und diesen sogar Interzeptoren und Dekoratoren hinzufügen, ohne den bestehenden Code anzurühren.

Nehmen wir an, Sie besitzen zwei bestehende Java Klassen, die bis dato in verschiedenen Anwendungen verwendet wurden. Die erste Klasse parst einen String in eine Liste von Sätzen:

public class SentenceParser {

    public List<String
> parse(String text) { ... }
}

Bei der zweiten bestehenden Klasse handelt es sich um das Front-End eines "stateless Session Beans" für ein externes System, das in der Lage ist Sätze von einer Sprache in eine andere zu übersetzen:

@Stateless

public class SentenceTranslator implements Translator {
    public String translate(String sentence) { ... }
}

Wo Translator das lokale Interface ist:

@Local

public interface Translator {
    public String translate(String sentence);
}

Leider besitzen wir keine bereits bestehende Klasse die ganze Textdokumente übersetzt. Schreiben wir also ein Web Bean, das diesen Job übernimmt:

public class TextTranslator {

    
    private SentenceParser sentenceParser;
    private Translator sentenceTranslator;
    
    @Initializer
    TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) {
        this.sentenceParser = sentenceParser;
        this.sentenceTranslator = sentenceTranslator;
    }
    
    public String translate(String text) {
        StringBuilder sb = new StringBuilder();
        for (String sentence: sentenceParser.parse(text)) {
            sb.append(sentenceTranslator.translate(sentence));
        }
        return sb.toString();
    }
    
}

Wir erhalten eine Instanz von TextTranslator durch dessen Einspeisung in ein Web Bean, Servlet oder EJB:

@Initializer

public setTextTranslator(TextTranslator textTranslator) {
    this.textTranslator = textTranslator;
}

Alternativ erhalten wir eine Instanz durch direkten Aufruf einer Methode des Web Bean Managers:

TextTranslator tt = manager.getInstanceByType(TextTranslator.class);

Aber warten Sie: TextTranslator besitzt keinen Konstruktor ohne Parameter! Handelt es sich noch um ein Web Bean? Nun, eine Klasse, die keinen Konstruktor ohne Parameter besitzt, kann nach wie vor Web Bean sein, falls es einen mit @Initializer annotierten Konstruktor besitzt.

Wie Sie wahrscheinlich bereits erraten haben, hat die @Initializer-Annotation etwas mit Dependency-Einspeisung zu tun! @Initializer kann am Konstruktor oder der Methode eines Web Beans angewendet werden und teilt dem Web Bean Manager mit, diesen Konstruktor oder diese Methode bei Instantiierung des Web Beans aufzurufen. Der Web Bean Manager speist andere Web Beans in die Parameter des Konstruktors oder der Methode ein.

Zum Zeitpunkt der Systeminitialisierung muss der Web Bean Manager validieren, dass genau ein Web Bean existiert, das jedem Einspeisungspunkt gerecht wird. Für unser Beispiel bedeutet das, wenn keine Implementierung von Translator verfügbar ist — wenn der SentenceTranslator EJB nicht deployt wurde — dass der Web Bean Manager eine UnsatisfiedDependencyException melden würde. Wäre mehr als eine Implementierung von Translator verfügbar, so würde der Web Bean Manager eine AmbiguousDependencyException melden.

Was also genau ist ein Web Bean?

Bei einem Web Bean handelt es sich um eine Anwendungsklasse, die Business Logik enthält. Ein Web Bean kann direkt von Java Code oder via Unified EL aufgerufen werden. Ein Web Bean kann auf transaktionale Ressourcen zugreifen. Abhängigkeiten zwischen Web Beans werden automatisch durch den Web Bean Manager verwaltet. Die meisten Web Beans sind stateful und kontextbezogen. Der Lebenszyklus eines Web Beans wird immer durch den Web Bean Manager verwaltet.

Erinnern wir uns. Was genau bedeutet es, "kontextuell" zu sein? Da Web Beans "stateful" sein können, ist es relevant welche Bean-Instanz ich besitze. Anders als ein Komponentenmodell, das "stateless" ist (etwas "stateless" Session Beans) oder ein Singleton Komponentenmodell (wie Servlets oder Singleton Beans), sehen verschiedene Clients eines Web Beans das Web Bean in unterschiedlichen Stati. Der Client-sichtbare Status ist abhängig davon, auf welche Instanz des Web Beans der Client verweist (eine Referenz besitzt).

Wie beim "stateless" oder "singleton" Modell anders jedoch als bei "stateful" Session Beans, steuert der Client den Lebenszyklus der Instanz nicht durch expliziertes Erstellen und Löschen. Stattdessen bestimmt der Geltungsbereich des Web Beans:

Für einen bestimmten Thread in einer Web Beans Anwendung kann ein aktiver Kontext mit dem Geltungsbereich des Web Beans assoziiert sein. Dieser Kontext kann eindeutig für den Thread sein (etwa wenn für die Web Bean Anfrage ein Geltungsbereich gilt) oder aber kann mit anderen Threads (etwa wenn für die Web Bean ein Session-Geltungsbereich gilt) oder gar allen Threads (falls ein Anwendungs-Geltungsbereich gilt) geteilt werden.

Clients (etwa andere Web Beans), die in demselben Kontext ausführen sehen dieselbe Instanz des Web Beans. Clients in einem anderen Kontext aber sehen eine andere Instanz.

Ein großer Vorteil des kontextuellen Modells ist es, dass es uns gestattet, stateful Web Beans wie Dienste zu behandeln! Der Client muss sich keine Gedanken um das Management des Lebenszyklus des verwendeten Web Beans machen und muss nicht einmal wissen was der Lebenszyklus ist. Web Beans interagieren durch Weitergabe von Nachrichten und die Web Bean Implementierungen definieren den Lebenszyklus ihres eigenen Status. Die Web Beans sind lose gepaart, weil:

Wir können ein Web Bean durch ein anderes Web Bean ersetzen, das dasselbe API und einen anderen Lebenszyklus (einen anderen Geltungsbereich) besitzt, ohne dass die übrige Web Bean Implementierung hiervon betroffen ist. Genau genommen definieren Web Beans eine raffinierte Einrichtung zur Außerkraftsetzung von Web Bean Implementierungen zum Zeitpunkt des Deployment wie wir in Abschnitt 4.2, „Deployment Typen“ noch sehen werden.

Beachten Sie, dass es sich nicht bei allen Clients eines Web Beans um Web Beans handelt. Andere Objekte wie Servlets oder Message-Driven Beans — die ihrem Wesen nach nicht einspeisbar sind, kontextuelle Objekte — können durch Einspeisung ebenfalls Verweise auf ein Web Beans erhalten.

Formeller gilt, gemäß der Spezifikation:

Ein Web Bean besteht aus:

  • Einem (nicht leeren) Satz von API-Typen

  • Einem (nicht leeren) Satz von bindenden Annotationstypen

  • Einem Geltungsbereich

  • Einem Deployment-Typ

  • Optional einem Web Bean Namen

  • Ein Satz Interzeptor Binding-Typen

  • Einer Web Bean Implementierung

Sehen wir uns jetzt genauer an, was diese Begriffe für einen Entwickler von Web Beans bedeuten.

Web Beans erhalten Verweise auf andere Web Beans in der Regel via "Dependency"-Einspeisung. Jedes eingespeiste Attribut legt einen "Vertrag" fest, der vom einzuspeisenden Web Bean erfüllt sein muss. Der Vertrag lautet:

Bei einem API handelt es sich um eine benutzerdefinierte Klasse oder Interface. (Falls es sich bei dem Web Bean um ein EJB Session Bean handelt, so ist der API-Typ das @Local-Interface oder Bean-Klasse lokale Ansicht). Ein Binding-Typ repräsentiert Client-sichtbare Semantik, die von einigen Implementierungen des API erfüllt wird, von anderen wiederum nicht.

Binding-Typen werden durch benutzerdefinierte Annotationen repräsentiert, die ihrerseits mit @BindingType annotiert sind. Zum Beispiel besitzt der folgende Einspeisungspunkt den API-Typ PaymentProcessor und Binding-Typ @CreditCard:

@CreditCard PaymentProcessor paymentProcessor

Wird an einem Einspeisungspunkt kein Binding-Typ explizit festgelegt, so wird vom standardmäßigen Binding-Typ @Current ausgegangen.

Für jeden Einspeisungspunkt sucht der Web Bean Manager nach einem Web Bean, das den Vertrag erfüllt (das API implementiert und alle Binding-Typen besitzt) und speist dieses Web Bean ein.

Das folgende Web Bean besitzt den Binding-Typ @CreditCard und implementiert den API-Typ PaymentProcessor. Es könnte daher am Beispiel-Einspeisungspunkt eingespeist werden:

@CreditCard

public class CreditCardPaymentProcessor 
    implements PaymentProcessor { ... }

Falls ein Web Bean nicht explizit einen Satz von Binding-Typen festlegt, so besitzt es genau einen Binding-Typ: den standardmäßigen Binding-Typ @Current.

Web Beans definiert einen fortgeschrittenen aber intuitiven Auflösungsalgorithmus, der dem Container dabei hilft zu entscheiden was geschehen soll, wenn mehr als ein Web Bean einen bestimmten Vertrag erfüllt. Wir gehen in Kapitel 4, Dependency-Einspeisung näher darauf ein.

Wir haben bereits gesehen, dass JavaBeans, EJBs und einige andere Java-Klassen Web Beans sein können. Aber um was für Objekte genau handelt es sich bei Web Beans?

Die Spezifikation besagt, dass alle EJB 3-style Session und Singleton Beans Enterprise Web Beans sind. Message-driven Beans sind keine Web Beans — da sie nicht zur Einspeisung in andere Objekte vorgesehen sind — aber sie können den größten Teil der Funktionalität von Web Beans nutzen, darunter auch "Dependency"-Einspeisung und Interzeptoren.

Jedes lokale Interface eines Enterprise Web Beans und jedes seiner Super-Interfaces, das keinen Platzhaltertyp-Parameter oder eine Typenvariable besitzt, ist ein API-Typ des Enterprise Web Beans. Falls das EJB-Bean eine lokale Ansicht der Bean-Klasse besitzt, so handelt es sich auch bei der Bean-Klasse und jede von deren Super-Klassen um einen API-Typ.

Stateful Session Beans sollten eine Entfernungsmethode ("remove method") ohne Parameter oder eine Entfernungsmethode mit der Annotation @Destructor deklarieren. Der Web Bean Manager ruft diese Methode auf, um die Instanz des stateful Session Beans am Ende von deren Lebenszyklus zu löschen. Diese Methode nennt sich Destructor-Methode des Enterprise Web Beans.

@Stateful @SessionScoped

public class ShoppingCart {
    ...
    
    @Remove
    public void destroy() {}
}

Sollten wir also ein Enterprise Web Bean statt eines einfachen Web Beans verwenden? Nun, wenn wir ausgefeilte, durch EJB bereitgestellte Enterprise-Dienste benötigen, wie etwa:

so sollten wir ein Enterprise Web Bean verwenden. Wenn wir nichts von alledem brauchen, so reicht ein einfaches Web Bean vollkommen aus.

Viele Web Beans (einschließlich session- oder anwendungsbegrenzte Web Beans) sind für nebenläufigen Zugriff verfügbar. Daher ist das durch EJB 3.1 bereitgestellte Nebenläufigkeits-Management besonders nützlich. Die meisten session- oder anwendungsbegrenzten Web Beans sollten EJBs sein.

Web Beans, die Verweise auf schwergewichtige Ressourcen oder eine Menge internen Status besitzen, haben Vorteile durch den fortgeschrittenen, Container-gemanagten, durch das EJB @Stateless/@Stateful/@Singleton-Modell definierten Lebenszyklus und dessen Support von Passivation und Instanz-Pooling.

Schließlich ist es offenkundig, wenn Transaktions-Management auf Methodenebene, Sicherheit auf Methodenebene, Timer, Remote-Methoden oder asynchrone Methoden benötigt werden.

Es ist in der Regel leicht, mit einem einfachen Web Bean zu beginnen und es dann zu einem EJB zu machen, indem man eine Annotation: @Stateless, @Stateful oder @Singleton hinzufügt.