Betrifft: Java Entwicklung leicht(er) gemacht - Einführung in das Spring Framework Autor: Guido Schmutz Art der Info: Whitepaper (Juni 2005) Quelle: Aus unserem TechnoCircle und TTC (Trivadis Technology Center) Einführung J2EE bietet dem Java Entwickler ein sehr mächtiges Framework für die Entwicklung von unternehmensweiten Applikationen. Allerdings können Teile von J2EE sehr aufwendig sein, insbesondere dann, wenn mit Enterprise JavaBeans (EJB) gearbeitet wird. Aus diesem Grund haben in letzter Zeit die so genannten Lightweight Container eine immer stärkere werdende Aufmerksamkeit gefunden. Diese versuchen die Limitierungen und Probleme von EJB mit einem leichtgewichtigeren Ansatz zu beheben. Wir haben die Lightweight Container und die Vorteile, die sie bieten in einem getrennten Artikel behandelt [1]. Das Spring Framework wurde mit dem Ziel entwickelt, komplexe Enterprise Architekturen leichter testbar und wartbarer zu machen. Spring agiert als organisierende Schicht (Glue) zwischen den verwendeten Komponenten, wobei die Komponenten selbst nicht speziell für Spring geschrieben sein müssen. Spring liegt als Komponentenmodell die JavaBeans-Spezifikation zugrunde. Spring kann auf allen gängigen Applikationsservern oder Webcontainern sowie auch in einem J2SE-Umfeld (also ohne J2EE Container) eingesetzt werden, ist frei, Open Source und extrem flexibel. Dieser Artikel präsentiert eine Übersicht über wesentliche Bestandteile von Spring, zeigt welche Art Dependency Injection mit Spring verwendet werden kann und wie Spring die JDBC Programmierung vereinfacht. Die Geschichte von Spring Spring hat bereits eine relativ grosse Vergangenheit. Das erste Mal wurde der Code, allerdings noch nicht unter dem Namen Spring, im Buch „Expert One-on-One J2EE Design und Development“ von Rod Johnson [3] publiziert. Er hat darin sein interface21 Framework präsentiert, das er für die Verwendung in seinen eigenen Projekten entwickelt hat. Im Februar 2003 ist daraus das Open Source Projekt Spring entstanden. Seit Januar 2003 wird Spring auf SourceForge gehostet und es sind zurzeit ca. 20 aktive Entwickler mit der Weiterentwicklung von Spring beschäftigt, wobei sich die führenden Contributors hauptamtlich der Entwicklung und dem Support von Spring widmen. Was ist Spring? Typischerweise wird Spring als „Lightweight Framework für das Erstellen von Java Applikationen“ beschrieben. In dieser Beschreibung stecken 2 wichtige Aussagen 1.) Anders als andere Frameworks wie z.B. Apache Struts ist Spring nicht einfach nur auf die Unterstützung von Web Applikationen limitiert. Das Springframework kann für alle Arten von denkbaren Java-Applikationen wertbringend genutzt werden. 2.) der „Lightweight“ Teil in der Beschreibung bezieht sich nicht auf den Umfang von Klassen und der Grösse der Distribution, sondern umschreibt mehr die Prinzipien der mit Spring umgesetzten Philosophie – was man auch mit dem Begriff, minimale Auswirkungen auf den Applikationscode ausdrücken kann. Spring ist leichtgewichtig im Sinn, dass man nur wenige, wenn überhaupt, Anpassungen an existierendem Code vornehmen muss, um die Vorteile des Spring Kerns nutzen zu können. Injecting Dependencies Der Kern des Spring Frameworks basiert auf dem Prinzip von Inversion of Control (IoC) bzw. Dependency Injection (DI) [1]. Springs Implementierung von Dependency Injection basiert auf zwei grundlegenden Java Konzepten, der JavaBeans Spezifikation und Interfaces. Wenn man Dependency Injection verwendet, dann lässt man es zu, dass die Konfiguration der Abhängigkeiten ausserhalb des Codes stattfindet. JavaBeans bieten einen Standardmechanismus für das Erstellen von konfigurierbaren Java Ressourcen an. Tatsächlich wird auch jede, durch Spring verwaltete Ressource bean genannt. Interfaces und Dependency Injection sind Technologien die sich gegenseitig perfekt ergänzen. Die Komplexität des Assoziationsgeflechtes einer entkoppelten Applikationsarchitektur, die basierend auf Interfaces entworfen wurde, ist allerdings ziemlich hoch und bedeutet zusätzlichen Aufwand für den Entwickler. Mittels Dependency Injection lässt sich die Code-Menge, die traditionellerweise für die Verwendung eines Interface-basierten Designs zusätzlich notwendig ist, nahezu auf Null reduzieren und führt zu einer noch loseren Kopplung der einzelnen Systemkomponenten. Im Kontext von DI agiert Spring als ein Container und stellt fertig konfigurierte Instanzen der Applikationsklassen mit all ihren Abhängigkeiten der Applikation zur Verfügung. Architekturvorteile mit Spring Bevor wir das Spring Framework genauer betrachten, soll hier in einem kurzen Überblick dargestellt werden, welche Vorteile sich in einem Projekt mit dem Einsatz von Spring aus Architektur-Sicht ergeben können: • Spring hilft bei der Organisation der Objekte im Middle Tier, egal ob man mit oder ohne EJBs arbeitet. Wie es der Name Framework („Rahmenwerk“) eigentlich treffend beschreibt, gibt Spring dem Entwickler einen Rahmen vor, innerhalb dessen er sich bewegen kann. Es ist eine sinnvolle Einschränkung der Flexibilität, im Sinne der Befolgung von Best Practices. • Applikationen, die mit Spring entworfen werden, sind sehr einfach Unit-Tests zu unterziehen. • Spring kann den Einsatz von EJBs zu einer Implementierungswahl machen, anstatt eines bestimmenden Faktors der Applikations-Architektur. Man kann Business Interfaces als normale Java-Klassen (Plain Old Java Objects, POJO) oder lokale EJBs implementieren, ohne den aufrufenden Code ändern zu müssen • Spring kann viele Probleme lösen, für die EJBs vorgesehen waren und stellt so eine Alternative zu EJB dar, die für viele Applikationstypen geeignet ist. Z.B. verwendet Spring die Mechanismen von AOP (Aspekt Orientierte Programmierung) um deklaratives Transaktions-Management ohne den Einsatz eines EJB Containers anzubieten, sogar ohne eine JTA Implementierung, falls man mit nur einer einzelnen Datenquelle arbeitet. • Spring bietet ein konsistentes Framework für den Datenzugriff an, egal ob man JDBC oder aber ein O/R Mapping Produkt wie Hibernate, TopLink oder JDO einsetzt. Ziele des Spring Frameworks Das Hauptziel von Spring ist es J2EE Entwicklung einfacher zu machen und gute Programmierpraktiken zu fördern. Dies wird durch die Unterstützung von POJOs als Programmiermodell erreicht, wodurch eine Vielzahl von unterschiedlichen Umgebungen unterstützt wird. Spring erfindet das Rad nicht neu. Es kann als so genanntes Meta-Framework gesehen werden. Wann immer ein anderes Framework oder Produkt eine Aufgabe bereits zufrieden stellend gelöst hat, wird versucht, dieses in Spring auf eine konsistente Art zu integrieren. Daher findet man in Spring z.B. keine Logging Funktionalität, keine Connection-Pools und keinen verteilten Transaktions-Koordinator. Alle diese Dinge werden von diversen Open Source Projekten bereits zur Verfügung gestellt (z.B. Commons Logging, Commons DBCP) oder aber vom Applikation Server selbst. Aus dem gleichen Grund enthält Spring keinen O/R Mapping Layer. Es gibt auch hier bereits genügend gute Lösungen auf dem Markt (TopLink, Hibernate und JDO), um dieses Problem zu lösen. Spring verfolgt damit die simple Idee, existierende Technologie einfacher nutzbar zu machen. Spring konkurriert nicht direkt mit bereits auf dem Markt bestehenden, anderen Open Source Projekten, ausser dort, wo Spring etwas grundsätzlich Neues zu bringen sucht. Zum Beispiel waren die Entwickler von Spring nie besonders glücklich mit dem Struts Framework und haben daher ein eigenes MVC Framework entwickelt und dieses ist ebenfalls Bestandteil der Spring Distribution. Zum Glück ist dies jedoch die Ausnahme. In der Kernkompetenz von Spring, dem Lightweight IoC Container und dem AOP Framework konkurriert Spring explizit mit anderen Open Source Projekten (siehe auch Abschnitt verfügbare Lightweight Container Implementierungen [1]), Spring war in vielen Bereichen hier jedoch ganz klar Pioneer. Spring ist portabel über Applikations-Server hinweg. Natürlich ist das Sicherstellen der Portabilität eine Herausforderung, Spring ist es aber bis heute gelungen, auf plattform-spezifische und nicht-standardisierte Dinge aus Entwicklersicht zu verzichten, und erlaubt daher die Verwendung von Spring innerhalb aller bekannten Applikations-Server, wie z.B. Bea WebLogic, Tomcat, Resin, JBoss, WebSphere, Oracle Application Server. Spring Module Spring enthält eine Vielzahl von Funktionalitäten und Features, die wohlgeordnet in sieben verschiedenen Modulen untergebracht sind, wie dies das folgende Diagramm zeigt: Abbildung 1: Modulübersicht (Quelle Spring Referenz) Alle 7 Module zusammen als Ganzes stellen alle Funktionalitäten zur Verfügung, die für die Entwicklung von unternehmensweiten Applikationen notwendig sind. Es ist jedoch nicht zwingend, die Applikation voll auf dem Spring Framework zu basieren, sondern man ist frei in der Wahl der Module und kann diejenigen aussuchen, die den meisten Nutzen bringen und den Rest ignorieren. Wie gut zu sehen ist, basieren sämtliche Module auf dem Spring Core Modul, was nichts anderes ist, als der Spring Container. Der Container definiert, wie Beans erstellt, konfiguriert und verwaltet werden, d.h. das A und O von Spring. Spring Core Module Spring’s Kern stellt den Container zur Verfügung, der die grundlegenden Funktionalitäten des Spring Frameworks anbietet. In diesem Modul ist die BeanFactory zu finden, das Herz jeder Spring-basierten Applikation. Eine BeanFactory ist die Implementierung des Factory Patterns, die IoC dazu verwendet, die Applikations-Konfiguration und die Spezifikation von Abhängigkeiten vom Code zu trennen. Spring (Application) Context Modul Die BeanFactory aus dem Core Modul macht aus Spring einen Container, das Context Modul enthält das, was Spring zu einem Framework macht. Dieses Modul erweitert das Konzept der BeanFactory und fügt Unterstützung für Internationalisierung (I18N), Life-cycle Ereignisse und Validierung hinzu. Zudem stellt dieses Modul auch viele Enterprise Services wie E-Mail, JNDI Zugriff, EJB Integration, Remoting und Scheduling zur Verfügung. Spring’s AOP Modul Spring stellt mit dem AOP Modul einen reichen Support für aspekt-orientierte Programmierung zur Verfügung. Dieses Modul dient dabei als Basis für die Entwicklung von eigenen Aspekten für Spring-basierte Applikationen. Um eine möglichst gute Interoperabilität zwischen Spring und anderen AOPFrameworks zu gewährleisten, basiert der grösste Teil des Spring AOP Supports auf dem von der AOP Alliance definierten API. Das Spring AOP Modul führt auch Metadaten-Programmierung in Spring ein. Mit der Hilfe von Spring’s Metadata Support können Annotations zum Source-Code hinzugefügt werden, die Spring anweisen, welche Aspekte wann und wo appliziert werden sollen. Spring’s DAO Modul Die Arbeit mit JDBC endet oft mit sehr viel technischem Code, um DatenbankVerbindungen aufzubauen, Statements zu erstellen, Resultat-Sets zu verarbeiten und Fehler zu behandeln. Das Spring DAO Modul mit der JDBC Abstraktion versucht diesen Code aus dem Applikationscode zu entfernen und in die Framework Klassen zu verlagern. Damit wird der Applikations-Code wesentlich klarer, da er sich auf abwendungs-spezifische Dinge konzentrieren kann und die technischen Dinge dem Framework überlassen kann. Zudem werden Probleme, wie fälschlicherweise nicht geschlossene Datenbank-Verbindungen vermieden. Zudem verwendet dieses Modul das Spring AOP Modul um TransaktionsManagement Dienste für die Objekte einer Spring Applikation anzubieten. Spring’s ORM Modul Für diejenigen, die ein objekt-relationales Mapping (ORM) Tool der direkten JDBC Programmierung vorziehen, stellt Spring mit dem ORM Modul eine Integration der populärsten ORM Frameworks zur Verfügung. Spring versucht hier also nicht eine eigene ORM Lösung zu erstellen, sondern bietet Hilfe für die Integration von Hibernate, TopLink, JDO und iBATIS SQL Maps an. Die Spring TransaktionsManagement Unterstützung funktioniert dabei für die ORM Frameworks genauso wie für JDBC. Spring Web Modul Das Web Modul baut auf dem Context Modul auf und stellt einen Context zur Verfügung, der auch innerhalb einer web-basierten Applikation einfach verwendet werden kann. Zudem enthält dieses Modul eine Integration von Jakarta Struts mit Spring. Spring Web MVC Modul Spring enthält ein vollständiges MVC Framework für die Erstellung von WebApplikationen. Dieses Modul ist eigentlich der einzige Bereich, wo Spring eine Konkurrenz-Situation zu bereits bestehenden Frameworks, wie z.B. Struts schafft. Die Macher von Spring argumentieren hier, das ihr MVC mit der Integration von IoC wesentlich flexibler ist, als die bestehenden Frameworks, und daher seine Berechtigung hat. Im Folgenden gehen wir genauer auf die beiden Module Core und DAO ein und zeigen, wie die Dependency Injection in Spring genutzt werden kann und welche Vorteile das DAO Modul bei der Programmierung mit JDBC bietet. Der Spring Container und Dependency Injection Wie bereits erwähnt ist der Container der Kern des Spring Frameworks. Spring’s Container verwendet Inversion of Control für die Verwaltung der Komponenten die eine Applikation ausmachen. Dies beinhaltet das Erstellen der Beziehungen zwischen zusammenwirkenden Komponenten. Es gibt keinen einzelnen Spring Container. Spring hat aktuell zwei verschiedene Arten von Container. Bean Factories (definiert durch das BeanFactory Interface) sind die einfachsten Container und stellen die grundlegende Unterstützung von Dependency Injection zur Verfügung. Application Contexts (definiert durch das ApplicationContext Interface) bauen auf der Idee der Bean Factory auf in dem sie erweiterte Applikations-Framework Services zur Verfügung stellen. Spring selbst liefert bereits verschiedene Implementierungen von BeanFactory und ApplicationContext. Arbeiten mit dem ApplicationContext Wegen den zusätzlichen Funktionalitäten, die ein ApplicationContext liefert, ist er für alle Applikationen gegenüber der BeanFactory zu bevorzugen. Eine BeanFactory kann jedoch dann interessant werden, wenn Ressourcen beschränkt sind, z.B. auf einem Mobile Device. Spring Benutzer konfigurieren ihre Applikationen normalerweise über XML BeanDefinition-Files. Unter den vielen Implementierungen von ApplicationContext werden drei üblicherweise genutzt: • ClassPathXmlApplicationContext – lädt den Context aus einem XML File, das über den Classpath lokalisiert wird. • FileSystemXmlApplicationContext – lädt den Context aus einem XML File im Filesystem. • XmlWebApplicationContext – lädt den Context aus einem XML File, das innerhalb der Web-Applikation enthalten ist. Um ein Application Context vom Filesystem zu laden, kann folgendes Statement verwendet werden ApplicationContext context = new FileSystemXmlApplicationContext(“c:/foo.xml”); Egal welche Container-Art genutzt wird, man muss dem Spring Framework mitteilen, welche Beans die Applikation verwenden soll und wie diese in Beziehung stehen. Dieses Zusammenstellen der Beans innerhalb des Spring Containers wird auch „wiring“ genannt, was nichts anderes bedeutet, als dem Container mitzuteilen, welche Klassen hinter den Beans stehen und wie der Container Dependency Injection verwenden soll, um diese zu verbinden. Betrachten wir nun ein einfaches Beispiel einer Konfiguration von Beans über XML. Es soll ein Service-Objekt implementiert werden, das ein DAO (Data Access Objekt) verwendet an welches es den Datenzugriff delegiert (Abbildung 1Abbildung 2). Die aktuelle Implementierung des UserDAO ist momentan unwichtig (wir werden in einem späteren Abschnitt sehen, wie DAOs mit Spring implementiert werden können.). Beachtenswert ist jedoch die Implementierung der Klasse GreetingServiceImpl. Sie stellt zwei grundsätzliche Strategien zur Verfügung, wie ihr eine Implementierung vom UserDAO übergeben werden kann, entweder über den Constructor oder aber über die setUserDAO() Methode (Listing 1). D.h. eine UserDAO Implementierung kann sowohl über Constructor Injection wie auch über Setter Injection „eingespritzt“ werden. Public class GreetingServiceImpl implements GreetingService { private UserDao userDao; public GreetingServiceImpl(UserDao dao) { this.userDao = dao; } public void setUserDao(UserDao dao) { this.userDao = dao; } ... } Listing 1: Die Implementierung des GreetingService Interface <bean id=“greetingService” class=“com.trivadis.lwc.ch03.GreetingServiceImpl”> <property name=“userDao”> <ref bean=“userDao”/> </property> </bean> <bean id=“userDao” class=“com.trivadis.lwc.ch03.UserDaoImpl”/> ... Listing 2: Der Application Context mit dem "wiring" der Beans über das XML-File Abbildung 2: Abhängigkeit zwischen Service und DAO Das XML File in Listing 2 zeigt, wie das „Wiring“ zwischen der GreetingServiceImpl und dem UserDAOImpl definiert werden muss. Man sieht es ist ein relativ einfaches XML File. In diesem Beispiel wird ferner gezeigt, wie eine Abhängigkeit in Spring über Setter Injection aufgelöst werden kann. Um nun aus einer Applikation auf ein Bean im Container zuzugreifen, z.B. auf den greetingService, reicht es, den Container wie oben gezeigt aufzubauen und auf dem Context-Objekt die get() Methode anzuwenden (Listing 3) ApplicationContext context = new FileSystemXmlApplicationContext(“c:/foo.xml”); GreetingService service = (GreetingService)context.get(“greetingService”); Listing 3: Zugriff auf ein Bean aus dem Applikations-Code Damit erhält man eine vollständige Instanz vom Greeting-Service mit allen Abhängigkeiten (DAO) aufgelöst und gesetzt. Datenzugriff mit dem Spring Framework Im vorherigen Abschnitt haben wir gesehen, wie einfach man in Spring Komponenten mittels Dependency Injection deklarativ zu Applikationen verknüpfen kann. Die meisten Applikationen werden jedoch früher oder später auch Daten persistent machen wollen und diese in einer (zumeist relationalen) Datenquelle abspeichern wollen. Dabei ist es wichtig, dass die damit notwendige Datenzugriffs-Schicht gut entworfen und implementiert wird, um so flexibel und so performant wie möglich die Anforderungen zu erfüllen. Die beste Datenbank wird wenig nutzen, wenn hier Fehler gemacht werden. Als Best Practice hat sich der Einsatz des Data Access Object (DAO) Pattern durchgesetzt, um die Implementierung des Datenzugriffs von der Business-Logik zu trennen. Das Beispiel von oben (Abbildung 2: Abhängigkeit) hat den Einsatz eines DAO-Objekts bereits gezeigt. Standard JDBC JDBC stellt einen standardisierten Weg für den Datenzugriff aus Java Applikationen zur Verfügung. Das JDBC Framework ist recht komplex und mit dieser Komplexität steigen natürlich die Schwierigkeiten bei der Entwicklung. Ein Problem von JDBC ist, dass sichergestellt werden muss, dass der Code die Verbindungen zur Datenbank (Connections) richtig verwaltet. Eine Connection ist eine knappe Ressource und es ist relativ teuer, eine neue zu erstellen. Zudem ist die Anzahl gleichzeitig offener Connections im Normalfall limitiert, zu viele offene Verbindungen können die Datenbank langsam machen. In diesem Abschnitt werden wir zeigen, wie Spring dem Entwickler hilft, diese Komplexität in den Griff zu bekommen. Bevor wir dies jedoch zeigen können, möchten wir kurz auf die pure JDBC Programmierung eingehen und auf einige der Probleme hinweisen. Damit werden dann die Vorteile von Spring offensichtlich. public class UserDaoImpl implements UserDao { /* Returns a list of users looking something like this: * [{“name”=“Bob”, “id”=1}, {“name”=“James”, “id”=2}] */ public List getUserList() { List resultList = new ArrayList(); DataSource ds = getDataSource(); // JNDI Lookup Connection con = null; try { con = ds.getConnection(); Statement st = con.createStatement(); ResultSet rs = st.executeQuery("select * from usertable"); while (rs.next()){ Map aRecord = new HashMap(); aRecord.put("id", new Integer(rs.getInt("id"))); aRecord.put("name", rs.getString("name")); resultList.add(aRecord); } } catch (SQLException e) { // handle exception, rethrow something meaningful } finally{ if (con != null){ try { con.close(); } catch (SQLException e){ // handle exception, rethrow something meaningful } } return resultList; } Listing 4: Standard JDBC Implementation In Listing 4 ist ein eigentlich relativ einfaches Statement abgebildet, das mittels JDBC einen SELECT von einer Tabelle implementiert. Was dabei auffällt (im Code fett markiert), ist das umfangreiche Exception-Handling und die Tatsache, dass man sich als Entwickler um die Datenbank-Verbindung selbst kümmern muss. Dies ist alles Code, den man immer wieder in den DAO-Klassen wiederholen muss oder aber in eine Helper-Klasse auslagert. Dennoch ist dieser Code äusserst wichtig, ein kleiner Fehler und schon werden Verbindungen zu der Datenbank nicht mehr in jedem Fall geschlossen und es entstehen so genannte „Connection Leaks“, ein viel gesehene Fehlerquelle in der Praxis. Spring DAO Framework Dies ist genau der Bereich, wo das DAO Framework von Spring aufsetzt. Das Framework eliminiert diesen immer wiederkehrenden, technischen Code und ermöglicht es dem Entwickler, sich auf den effektiven applikations-spezifischen Code zu konzentrieren, was im oben gezeigten Beispiel Dinge sind wie das SQLStatement und die Abarbeitung des Resultatsets und das Bereitstellen des ReturnWertes. Spring JDBC Abstraktion Das gleiche Statement über die Spring JDBC Abstraktion gelöst sieht folgendermassen aus: public class UserDaoImpl extends JdbcDaoSupport implements UserDao { ... /* * Returns a list of users looking something like this: * [{“name”=“Bob”, “id”=1}, {“name”=“James”, “id”=2}] */ public List getUserList() { return getJdbcTemplate().queryForList(“select * from usertable”); } ... } Listing 5: Implementierung des DAO mit Verwendung des Spring DAO Frameworks Wir sehen, dass wir mit der DAO-Klasse das gleiche business-spezifische Interface implementieren, dieses Mal aber zusätzlich noch von der Spring Framework Klasse JdbcDaoSupport ableiten. Spring stellt zwei verschiedene Ebenen von JDBC Abstraktion zur Verfügung. Die erste, untere Ebene basiert auf einem Callback Ansatz, um die Kontrolle – und dadurch das Exception- und Connection-Handling – aus dem Applikationscode ins Framework zu verlagern. Dies ist eine andere Art von Inversion of Control und ist eine Verwendung des Template Method Patterns. Spring verwendet ähnliche Callback Ansätze für viele andere API’s die spezielle Schritte für das Erlangen und Freigeben von Ressourcen einschliessen, wie z.B. JDO, Transaktions-Management und JNDI. Spring Klassen die diese Callbacks ausführen werden Templates genannt. In unserem Beispiel von oben wird ein JdbcTemplate Objekt verwendet, um ein SQL Query abzusetzen und das Resultat in einer ArrayList abzuspeichern. Ohne weitere Angaben werden die einzelnen Rows automatisch in eine HashMap abgespeichert und diese wird dann als Element der Liste hinzugefügt. Damit entspricht das Ergebnis exakt dem ursprünglichen JDBC-Standardlösung (Listing 4). Über eine zusätzliche mapRow Callback Methode und der Verwendung von query() anstelle von queryForList(), kann dieses Verhalten jedoch einfach für jede Row im ResultatSet übersteuert werden (Listing 6). public class UserDaoImpl extends JdbcDaoSupport implements UserDao { ... /* * Returns a list of users looking something like this: * [{“name”=“Bob”, “id”=1}, {“name”=“James”, “id”=2}] */ public List getUserList() { return getJdbcTemplate().query(“select * from usertable”, new RowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException { // map rs to a custom defined bean } }}; Listing 6: Zusätzliche mapRow Callback Methode Das JdbcTemplate stellt viele verschiedene Methoden zur Verfügung, um unterschiedliche Szenarien zu unterstützen, inklusive Prepared Statement und Batch Updates. Ganz einfache Aufgaben, wie das Ausführen von SQL Funktionen können in einem Einzeiler ohne Callback gelöst werden. public int getNumberOfEmps() { return count = getJdbcTemplate().queryForInt(“SELECT COUNT() FROM emp”); } Listing 7: Aufruf einer SQL Funktion Die höhere Ebene der JDBC Abstraktion baut auf der Kernfunktionalität der JDBC Callbacks auf, stellt aber zusätzlich ein API zur Verfügung, bei dem eine RDBMS Operation – egal ob Query, Insert, Update oder Stored Procedure – als Java Objekte modelliert wird. Ein Beispiel einer solchen Klasse ist im Listing 8 zu sehen. public class UserDAO extends JdbcDaoSupport implements UserDao { ... private class UserQuery extends MappingSqlQuery { public UserQuery (DataSource ds) { super(ds, "SELECT id, name FROM usertable WHERE id = ?"); super.declareParameter(new SqlParameter("id", Types.INTEGER)); compile(); } public Object mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId((Integer)rs.getObject("id")); user.setName(rs.getString("name")); return user; } } // returns a User object public User getUser(Integer id) { UserQuery userQuery = new UserQuery(getDataSource()); Object[] parms = new Object[1]; parms[0] = id; List users = userQuery.execute(parms); return (User) users.get(0); } ... Listing 8: Ein Query Objekt, das User Objekte zurückgibt Diese Klasse kann auf folgende Art verwendet werden User user = userDAO.getUser(25); Die eigentliche Verarbeitung findet dabei in der Inner-Class UserQuery statt, mit der Definition des SQL-Statements und dem Abarbeiten jeder Row in der entsprechenden mapRow Callback-Methode. Sie werden sich nun aber möglicherweise schon lange fragen, wo und wie denn das Connection-Handling gemacht wird. Durch das Vererben der DAO Klassen von der JdbcDaoSupport Basis-Klasse erhält jedes DAO automatisch eine setDataSource() Setter-Methode. Diese muss nun über Konfiguration im Spring Context entsprechend gesetzt (injected) werden (Listing 9). Das Beispiel zeigt hier die Konfiguration für das UserDAO aus Listing 8. <beans> <bean id=“dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>jdbc/myds</value> </property> </bean> <bean id=”userDAO” class=" com.trivadis.lwc.ch04.dao.jbdc.UserDaoImpl"> <property name=”dataSource” <ref bean=”dataSource”/> </property> </bean> </beans> Listing 9: Konfiguration der JNDI DataSource im Spring Context Mit der konfigurierten DataSource arbeitet Spring dann im Hintergrund, um jeweils eine Connection zu lösen, bzw. diese zum richtigen Zeitpunkt wieder freizugeben. Im oben gezeigten Konfigurationsbeispiel wird eine über JNDI zugreifbare DataSource angesprochen (über JndiObjectFactoryBean). Dies bedeutet, dass diese Konfiguration nur innerhalb eines Applikations-Server lauffähig ist, d.h. dieses Setup wahrscheinlich in der Produktion bzw. Test verwendet wird. Soll jedoch während der Entwicklung der Code lokal in J2SE-Umfeld getestet werden können, dann werden wir die DataSource wahrscheinlich nicht über JNDI ansprechen wollen. In diesem Fall reicht es, den Spring Context entsprechend umzukonfigurieren (Listing 10). <beans> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>oracle.jdbc.OracleDriver</value> </property> <property name="url"> <value>jdbc:oracle:thin:@localhost:1521:ORCL</value> </property> <property name="username"> <value>scott</value> </property> <property name="password"> <value>tiger</value> </property> </bean> <bean id=”userDAO” class=" com.trivadis.lwc.ch04.dao.jdbc.UserDaoImpl"> <property name=”dataSource” <ref bean=”dataSource”/> </property> </bean> </beans> Listing 10: Konfiguration einer lokalen DataSource über DriverMangerDataSource Da wir diese Konfiguration nur in der Entwicklungsumgebung verwenden, ist es nicht weiter schlimm, dass die Datenbank-Properties (Passwort) hart-codiert sind und dass kein Connection-Pooling verwendet wird. Mit dem von Spring angebotenen JDBC Support, wird JDBC wieder zu einer echten Alternative verglichen zu den O/R-Mapping Frameworks. Insbesondere dann, wenn man auf die volle Aussagekraft von SQL angewiesen ist, und datenbank-spezifische Features über SQL ausschöpfen will, kommt man auch heute noch um SQL bzw. JDBC nicht herum. Ausserdem ist bekannt, dass die O/R-Mapping Frameworks insbesondere dann Probleme bekommen, wenn eine grosse Menge von Informationen, die aus verschiedenen Domänen-Objekten stammen, zurückgeben werden müssen. Der Workaround dafür, auch Fast-Lane Reader Pattern genannt, führt über SQL und damit über den Einsatz von JDBC. Spring O/R Mapping Framework Integration Neben dem Support für JDBC; wie hier gezeigt, enthält Spring auch Support für weitere Persistenz-Technologien, wie TopLink, Hibernate, JDO oder iBATIS. Dies ist jeweils in ähnlicher Art und Wiese gelöst wie bei JDBC, über die Definition von spezifischen Template-Klassen mit entsprechenden Callback-Methoden. Insbesondere das iBATIS Framework ist eine interessante Alternative zu JDBC. Es implementiert keine O/R Mapping, sondern führt das Konzept einer SQL Map ein, bei der das Mapping zwischen SQL Queries und Input- und Output-Parameter beschrieben wird. Wir werden die Integration von iBATIS in Spring in einem zukünftigen Artikel zeigen. Fazit Dieser Artikel sollte zeigen, was unter dem Spring Framework zu verstehen ist, welche Konzepte zugrunde liegen und was für Vorteile es dem Java-Entwickler bietet. Die Spring Community ist wahrscheinlich eine der Besten, die wir bis jetzt bei Open Source Projekten gesehen haben. Die Mailing-Listen und die Foren sind immer aktiv und der Fortschritt bei neuen Features ist normalerweise rasch. Das Entwicklungsteam hat sich dem Ziel verschrieben, aus Spring das erfolgreichste Java Applikations-Framework zu machen, und das zeigt sich in der Qualität des Codes, der produziert wird. Trivadis hat sich für das Spring Framework entschieden, da es zur Zeit den grössten Funktionalitätsumfang aufweist und besonders für datenbanknahe Applikationen mit der hervorragenden Integration von unterschiedlichen Persistenz-Technologien und einem deklarativen Transaktions-Management gute Unterstützung bietet. Wir setzen Spring seit ca. 2 Jahren erfolgreich zur Applikationsentwicklung ein. Lesen Sie hierzu auch den Erfahrungsbericht zum Einsatz von Spring in einem Kundenprojekt [2]. Interessant ist, dass sich andere Open Source Produkte (wie z.B. Mule [9] oder Active MQ [10]) Spring annähern und den Spring Container für die Konfiguration ihrer Komponenten einsetzen. Erwähnenswert sind weiter die sogenannten Spring-Subprojekte. Hier gilt es insbesondere das Spring Security (Acegi) Projekt zu beachten, ein leichtgewichtiges Security Framework, das sich hervorragend mit Spring kombinieren lässt. Wir werden in Zukunft mit weiteren Artikeln spezifische Features aus dem Spring Framework beschreiben. Ressourcen [1] Schmutz Guido, Abnehmen leicht gemacht – Lightweight Container auf dem Vormarsch, http://www.trivadis.com [2] Stern Nicolas, A first experience with the Spring Framework, http://www.trivadis.com [3] Johnson Rod, Expert One-on-One J2EE Design and Development, Wrox, ISBN 07645-4385-7 [4] Johnson Rod and Hoeller Juergen, Expert One-on-One J2EE Development without EJB, Wrox, ISBN 0-7645-5831-5 [5] Johnson Rod, Introducing the Spring Framework, http://www.theserverside.com/articles/article.tss?l=SpringFramework [6] Harrop Rob and Machacek Jan, Pro Spring, Apress, ISBN 1-59059-461-4 [7] Walls Craig and Breidenbach Ryan, Spring in Action, Manning, ISBN 1-93239435-4 [8] Spring Framework 1.2 reference manual, http://www.springframework.org/documentation [9] Mule, http://mule.codehaus.org [10] Active MQ, http://activemq.codehaus.org/