Ein erster Blick auf MVC (JSR 371)

Was lange währt: Das neue MVC Framework in Java EE

Thilo Frotscher

© iStockphoto.com / RyanJLane

Mit Java EE 8 erhält die Plattform ein neues Webframework. Im Unterschied zu den JavaServer Faces (JSF) wird dieses nicht komponenten-, sondern actionbasiert sein. Eine Entscheidung, die durchaus kontrovers diskutiert wird. Während manche Entwickler eine jahrelange Hoffnung endlich erfüllt sehen, halten andere diesen Schritt für unnötig. Dieser Artikel bietet einen Einstieg in die Thematik, skizziert Gründe für die Entscheidung und wirft einen ersten Blick auf die neue Spezifikation, die stark an JAX-RS angelehnt ist.

Das Model View Controller Pattern (MVC) ist den allermeisten Entwicklern sicherlich bestens bekannt. Das Pattern hat sich unter anderem als bewährtes Mittel zur Implementierung von Webanwendungen in der Breite durchgesetzt. Neben einer Vielzahl von Java-Webframeworks setzen auch zahlreiche Frameworks anderer Programmiersprachen auf diesen Ansatz.

Für Java ist über die Jahre eine große Vielzahl an MVC-basierten Webframeworks entstanden. Bereits vor zehn Jahren stellte es eine gewisse Herausforderung dar, zu Beginn eines neuen Projekts ein geeignetes Webframework auszuwählen. Zu groß war schon damals die Fülle der Alternativen, um eine gesicherte Aussage treffen zu können, welches Framework die Projektanforderungen am besten erfüllen würde. So entstanden Kriterienkataloge, Artikel und Präsentationen, die dabei helfen sollten, die Frameworks miteinander zu vergleichen und ihre Vor- und Nachteile gegeneinander abzuwägen.

Bei aller Vielfalt haben sich gleichzeitig zwei unterschiedliche Ansätze für die Umsetzung des MVC-Patterns etabliert, in die sich die meisten Java-Webframeworks einteilen lassen. Diese werden gemeinhin als actionbasierte (oder Request-basierte) und komponentenbasierte Webframeworks bezeichnet. Indem man sich zunächst für einen dieser beiden Ansätze entscheidet, kann man die Menge der zur Wahl stehenden Frameworks immerhin schon einmal deutlich reduzieren. Nun muss man sich nur noch innerhalb der gewählten Kategorie entscheiden. Zu den bekanntesten Beispielen der actionbasierten Frameworks zählen Struts und Spring MVC. Populäre Vertreter des komponentenbasierten Ansatzes sind beispielsweise JavaServer Faces und Wicket.

Die beiden Ansätze unterscheiden sich ganz wesentlich. Bei actionbasierten Frameworks dreht sich für den Entwickler alles um eingehende Requests, die vom Framework an so genannte Actions geleitet werden. Eine Action behandelt den Request, aktualisiert gegebenenfalls das Modell und leitet schließlich an eine resultierende View weiter. Die Parameter des Requests müssen manuell verarbeitet werden, und Entwickler sind auch für die Erstellung von HTML, CSS und JavaScript selbst verantwortlich. Weiterhin gibt es typischerweise keine automatische Validierung und keine automatische Umwandlung von Eingabedaten. Je nach verwendetem Framework können Einzelheiten natürlich abweichen.

Komponentenbasierte Frameworks fokussieren sich dagegen auf eine komplette Webseite, die aus mehreren Komponenten besteht. Die Komponenten sind für HTML und JavaScript verantwortlich, der Entwickler einer Seite muss sich in der Regel nicht explizit darum kümmern. Vielmehr besteht seine Aufgabe schwerpunktmäßig darin, die Seite aus mehreren bestehenden Komponenten zusammenzusetzen und diese mit dem zugrunde liegenden Modell zu verknüpfen. Eingabedaten werden automatisch umgewandelt und validiert. Mit diesem Ansatz können Oberflächen mit etwas Übung sicherlich schneller erstellt werden. Er eignet sich jedoch nur dann, wenn kein direkter Zugriff auf HTTP-Requests und -Responses benötigt oder gewünscht wird, oder wenn mehr Kontrolle über HTML, CSS und JavaScript erforderlich ist.

