Verteilung, EJB 3 und Spring
02.01.2008 Permalink Die folgenden zwei Fragen scheinen Dauerbrenner zu sein, denn in dieser und ähnlicher Form habe ich sie schon häufiger diskutiert. Da ich sie jetzt zum ersten Mal schriftlich versuche zu beantworten, gibt's das auch gleich für den Rest der Welt zum Mitlesen.
1. Was sind die Vorteile von Spring im Vergleich zu EJB3?
2. Was sind die Vorteile von Spring in einem EJB-Container im Vergleich zu
Spring im Servlet-Container?
Ich verzichte jetzt auf einen detaillierten Featurevergleich zwischen Spring und EJB3, den findet man im Web z.B. unter Comparison of Spring and EJB 3.0 und fortgesetzt im zweiten Teil. Was dort steht, ist allemal lesenswert. Eingeschränkt auf die o.g. Fragen lauten meine Antworten nun wie folgt:
zu 1.)
EJB3 ist eine Technologie für verteilte Systeme. "Verteilt" heißt z.B.
Rich-Client und zentraler Server mit Datenbankanbindung. Oder die Trennung der
Web-Schicht vom "fachlichen Kern" des Systems. Das sind jetzt technisch
motivierte Trennungen. Man kann auch fachlich gliedern und die Fachkomponenten
auf verschiedene Maschinen verteilen.
Ob und wann man Verteilung wirklich braucht, diskutiere ich gleich.
EJBs laufen in einer Ablaufumgebung namens EJB-Container, z.B. JBoss. Ein Tomcat ist ebenfalls eine Ablaufumgebung, eben nicht für EJBs sondern für Servlets. Solche Ablaufumgebungen stehen meist nicht alleine da. Daneben kann es noch einen eigenständigen Transaktionsmanager geben für die Realisierung verteilter Transaktionen, und vielleicht noch einen Scheduler oder Message-Broker.
Spring ist KEINE Technologie für Verteilung, obwohl es ein Feature namens "Spring Remoting" mitbringt. Spring ist auch KEINE Ablaufumgebung, obwohl es einfache Implementierungen z.B. für Messaging mitbringt. Spring kann drei Dinge gut, die man lange im sonstigen JEE Umfeld vernachlässigt hat:
- Spring beherrscht sehr gut Dependency Injection, also das Konfigurieren von Objektnetzen beim Start des Systems.
- Spring integriert sehr gut AOP.
- Spring vereinfacht die Verwendung vieler JEE APIs und bringt für viele Enterprise Java Probleme sehr gute Hilfsmittel mit, die man sonst schmerzlich vermisst.
In den letzten Jahren hatte ich den Eindruck, dass in manchen Projekten ohne genaueres Nachdenken EJBs verwendet wurden, obwohl es keine Indikation für Verteilung gab. Im Moment sehe ich auch kommen, dass wir ähnliches anders herum bei der Kombination von Tomcat+Spring erleben, obwohl es manchmal starke Argumente für Verteilung gäbe. Da bleibt abzuwarten, ob es der Branche diesmal besser gelingt, rechtzeitig die richtigen Fragen zu stellen. Durch SOA-nahe Technologien wie Web Services hat EJB bei der Realisierung von Verteilung Konkurrenz bekommen. Man neigt heute m.E. eher dazu, kleine autarke Web Service Anwendungen (die bei HTTP Transport natürlich auch im Tomcat laufen) zu bauen, und die Integration der Einzelsysteme über XML-Nachrichten zu realisieren.
Die Gretchenfrage ist aber, ob man Verteilung braucht.
Die Verteilung nach fachlichen Gesichtspunkten ist sinnvoll, wenn man SEHR GROSSE Systeme auf Basis von EJBs bauen will. Da wir dann meist ganze Systemlandschaften betrachten müssen und dort bestehende ERP-Systeme oder Hosts vorfinden, die im Zweifel leichter XML als EJB "sprechen lernen", sollte man hier eher über eine SOA, die auf Nicht-Java-Standards wie XML, SOAP und WSDL basiert, nachdenken als über EJB.
Betrachten wir also die Verteilung aus technischen Gesichtspunkten innerhalb des Systems. Wenn wir einen Rich-Client (Swing, RCP) anbinden müssen, muss der mit dem zentralen Teil der Anwendung kommunizieren, dafür würde sich klassisch RMI anbieten, und das wiederum kann ein EJB-Container sehr gut. Das System ist dann natürlicherweise auf Client und Server "verteilt" und daher passt EJB als Technologie vorbildlich.
Wenn wir einen gehörigen Anteil an Fremdsystem-Integration über Messaging (JMS) oder mittels besonderer Adapter (JCA) benötigen, dann bietet uns hier ein EJB-Container auch eine gute Unterstützung. Hier haben wir bisher noch nichts über Verteilung gesagt. Wenn aber zusätzlich noch eine Web-Präsentation dazukommt, wird's interessant. Für das Messaging benötigt man nun z.B. eine JMS-fähige Ablaufumgebung und für das Web eine Servlet-fähige Ablaufumgebung, das können eigentlich nicht dieselben sein... und schon reden wir über Verteilung.
Verteilung kommt leider nicht umsonst daher, die Einführung des Command-Pattern oder alternativ einer SessionFacade mit Transferobjekten sind der Preis für zusätzliche Flexibilität.
zu 2.)
Tomcat ist eine Servlet-Ablaufumgebung, d.h. damit lassen sich HTTP Requests
abarbeiten. Dabei erledigt Tomcat das Multithreading bei parallel eintreffenden
Requests, indem jedem Request ein Thread zugeordnet wird.
Spring erzeugt beim Start im Tomcat ein Objektnetz von Singletons (z.B.
BusinessServices, DAOs), die sich untereinander über Objektreferenzen kennen.
Die parallelen Threads verwenden nun exakt die GLEICHEN Singletoninstanzen. Die
Threads würden sich bei schreibender Verwendung von Klassen- oder
Instanzvariablen überschneiden, was zu fehlerhaftem Verhalten führen würde. Nur
Methoden- und Thread-lokale Variablen sind sauber voneinander getrennt.
Ein EJB-Container verarbeitet klassischerweise RMI Requests, kann aber
inzwischen je nach Hersteller z.B. auch SOAP oder proprietäre Remoteprotokolle
unterstützen. Ein EJB-Container poolt die Request-verarbeitenden Objekte (EJBs)
und weist einem Request in einem eigenen Thread exklusiv eine Instanz zu, d.h.
bei Parallelverarbeitung können verschiedene Threads auch Instanzvariablen
nutzen. Der Programmierer hat stärker die Illusion, "allein auf der Welt" zu
sein.
Verwendet man Spring in einem EJB-Container, so kann man entsprechend dieser
Isolation hinter jeder Instanz einer Session Bean ein eigenes Objektnetz
instantiieren lassen. Ein einziges gemeinsames Objektnetz ist -- meines Wissens
nach -- technisch möglich, aber aufgrund des Bruchs im Programmiermodell nicht
ratsam.
Neben dem Unterschied bei Parallelverarbeitung gibt es noch einen beachtenswerten bei der Transaktionsgrenze. Der EJB-Container bildet sie nämlich automatisch, d.h. Beginn und Ende jedes Methodenaufrufs "von außen" an eine SessionBean startet bzw. beendet eine Transaktion. Bei Spring hingegen kann man diese Grenze mittels AOP sehr flexibel auf beliebige Methoden legen. Das ist vor allem bei Webanwendungen praktisch, in denen die technische Transaktion mit dem Request durch den Benutzer zusammenfällt. Außerdem vermeidet man durch das Zusammenlegen von Persistenz und Präsentation in einer JVM das Problem, das sich aus dem Lazyloading von ORM wie Hibernate ergibt: spätes Nachladen zwecks Seitenaufbau nach dem Abschluss des Remote-Aufrufs. Durch das Command-Pattern kann man sich allerdings auch bei JVM-Trennung von Web- und Persistenz-Schicht in beiden Fällen helfen, was aber natürlich nicht ganz umsonst ist.
Von daher ist bei der Entscheidung zwischen Servlet- und EJB-Container viel stärker ausschlaggebend, ob "nur" eine einfache Webanwendung vorliegt, oder man über einen eigenständigen mit Fremdsystemen integrierbaren Fachkern nachdenken muss. Letzteres ist flexibler, aber eben auch aufwändiger herzustellen. Spring setze ich gerne unabhängig von dieser Frage ein, da es neben den "großen" Features auch viele angenehme kleine Hilfsmittel bietet und sich modular verwenden lässt.
Soweit meine hoffentlich verständlichen Antworten auf zwei häufig gestellte Fragen.