Einführung

Werbung
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/
Herunterladen