Die Programmier- und Gedankenmodelle von actionbasierten und komponentenbasierten Frameworks sind also sehr unterschiedlich. Viele Entwickler sind überzeugte Anhänger des einen oder des anderen Ansatzes. Entwickler, die beide Ansätze mögen, trifft man dagegen eher selten an. Doch obwohl die Java-Welt über die Jahre gerade im Bereich der actionbasierten Frameworks einige besonders populäre Vertreter hervorgebracht hat, enthält der Java-EE-Standard mit JSF lediglich Unterstützung für den komponentenbasierten Ansatz. Nun soll in Java EE 8 schließlich doch auch ein actionbasiertes Webframework hinzukommen.

Kontroverse Entscheidung

Wie bereits angedeutet, wird diese Entscheidung durchaus kontrovers diskutiert. Es stellt sich zunächst die Frage des Zeitpunkts: Warum wird im Java-EE-Standard ausgerechnet jetzt ein actionbasiertes Webframework benötigt? Ist Version 8 nicht ein bisschen spät? Hätte ein solches Framework nicht schon vor etlichen Jahren standardisiert werden müssen? Wird es jetzt überhaupt noch benötigt?

Maßgeblich verantwortlich für diese Entwicklung war letztlich aber die Community selbst. Sie konnte sich zu Beginn des Jahres 2014 in einer Umfrage dazu äußern, welche neuen Features für Java EE 8 am meisten gewünscht werden. Es beteiligten sich mehrere tausend Entwickler, die Ergebnisse der Umfrage sind online einsehbar. Über 60 Prozent der Entwickler forderten für Java EE ein actionbasiertes Webframework als zusätzliche Alternative zu JSF.

Dieses Ergebnis war für viele Beobachter wenig überraschend. Schon seit längerer Zeit wurde immer wieder Kritik daran laut, dass ein so populärer Standard wie Java EE keine Unterstützung für actionbasierte Webanwendungen bietet. Aus heutiger Sicht hätte man vielleicht erwarten können, dass ein actionbasiertes Framework bereits vor langer Zeit in den Standard eingeflossen wäre. Andererseits stand jederzeit eine ganze Palette alternativer Frameworks zur Verfügung, die zu diesem Zweck verwendet werden konnten. So war Apache Struts für viele Jahre praktisch gesetzt, wenn Webanwendungen mit Java entwickelt werden sollten. Es gab somit also vielleicht nicht die ganz große Notwendigkeit, tatsächlich ein actionbasiertes Framework zu standardisieren. Dennoch wurden die Rufe nach einem solchen Framework seit Java EE 7 wieder vernehmlich lauter.

Ein zweites Umfrageergebnis überraschte schon eher. So waren nur 26 Prozent der Entwickler der Meinung, dass es einen existierenden De-facto-Standard für actionbasierte Webframeworks gebe, an dem sich ein etwaiger neuer Java-EE-Standard orientieren könnte. Sicherlich hätte man annehmen können, dass ein größerer Anteil der Befragten einen existierenden De-facto-Standard in diesem Bereich erkennt. Denn in der Fülle der verfügbaren actionbasierten Frameworks hatten sich in den vergangenen Jahren durchaus einige Kandidaten mit besonders hoher Popularität herauskristallisiert.

In einer weiteren Umfrage wurde die Community in der Folge gebeten, die zuvor am häufigsten gewünschten Features für Java EE 8 zu priorisieren. Dabei belegte das neue Webframework immerhin Platz 5 von 13. Folgerichtig wurden die Arbeiten zur Spezifikation eines solchen Frameworks auf den Weg gebracht.

In der Folge waren indes viele Entwickler besorgt, was denn nun aus JSF werden würde. Ed Burns, Specification Lead für JSF, äußerte sich daraufhin ausführlich in einem Artikel zu diesem Thema. Darin erklärte er, weshalb die Aufnahme eines actionbasierten Frameworks in Java EE durchaus Sinn ergebe und welche Bedeutung dies für JSF haben werde. Insbesondere machte er deutlich, dass JSF weiterhin ein wichtiger Bestandteil von Java EE bleiben wird. Zwischenzeitlich war sogar in Erwägung gezogen worden, den actionbasierten Ansatz in Form einer alternativen Lifecycle-Implementierung in JSF zu integrieren. Von dieser Idee nahm man jedoch zugunsten eines eigenständigen JSRs Abstand.

Für alle Fans von JSF bleibt festzuhalten, dass die Hinzunahme des neuen Webframeworks keinesfalls das Ende von JSF bedeuten wird. Vielmehr erhalten Entwickler damit eine neue Alternative, um Webanwendungen mit Java EE zu implementieren. Neben dem komponentenbasierten JSF steht künftig also zusätzlich ein actionbasiertes Framework zur Auswahl. Somit wird es möglich, auch actionbasierte Webanwendungen ausschließlich auf Basis des Java-EE-Standards zu entwickeln, das heißt ohne den Einsatz eines „fremden“ Frameworks. Es gibt durchaus nicht wenige Unternehmen mit der Richtlinie, bevorzugt immer eine Standardtechnologie einzusetzen.

Eine letzte Kontroverse betrifft den Namen des neuen Frameworks. Es soll schlicht „MVC“ heißen. Doch auch JSF ist ein MVC-Framework, nur eben ein komponentenbasiertes. Wenn diesem nun ein actionbasiertes MVC-Framework zur Seite gestellt wird, hätte man aus Sicht vieler Entwickler sicherlich einen besseren Namen finden können. Das neue Framework „MVC“ zu nennen, wird vorhersehbar zu zahlreichen Missverständnissen führen.

Überblick

Die Spezifikation von MVC 1.0 wird im JSR 371 als Teil von Java EE 8 erarbeitet. Dieser Prozess ist in vollem Gange. Zum Redaktionsschluss für diese Ausgabe stand die Veröffentlichung des so genannten „Early Draft“ kurz bevor. Höchste Zeit also, einen ersten Blick auf den aktuellen Stand zu werfen und sich einen Überblick zu verschaffen, wie das neue Framework aussehen wird. Natürlich sollte dabei stets bedacht werden, dass die offizielle Verabschiedung der Spezifikation erst für das kommende Jahr geplant ist und dass sich daher jederzeit noch Änderungen ergeben können.

Die namhaft besetzte Expert Group hatte zunächst eine strittige Entscheidung zu treffen. Sie betraf die Frage, ob MVC auf dem Servlet-API oder auf JAX-RS basieren sollte. Nach einer knapp ausgefallenen Abstimmung fiel die Entscheidung schließlich auf JAX-RS. Die Controllerkomponente des MVC-Patterns wird somit von JAX-RS-Ressourcen übernommen. Das Modell basiert (vorzugsweise) auf CDI-Beans. Für die View-Komponente können prinzipiell unterschiedliche Technologien zum Einsatz kommen. Diese werden mithilfe so genannter View Engines in MVC integriert.

Controller

Die wesentliche Aufgabe eines Controllers ist die Zusammenführung des Datenmodells mit einer View (oder einem Template), um Seiten einer Webanwendung zu erzeugen. Im neuen MVC-Framework ist der Controller dabei eine JAX-RS-Ressourcenmethode, die mit @Controller markiert wurde. Falls eine komplette JAX-RS-Ressourcenklasse mit dieser Annotation versehen wurde, dann werden sämtliche Methoden der Klasse als Controller betrachtet.

Der Rückgabetyp einer Controllermethode ist beschränkt auf die Typen void, String, Viewable und Response. In Listing 1 liefert die Controllermethode etwa einen String zurück. In diesem Fall interpretiert MVC den Rückgabewert als Pfad zu einer View, beispielsweise zu einer JSP.

Listing 1
@Path("hello")
public class HelloController {
  
  @GET
  @Controller
  public String hello() {
    return "hello.jsp";
  }
}

Viewable ist ein Klasse, welche zusätzliche Informationen über eine View kapseln kann. So kann Viewable Referenzen zu Modellen oder zu einer View Engine enthalten, die für das Rendern der View verantwortlich sein soll. Tabelle 1 zeigt eine Übersicht der erlaubten Rückgabetypen von Controllermethoden.

Als Media Type der Response einer Controllermethode wird in MVC standardmäßig text/html angenommen. Davon abweichende Controller können ihren Media Type wie in JAX-RS gewohnt mittels der Annotation @Produces anzeigen. Auch bezüglich der Parameter von Controllermethoden funktioniert alles genauso, wie aus JAX-RS gewohnt. Anders als beim Rückgabetyp gibt es hier keinerlei Einschränkungen durch MVC.

Tabelle 1: Übersicht über erlaubte Rückgabetypen und Controllermethoden

Tabelle 1: Übersicht über erlaubte Rückgabetypen und Controllermethoden (Anklicken zum Vergrößern)

Für Entwickler, die bereits Erfahrung mit JAX-RS gesammelt haben, sollte der Einstieg in MVC somit vergleichsweise leicht gelingen. Einige Besonderheiten gilt es dennoch zu beachten. So müssen MVC-Controller zwingend CDI-managed sein. Es ist also beispielsweise nicht möglich, eine JAX-RS-Ressource zu verwenden, die als EJB implementiert wurde.

Modelle

Für die Umsetzung des Modells unterstützt MVC 1.0 zwei verschiedene Ansätze. Der erste Ansatz basiert auf CDI @Named Beans und wird von der Spezifikation mit Nachdruck empfohlen. Voraussetzung hierfür ist allerdings, dass CDI Named Beans von der verwendeten ViewEngine unterstützt werden. Dies ist beispielsweise bei JSP und Facelets der Fall. Das Beispiel in Listing 2 zeigt die Modellklasse Greeting, welche mit @Named markiert wurde.

Listing 2
@Named("greeting")
@RequestScoped
public class Greeting {
  
  private String message;

  public String getMessage() { return message; }
  public void setMessage(String message) { this.message = message; }
  ...
}

Instanzen dieser Modellklasse können in MVC-Controller auf bekannte Weise injiziert und mit Daten befüllt werden (Listing 3).

Listing 3
@Path("hello")
public class HelloController {

  @Inject
  private Greeting greeting;

  @GET
  @Controller
  public String hello() {
    greeting.setMessage("Hello there!");
    return "hello.jsp";
  }
}

Innerhalb der View kann dann auf das Modell über den Namen zugegriffen werden, welcher dem Modell mittels @Named zugewiesen wurde.

Für solche Fälle, bei denen eine View Engine eingesetzt werden soll, die CDI Named Beans nicht unterstützt, oder wenn diese aus einem anderen Grund nicht verwendet werden sollen, unterstützt MVC 1.0 alternativ noch einen zweiten Ansatz für die Umsetzung der Modelle. Er basiert auf einer Klasse namens Models. Dabei handelt es sich im Wesentlichen um eine Map, in die Instanzen von Modellklassen abgelegt werden können. Bei der Ablage in die Map wird der gewünschte Name des Modellobjekts als Schlüssel verwendet. Dieser Schlüssel hat somit die gleiche Funktion wie die @Named-Annotation (Listing 4).

Listing 4
@Path("hello")
public class HelloController {

  @Inject
  private Models models;

  @GET
  @Controller
  public String hello() {
    models.put("greeting", new Greeting("Hello there!");
    return "hello.jsp";
  }
}

Views

Views (oder Templates) definieren die Struktur einer Seite der Webanwendung und können Referenzen zu einem oder mehreren Modellen besitzen. Einer so genannten View Engine kommt dabei die Aufgabe zu, die von der View benötigten Daten aus den Modellen zu extrahieren, an den entsprechenden Stellen in die View einzufügen und somit die resultierende Seite zu produzieren.

MVC 1.0 kann prinzipiell beliebige Technologien für die Views unterstützen. Voraussetzung ist lediglich, dass eine entsprechende View Engine bereitgestellt wird. Standardmäßig werden zunächst JSP und Facelets unterstützt. Aber auch die Verwendung anderer Technologien wie beispielsweise Freemarker wäre möglich. Es ist anzunehmen, dass schon bald MVC View Engines für alternative Templating-Technologien erhältlich sein werden. Diese werden dann mithilfe eines CDI-basierten Erweiterungsmechanismus eingebunden.

Das Beispiel in Listing 5 zeigt eine JSP, die eine Property aus der Modellklasse verwendet. Der Name des Modells wurde entweder mittels @Named oder als Schlüssel in Models spezifiziert, je nachdem, welcher der obigen Controller zum Einsatz kam.

Listing 5
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Hello</title>
  </head>
  <body>
    <h1>${greeting.message}</h1>
  </body>
</html>

In Facelets könnte der Zugriff auf die gleiche Property beispielsweise so aussehen:

<h:outputLabel value="#{greeting.message}"/>

Für den Fall, dass zur Laufzeit mehrere View Engines verfügbar sind, die eine View rendern könnten, besteht die Möglichkeit, den View Engines mithilfe von @Priority eine von mehreren vordefinierten Prioritäten zuzuweisen. Die MVC-Implementierung wird die zu verwendende View Engine dann anhand dieser Prioritäten auswählen.

Validierung und Fehlerbehandlung

Auch der Mechanismus zur Behandlung von Exceptions in MVC basiert stark auf den zugrunde liegenden Mechanismen aus JAX-RS. Das REST-API bietet Entwicklern die Möglichkeit, so genannte Exception Mapper zu implementieren und mit der Anwendung zu registrieren. Exception Mapper erlauben es, auftretende Exceptions an zentraler Stelle zu behandeln und in eine entsprechende Response umzuwandeln, die dann an den Client gesendet wird. Falls bei der Verarbeitung eines eingegangenen Requests also eine Exception auftritt, prüft das JAX-RS-Framework, ob für den jeweiligen Exception-Typ ein entsprechender Exception Mapper registriert wurde, und leitet die Exception dort hin. Falls nicht, kommt ein Default-Mechanismus für die Exception-Behandlung zum Einsatz.

Auf dieser Basis können beispielsweise auch Exception Mapper für Validierungsfehler erstellt werden, also für eine auftretende ConstraintViolationException. JAX-RS verwendet Bean Validation für die Prüfung von Request-Parametern. Und erneut wird dieser Mechanismus von MVC wieder verwendet. Allerdings wird es eine Erweiterung geben, welche Entwickler insbesondere bei der Validierung von HTML-Formulardaten unterstützen soll. Denn bei der Entwicklung von Webanwendungen mit Benutzereingabe würde sich das Problem ergeben, dass aus allen Formularfeldern der Anwendung sämtliche Validierungsfehler zum gleichen Exception Mapper geleitet werden. Eine einzige Methode müsste dann gegebenenfalls die Validierungsfehler hunderter Eingabefelder behandeln. Dies würde den Code zur Fehlerbehandlung recht unübersichtlich machen.

MVC bietet daher einen alternativen Mechanismus für die Behandlung von Validierungsfehlern. Anstatt jede auftretende ConstraintViolationException an eine zentrale Stelle zu leiten, können Controllermethoden die Fehlerbehandlung selbst übernehmen. Das bedeutet, dass die Controllermethode vom Framework aufgerufen wird, obwohl die Validierung der Eingangsdaten fehlgeschlagen ist. Um anzuzeigen, dass dieses Verhalten gewünscht ist, muss die Controllerklasse ein Feld vom Typ ValidationResult enthalten, das zusätzlich mit @Inject versehen ist. In dieses werden die Validierungsergebnisse dann vor Aufruf der Controllermethode injiziert:

@Inject
 private ValidationResult vr;

Die Controllermethode sollte dann als erste Handlung dieses ValidationResult prüfen und eine fehlgeschlagene Validierung entsprechend behandeln, wie in Listing 6 dargestellt.

Listing 6
@POST
@ValidateOnExecution(type = ExecutableType.NONE)
public Response formPost(@Valid @BeanParam FormDataBean form) {
  if (vr.isFailed()) {
    // handle error
    return Response.status(BAD_REQUEST).entity("error.jsp").build();
  }
  return Response.status(OK).entity("data.jsp").build();
}

Framework-Events

Ein interessantes Feature sind spezielle CDI-Events, die von der eingesetzten MVC-Implementierung während der Verarbeitung von Requests gefeuert werden. MVC-Anwendungen können sich als Observer für diese Events registrieren und werden somit darüber informiert, wenn das entsprechende Ereignis eintritt. Als Einsatzzweck für diesen Mechanismus nennt die Spezifikation beispielsweise Logging oder Performance Monitoring. Eine Unterstützung der folgenden Events ist für MVC-Frameworks verpflichtend, das heißt MVC-Anwendungen können sich darauf verlassen, dass diese auf jeden Fall gefeuert werden:

  • ControllerMatched: Enthält Informationen über die Controllermethode, der ein eingegangener Request zugewiesen wurde.
  • ViewEngineSelected: Enthält Informationen über die View Engine, die für das Rendern der View ausgewählt wurde.

Darüber hinaus steht es einzelnen MVC-Implementierungen frei, weitere Events zu definieren oder die verpflichtenden Events mit zusätzlichen Informationen anzureichern.

MVC-Anwendungen

Entwickler, die bereits praktische Erfahrung mit JAX-RS gesammelt haben, kennen die Klasse Application sowie den Begriff der JAX-RS-Anwendung. Da MVC auf JAX-RS basiert, gibt es auch hier sehr viele Gemeinsamkeiten. Eine MVC-Anwendung besteht aus einer oder mehreren JAX-RS-Ressourcen, die mit @Controller markiert sind, sowie einer beliebigen Menge von Providern. Sind keine Ressourcen mit @Controller markiert, dann handelt es sich folglich auch nicht um eine MVC-Anwendung, sondern um eine reine JAX-RS-Anwendung. Natürlich kann es aber hybride Anwendungen geben, die eine Kombination aus MVC-Contollern und JAX-RS-Ressourcen enthalten. Genau wie in JAX-RS-Anwendungen werden auch in MVC-Anwendungen die Controller und Provider der Anwendung sowie der URL-Pfad, unter dem die MVC-Controller erreichbar sein sollen, mittels einer Subklasse von Application konfiguriert.

Zusammenfassung

Nach vielen Jahren der Entwicklung von Webanwendungen mit Java sowie unzähligen Frameworks und Projekten, die dabei auf einen actionbasierten Ansatz gesetzt haben, wird dieser Ansatz nun endlich auch im Java-EE-Standard unterstützt. Es mag unterschiedliche Ansichten über den Zeitpunkt geben, und auch der Name des neuen Frameworks ist in den Augen vieler Entwickler recht unglücklich gewählt. Doch letztlich ist die Entscheidung zu begrüßen. Durch die Hinzunahme von MVC in den Java-EE-Standard wird die Plattform noch kompletter. Unstrittig ist jedoch auch, dass ein Verzicht auf das neue Webframework sicherlich nicht den Untergang des Java-EE-Standards bedeutet hätte.

Nach momentanem Stand der Dinge werden wichtige Ziele dieses JSRs erreicht: MVC wird sehr stark auf existierenden Technologien beruhen (u. a. JAX-RS, CDI, Bean Validation, JSP, Facelets). Dadurch wird auch die Spezifikation recht schlank bleiben. Für Entwickler bedeutet dies, dass der Einstieg in MVC vergleichsweise leicht gelingen sollte. Anhänger von JSF müssen unterdessen keine Sorge haben. Der komponentenbasierte Ansatz wird auch weiterhin ein wichtiger Bestandteil von Java EE bleiben.

Die Referenzimplementierung von MVC trägt den Namen Ozark. Neben der von der Spezifikation geforderten Unterstützung von JSP und Facelets ist zusätzlicher Support für FreeMarker, Velocity, Thymeleaf, Mustache und Handlebars geplant. Sobald die Referenzimplementierung weit genug fortgeschritten ist, werden wir in einem nachfolgenden Artikel die Entwicklung von Webanwendungen auf Basis von MVC und Ozark genauer unter die Lupe nehmen.

Geschrieben von
Thilo Frotscher
Thilo Frotscher
Thilo Frotscher arbeitet als freiberuflicher Softwarearchitekt und Trainer. Seine technischen Schwerpunkte sind die Java-Plattform sowie der Themenbereich Services und Systemintegration. Er unterstützt Unternehmen durch die Mitarbeit in Entwicklungsprojekten und die Durchführung praxisnaher Schulungen.
Kommentare

Hinterlasse einen Kommentar

avatar
4000
  Subscribe  
Benachrichtige mich zu: