Ausarbeitung - Institut für Wirtschaftsinformatik

Werbung
Westfälische Wilhelms-Universität Münster
Ausarbeitung
Application Frameworks
im Rahmen des Seminars Software Engineering
Birgit Hecker
Themensteller: Prof. Dr. Herbert Kuchen
Betreuer: Christian Hermanns
Institut für Wirtschaftsinformatik
Praktische Informatik in der Wirtschaft
Inhaltsverzeichnis
1
Aufbau und Ziel der Arbeit........................................................................................ 1
2
Grundlagen ................................................................................................................ 2
3
2.1
Application Framework ..................................................................................... 2
2.2
Dependency Injection......................................................................................... 3
2.3
Aspektorientierte Programmierung .................................................................... 4
Spring Framework ..................................................................................................... 5
3.1
Beispielanwendung Bibliothek .......................................................................... 5
3.2
Spring-Container ................................................................................................ 6
3.3
Spring-Bean........................................................................................................ 7
3.4
Dependency Injection......................................................................................... 7
3.5
Aspektorientierte Programmierung .................................................................. 10
3.6
Model View Controller .................................................................................... 11
3.7
Datenbankzugriff.............................................................................................. 14
3.8
Sicherheit.......................................................................................................... 17
4
Vergleich Spring und Java EE ................................................................................. 21
5
Zusammenfassung und Ausblick ............................................................................. 22
Anhang A: Klassendiagramm ......................................................................................... 23
Anhang B: Quellcode...................................................................................................... 24
Literaturverzeichnis ........................................................................................................ 60
II
Kapitel 1: Aufbau und Ziel der Arbeit
1 Aufbau und Ziel der Arbeit
Die Nachfrage nach Geschäftsanwendungen im Java-Bereich steigt. Dabei werden die
Anwendungen immer komplexer. Folgen davon sind, dass eine einfache Verwendung
von Java ohne Bibliotheken nicht möglich ist. Eine Bibliothek stellt Funktionen zu
bestimmten Aufgabenstellungen bereit, in Form einer Sammlung von Klassen dieses
Aufgabengebietes. Die Einarbeitung in eine solche Anwendung ist für den Entwickler
sehr zeitintensiv. Einen Ausweg bieten Frameworks. Sie stellen eine Schicht über
verschiedene Bibliotheken dar und vereinfachen somit die Softwareentwicklung [Wo07,
Kap. 1.1]. Frameworks enthalten bereits Funktionalität, die in die eigentliche
Implementierung eingebunden werden kann. Im Gegensatz zur Bibliothek aus der der
Programmierer bei Bedarf einzelne Klassen verwendet, gibt das Framework die Struktur
für die Anwendung vor [GHJV04]. Der Programmierer erstellt und registriert Klassen
auf Grundlage der vorgegebenen Schnittstellen. So wird die konkrete Implementierung
durch das Framework gesteuert und benutzt. Es wird zwischen programmiersprachenund zweckabhängigen Frameworks differenziert. Beispiele für Frameworks sind
Java EE, .NET und Spring.
Für das Java-Framework Spring bildete das Buch „Expert One-On-One J2EE Design
und Development“ die Grundlage für das OpenSource-Projekt Spring. Durch die große
Resonanz und Beteiligung an dem OpenSource-Projekt wurde Spring stetig
weiterentwickelt.
Ziel dieser Arbeit ist die detaillierte Darstellung der Softwareentwicklung mit dem
Spring Framework. Dazu wird zunächst im 2. Kapitel der Begriff Framework näher
beleuchtet. Außerdem werden die Programmiertechniken Dependency Injection und
Aspektorientierte Programmierung beschrieben. Diese sind von großer Bedeutung für
das Spring Framework, welches in Kapitel 3 vorgestellt wird. Anhand von Beispielen
aus der Entwicklung einer Bibliotheksanwendung wird die Softwareentwicklung mit
diesem Framework erläutert. Kapitel 4 vergleicht schließlich Spring mit dem Java EE
Framework. Abschließend erfolgen in Kapitel 5 eine Zusammenfassung und ein
Ausblick.
1
Kapitel 2: Grundlagen
2 Grundlagen
2.1 Application Framework
In der Literatur finden sich verschiedene Definitionen für den Begriff Framework. Eine
Definition, die häufig verwendet wird, stammt von [GHJV04]:
„Ein Framework ist eine Menge von kooperierenden Klassen, die einen
wiederverwendbaren Entwurf für einen bestimmten Anwendungskontext
vorgeben. Das Framework bestimmt dabei die Architektur der Applikation.“
Ein
Framework
ist
somit
eine
wiederverwendbare
Umgebung
für
die
Softwareentwicklung. Für die objektorientierte Programmierung bedeutet dies, dass ein
bestimmtes Paket an Klassen zur Verfügung gestellt wird, um aus diesem
Softwareanwendungen erstellen zu können. Das Framework gibt durch die verfügbaren
Schnittstellen und abstrakte Klassen die grobe Anwendungsstruktur und den
Kontrollfluss der Anwendung vor. Mit Hilfe dieser zur Verfügung gestellten Funktionen
erstellt der Entwickler eine konkrete Anwendung. Da diese Funktionen nicht ohne
Berücksichtigung eines konkreten Anwendungskontextes erstellt werden können, sind
Frameworks meist domänenspezifisch. Um die Anwendungsarchitektur vorzugeben,
findet eine Umkehrung des Kontrollflusses (Inversion of Control) statt. Dabei werden in
der Erstellung einer Anwendung auf Basis eines Frameworks Klassen und Methoden
aus dem Framework aufgerufen. Dieses Prinzip wird auch als Hollywood-Prinzip
(„Don’t call us, we call you“) bezeichnet [GHJV04].
Ein Vorteil von Frameworks ist, dass sie bereits fertig entworfene und implementierte
Softwarestrukturen bereitstellen, die wieder verwendet werden können. So muss der
Entwickler z. B. Grundfunktionalitäten wie das Überprüfen von Benutzerrechten, die in
jeder Anwendung vorkommen, nicht jedesmal neu implementieren. Durch die
Kapselung von Implementierungsdetails wird die Anwendung modularer und die
Softwarequalität steigt. Ein weiterer Vorteil von Frameworks ist die Erweiterbarkeit.
Das Framework stellt dem Entwickler feste Punkte bereit an denen er intervenieren und
das Framework konfigurieren und erweitern kann [FaSc97].
Bei Frameworks differenzieren [FaSc97] zwischen Black- und White-Box-Frameworks.
Die Grundlage für White-Box-Frameworks bilden Vererbung und dynamisches Binden,
um Erweiterbarkeit zu gewährleisten. Die bereitgestellte Funktionalität wird
2
Kapitel 2: Grundlagen
wiederverwendet und kann durch Vererbung der bereitgestellten Klassen oder durch
Überschreiben von vordefinierten Methoden ergänzt werden. In Black-BoxFrameworks wird die Erweiterbarkeit durch Komposition in das Framework integriert,
d. h. die zur Verfügung stehenden Komponenten müssen vom Entwickler konfiguriert
und zusammengesetzt werden. Bei Black-Box-Framework kennt der Benutzer daher nur
die Schnittstelle und nicht die innere Struktur. Im Gegensatz dazu muss der Benutzer
des White-Box-Frameworks die innere Struktur des Frameworks genau kennen. Daher
sind Black-Box-Framework leichter zu verwenden und erweitern als White-BoxFrameworks. Allerdings sind Black-Box-Frameworks schwieriger zu entwickeln, da
hier Schnittstellen zur Verfügung gestellt werden müssen, die eine Reihe von
potentiellen Anwendungsfällen abdecken [FaSc97].
In dieser Ausarbeitung wird das Application Framework Spring vorgestellt. Dabei
handelt es sich um eine Mischform aus Black- und White-Box-Framework, da es
Vererbung und dynamisches Binden verwendet ebenso wie Komposition. Dazu werden
die
Programmiertechniken
Dependency
Injection
und
Aspektorientierte
Programmierung benutzen.
2.2 Dependency Injection
Dependency Injection ist eine Programmiertechnik, die in der objektorientierten
Programmierung eingesetzt wird, um die Abhängigkeiten zwischen Objekten zu
minimieren. Solche Abhängigkeiten entstehen z. B. durch den Verweis eines Objekt auf
ein anderes Objekt. Ohne die Anwendung von Dependency Injection ist jedes Objekt
selbst für die Erzeugung und Verwaltung seiner Abhängigkeiten zuständig. Für die
Erzeugung anderer Objekte muss das abhängige Objekt Getter- und Setter-Methoden
für das zu erstellende Objekt zur Verfügung stellen. Bei Dependency Injection werden
diese Abhängigkeiten verringert, indem die Verantwortung für die abhänigen Objekten
an das Framework übergeben wird. Die Abhängigkeiten werden zur Laufzeit den
Objekten injiziert. Dies wird meistens deklarativ in XML-Dateien konfiguriert.
Außerdem lassen sich Klassen so einfacher testen, da ihre abhängigen Objekte durch
Mock-Objekte ersetzt werden können. Mock-Objekte dienen als Platzhalter für echte
Objekte innerhalb von Tests [Wo07].
Es gibt drei Varianten von Dependency Injection: Interface-Injection, Setter-Injection
und Constructor-Injection. Hierbei entsteht die größte Abhängigkeit bei Interface3
Kapitel 2: Grundlagen
Injection. Bei Interface-Injection muss eine Klasse alle Interfaces und deren
vorgegebene Methoden implementieren damit alle abhängigen Objekte zugeordnet
werden können. Es muss also für jede Art der Abhängigkeit eine Schnittstelle definiert
werden. Um für Setter-Injection Abhängigkeiten zur Laufzeit erzeugen zu können, muss
der Client die entsprechende Setter-Methode zum Setzen des benötigen Objektes
anbieten. Bei Constructor-Injection werden sämtliche Abhängigkeiten einer Klasse über
den Konstruktor übergeben. Damit kann ein Objekt der Klasse nur erzeugt werden,
wenn alle Abhängigkeiten bei der Erstellung übergeben werden [Fo04].
2.3 Aspektorientierte Programmierung
Aspektorientierte Programmierung (AOP) ist ein Ansatz, um Codewiederholungen zu
vermeiden. Dabei werden die sich ggf. wiederholenden Codestücke ausgelagert. Dies
entspricht dem Prinzip „Separation of Concerns“. Das bedeutet der Basisbestandteil
einer Methode wird von speziell zweckgebundenen und sich wiederholenden Teilen wie
dem Überprüfen von Benutzerrechten getrennt. Die ausgelagerten Teile werden in der
AOP Aspekte bezeichnet. Ein Aspekt kann z. B. für Logging, Transaktionen oder
Sicherheit zuständig sein. Durch AOP entfällt die Duplizierung von Code. Er wird an
einer zentralen Stelle implementiert und ist dadurch wiederverwendbar. Deshalb werden
Aspekte auch als Querschnittsbelange (Cross Cutting Concerns) bezeichnet. Ein
Beispiel hierfür ist die Sicherheit, für die in jeder Methode der Benutzer und seine
Rechte überprüft werden. Mit AOP ist es möglich bei einem Methodenaufruf
einzugreifen und zusätzlichen Code auszuführen. Die zentrale Speicherung des
Quellcodes erhöht die Verständlichkeit und Wartbarkeit des Codes [Wo07].
Im Umfeld von AOP werden neben dem Begriff Aspekt noch weitere Termini
verwendet, die im Weiteren näher erläutert werden. Ein Advice beschreibt die Aufgabe
eines Aspektes. Außerdem gibt der Advice an, wann die Aufgabe ausgeführt wird, z. B.
vor dem Methodenaufruf. Stellen, an denen in der Anwendung ein Aspekt eingebunden
werden kann, werden als Joinpoint bezeichnet. Ein Joinpoint kann z. B. vor einem
Methodenaufruf oder bei einem geänderten Feldwert auftreten. Pointcuts bilden eine
Teilmenge der Joinpoints. Sie schränken die Aufrufe von Aspekten ein, indem sie
definieren wo der Advice ausgeführt wird. Dies kann z. B. über reguläre Ausdrücke
erfolgen. Ein Aspekt ist in diesem Zusammenhang die Vereinigung von Advice und
Pointcut. Es ist bekannt welcher Code wo und wann ausgeführt wird [Wa08] .
4
Kapitel 3: Spring Framework
3 Spring Framework
Bei Spring handelt es sich um ein OpenSource-Framework, welches insbesondere die
Programmierung von Java-EE-Anwendungen erleichtert. Grundlage für das Spring
Framework bildete das Buch "Expert One-On-One J2EE Design and Development“. In
diesem wurden die Programmiertechniken Dependency Injection und AOP beschrieben,
die die wichtigsten Merkmale in Spring darstellen. Ziel der Entwicklung von Spring war
es, die Erstellung von Unternehmensanwendungen zu vereinfachen. Die Basis für jede
Spring-Anwendung bilden Anwendungsobjekte, sogenannte Spring-Beans, und der
Spring-Container, über den die Beans konfiguriert und über den kompletten
Lebenszyklus verwaltet werden. Diese werden in den beiden folgenden Abschnitten
näher beschrieben. Insgesamt setzt sich das Spring Framework aus sechs Modulen
zusammen. Das Kernmodul stellt den Anwendungscontainer (Spring-Container) zur
Verfügung, der die Grundlage für den Gebrauch von Dependency Injection bildet. Je
nach Bedarf können weitere Spring Module an das Kernmodul angebunden werden, so
dass die Nutzung von z. B. Hibernate ohne Probleme möglich ist. In den einzelnen
Abschnitten dieses Kapitels werden außerdem noch die Module DAO und ORM, die für
den Datenbankzugriff notwendig sind und die Module AOP und MVC erläutert. Die
Beschreibung von Spring und dessen Elementen erfolgt anhand eines Beispiels für die
Implementierung einer Bibliotheksanwendung.
3.1 Beispielanwendung Bibliothek
Diese Beispielanwendung besteht aus Benutzern, die Exemplare von Büchern ausleihen
können. Ein Klassendiagramm hierzu befindet sich im Anhang A. Es zeigt, dass Bücher
eine 1:N-Beziehung zu Exemplaren haben, d. h. es kann mehrere Exemplare von einem
Buch geben. Außerdem sind einem Benutzer mehrere Ausleihen zugeordnet. In diesem
werden das Ausleihdatum, der Benutzer und das ausgeliehene Exemplar gespeichert.
Ein Exemplar kann einer Ausleihe zugeordnet sein. Aus diesem Klassendiagramm wird
mit Hilfe des Modules MVC eine Webanwendung erstellt. Mit dieser ist es möglich sich
nach erfolgreichem Login alle Bücher sowie die aktuellen Ausleihen zu dem
angemeldeten Benutzer anzeigen zu lassen. Der Administrator der Anwendung kann die
Benutzer der Anwendung einsehen. In dieser Arbeit wird zunächst in Abschnitt 3.4
Dependency Injection anhand der Klasse User erklärt. Diese wird in Abschnitt 3.7 um
5
Kapitel 3: Spring Framework
einen Datenbankzugriff erweitert. Die gesamten Daten der Anwendung sind in einer
MySQL Datenbank gespeichert und werden über JDBC in die Anwendung integriert. In
Abschnitt 3.5 wird AOP am Beispiel vom Logging-Aspekt erläutert. Das Module MVC
wird in Abschnitt 3.6 anhand der Webseite zur Anzeige aller Bücher näher beschrieben.
Damit sichergestellt ist, dass der authentifizierte Benutzer auch die erforderlichen
Rechte besitzt um bestimmte Services in Anspruch zu nehmen, wird in Abschnitt 3.8
vorgestellt wie ein Sicherheitsaspekt mit Hilfe es Moduls Spring Security angelegt wird.
3.2 Spring-Container
Die Basis für jede Spring-Anwendung bilden Spring-Beans und der Spring-Container.
Der Container erstellt, verbindet, konfiguriert und verwaltet alle Anwendungsobjekte,
die Spring-Beans, über deren kompletten Lebenszyklus. Dazu wird Dependency
Injection eingesetzt. Es gibt zwei Typen von Spring-Containern. Der Unterschied beider
liegt im Leistungsumfang. Die BeanFactory ist eine Implementierung des FactoryEntwurfsmusters. Ihre Aufgabe ist es Spring-Beans zu erstellen und zu verteilen. Es
handelt sich dabei um eine Allzweck-Factory, die unterschiedliche Bean-Typen
zurückgibt. Die meistverwendete BeanFactory-Implementierung ist XmlBeanFactory.
Sie arbeitet nach dem Lazy-Loading-Prinzip, d. h. die Instanzierung der Spring-Bean
findet erst beim Aufruf der Bean durch die Methode getBean() statt. [Wa08, Kap. 2.1]
Der ApplicationContext ist mächtiger als die BeanFactory. Deshalb wird dieser in
komplexeren Anwendungen verwendet. Der Kontext umfasst neben den Funktionen der
BeanFactory Möglichkeiten zum Auslösen von Textnachrichten, Laden von
Dateiressourcen wie Bilder und Veröffentlichen von Ereignissen für registrierte SpringBeans. Im Gegensatz zur BeanFactory werden Singleton-Beans direkt instanziiert.
BeanFactory factory = new XmlBeanFactory
(new FileSystemResource("applicationContext.xml"));
User peter = (User) factory.getBean("U4711");
Listing 1: Erzeugung einer BeanFactory
Ein Spring-Container kann aus jeder normalen Java-Anwendung aufgerufen werden.
Der Aufruf beider Container-Typen ist ähnlich. Zunächst wird der Container erzeugt
und anschließend die Bean über die Methode getBean() des Containers aufgerufen.
Listing 1 zeigt die Erzeugung einer Spring-Bean über die BeanFactory. Dabei wird mit
Hilfe einer FileSystemResource ein Objekt der XmlBeanFactory erzeugt. Die BeanDefinition wird aus einer XML-Datei ausgelesen. Üblicherweise wird eine Datei für die
6
Kapitel 3: Spring Framework
Konfiguration einer Spring-Anwendung erstellt. Aus dieser wird im Beispiel ein UserBean, also der konkreter Benutzer Peter mit der ID U4711, über getBean() erzeugt
[Wa08, Kap. 2.1].
3.3 Spring-Bean
Spring-Beans stellen Java-Klassen dar, die mit Hilfe des Spring-Containers konfiguriert
werden. Für jedes Spring-Bean wird zunächst eine Java-Klasse mit setter- und getterMethoden für die Attribute bzw. einem Konstruktor erstellt. Für die Injizierung erfolgt
die Konfiguration über eine XML-Datei oder über Annotationen in der jeweiligen JavaKlasse. Bei XML-basierter Konfiguration wird eine Bean unter Verwendung des Tag
<bean> erzeugt. Der Name über den auf das Bean über den Spring-Container
zugegriffen werden kann, wird über das Attribut id übergeben. Im Attribut class wird
der vollständige Name der Java-Klasse, auf den sich das Bean bezieht, angegeben
[Wa08, Kap. 2.2].
In Listing 2 wird eine Bean der Klasse User erzeugt. Auf dieses kann über die ID
U4711 zugegriffen werden.
Geltungsbereiche für Beans
Jede Bean hat einen bestimmten Gültigkeitsbereich. Dieser wird über das Tag <scope>
definiert. Dabei wird zwischen Singleton, Prototype, Request, Session und GlobalSession differenziert. Standardmäßig sind alle Beans Singleton-Beans. Hierbei existiert
nur eine Bean pro Spring-Container. Alle Aufrufe einer konkreten Bean werden von
derselben Instanz durchgeführt. Mit Prototype ist eine mehrfache Instanziierung einer
Bean ist möglich. Bei jedem Aufruf einer Bean wird eine neue Instanz erzeugt. Die
Geltungsbereiche Request, Session und Global-Session finden nur im Rahmen von
webbasierten Anwendungen Einsatz (z. B. bei Spring MVC) [Wa08, Kap. 2.5.1].
3.4 Dependency Injection
In Spring können zwei Typen von Dependency Injection eingesetzt werden: SetterInjection und Konstruktor-Injection.
7
Kapitel 3: Spring Framework
Setter-Injection
Bei Setter-Injection werden die Attribute der Bean über das <property>-Tag gesetzt.
Dafür muss die Java-Klasse die entsprechenden Setter-Methoden aufweisen.
<bean id="U4711" class="bib.User">
<property name=”id”
value=”4711” />
<property name=”username” value=”Peter” />
<property name=”password” value=”1234” />
<property name="firstname" value="Peter" />
<property name="lastname" value="Muster" />
</bean>
Listing 2: Setter-Injection
Das Beispiel in Listing 2 zeigt, dass das <property>-Tag die Attribute name und value
besitzt. Mit name wird das Attribut der Bean-Klasse spezifiziert und mit value wird
diesem eine Ausprägung übergeben. Hier wird ein Benutzer mit der ID 4711 und dem
Namen Peter Muster erzeugt. Dies ist ein Beispiel ohne die Verwendung einer
Datenbank. Auf die Verwendung einer Datenbank wird in Kapitel 3.7 näher
eingegangen [Wa08, Kap. 2.3].
Constructor-Injection
Wenn die Injektion über einen Konstruktor erfolgen soll, passiert dies mit dem Tag
<constructor-arg>. Über das Attribut value wird der Wert für den ersten
Konstruktorwert übergeben. Durch Wiederholung des <constructor-arg>-Tags können
bei mehreren Parametern im Konstruktor diesem Werte übergeben werden [Wa08,
Kap. 2.2.2].
<bean id="U4711" class="bib.User">
<constructor-arg value="4711" />
<constructor-arg value=”Peter” />
<constructor-arg value=”1234” />
<constructor-arg value="Peter" />
<constructor-arg value="Muster" />
</bean>
Listing 3: Konstruktor-Injektion
In Listing 3 wird der gleiche Benutzer wie in Listing 2 erzeugt. In diesem Fall
allerdings durch Übergabe der Attributwerte an den Konstruktor.
Die Beispiele zeigen, dass dem Attribut value immer Strings übergeben werden. Intern
wird dieser String auf den entsprechenden Datentyp des Klassenattributes gecastet. Das
Attribut value kann nur für primitive Datentypen und deren entsprechende Wrapper-
8
Kapitel 3: Spring Framework
Klassen verwendet werden. Falls auf eine andere Bean referenziert werden soll, ist
hierfür das Attribut ref zu verwenden.
Collections verschachteln
Falls eine Bean eine Eigenschaft, die eine Liste von Ausprägungen wie eine Collection
besitzt, kann diese auf vier unterschiedliche Arten mit Spring konfiguriert werden. Die
Tags <list> und <set> verschachteln eine Liste von Werten, wobei nur bei <list>
doppelte Werte erlaubt sind. Die dritte Möglichkeit Mehrfacheigenschaften zu
konfigurieren ist über das <map>-Tag. Diese entspricht der Collection java.util.Map.
Das
<props>-Tag
stellt
die
vierte
Möglichkeit
zur
Konfiguration
von
Mehrfacheigenschaften dar und entspricht java.util.Properties [Wa08, Kap. 2.3].
<bean id="U4711" class="bib.User">
. . .
<property name="lendings">
<list>
<ref bean="ex3462">
<ref bean="ex4458">
<ref bean="ex5697">
</list>
</property>
</bean>
Listing 4: Injizierung von Collections
In Listing 4 wird über das Tag <list> eine Collection für das Attribut lending zum
Benutzer U4711 angelegt d. h. der Benutzer hat zur Zeit drei Exemplare ausgeliehen.
Autowiring
Statt alle Eigenschaften einer Bean über die Tags <property> oder <constructor-arg> zu
setzen, kann vor allem bei komplexen Anwendungen, die Verknüpfung der Beans
untereinander automatisch erfolgen. Dabei wird bei jedem Bean, das automatisch
verbunden werden soll, die Eigenschaft autowire gesetzt. In Spring gibt es vier Typen
von Autowiring. Bei Autowiring byName wird die Eigenschaft von Beans über den
Namen der Eigenschaft vervollständigt. Lautet der Name der Eigenschaft z. B. „book“
wird nach einem Bean mit der id „book“ gesucht. Die Vervollständigung kann auch
über den Typ der Eigenschaft, also dem Klassennamen z. B. class = bib.User, erfolgen.
Dies wird als Autowiring byType bezeichnet. Die dritte Variante von Autowiring ist
byConstructor. Dabei wird versucht eine oder mehrere Beans auf die Parameter des
Konstruktors abzubilden. Bei der vierten Variante, Autodect-Autowiring, wird
Autowiring erst byType und anschließend
byConstructor durchgeführt. Das
9
Kapitel 3: Spring Framework
automatische Verbinden aller Eigenschaften einer Bean, kann durch Angabe von
Eigenschaften über das Tag <property> überschrieben werden [Wa08, Kap. 2.4].
3.5 Aspektorientierte Programmierung
Vom Spring Framework werden in der AOP nur Joinpoints im Zusammenhang mit
einem Methodenaufruf unterstützt. Andere AOP Funktionalitäten können über die
Einbindung von der Java-AOP-Erweiterung AspectJ in das Spring Framework erzielt
werden. Spring-Advices sind in Java-Klassen zu erstellen. Die dazugehörigen Pointcuts
werden gewöhnlich in der XML-Konfigurationsdatei geschrieben. Aspekte werden in
Spring zur Laufzeit in Spring-Beans eingebettet, indem sie in einer Proxy-Klasse
gekapselt werden. Der Proxy fängt den Methodenaufruf ab, so dass die Methode um die
Zusatzfunktionalität, den Advice, erweitert werden kann. Der Proxy wird ebenfalls in
der Bean-Konfigurationsdatei definiert. Bei Spring AOP gibt es fünf verschiedene
Typen von Advices, die über Einbindung des entsprechenden Interfaces implementiert
werden. Dabei werden die Interfaces MethodBeforeAdvice, AfterReturningAdvice,
ThrowsAdvice, MethodInterceptor und IntroductionInterceptor unterschieden [Wa08,
Kap. 4.1].
package aop;
public class LoggingAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation){
Log log;
log = LogFactory.getLog(getClassName(methodInvocation));
log.info("Class : " + getClassName(methodInvocation));
log.info("Method : " + getMethodName(methodInvocation));
log.info("Arguments : " + getArguments(methodInvocation));
...
//ausführen der eigentlichen Methode
result = methodInvocation.proceed();
...
}
...
}
Listing 5: LoggingAdvice
Listing 5 zeigt ein typisches Beispiel für einen Advice, den LoggingAdvice. Hiermit
werden zu jeder Methode, die dem Advice zugeordnet wird, Logginginformationen wie
die aufgerufene Methode und deren übergebene Parameter auf der Konsole ausgegeben.
Dazu wird das Interface MethodInterceptor implementiert, das die Methode invoke()
beinhaltet, die implementiert werden muss. Über methodeInvocation.proceed() wird die
abgefangene Methode ausgeführt.
10
Kapitel 3: Spring Framework
Anschließend werden Pointcuts in der Konfigurationsdatei definiert. In Spring werden
Pointcuts am häufigsten über reguläre Ausdrücke oder mit AspectJ-Ausdrücken
festgelegt. Der reguläre Ausdruck legt fest, in welchen Methoden der zum Pointcut
gehörende Advice ausgeführt wird. Dies erfolgt über einen Vergleich zwischen dem
regulären Ausdruck und der Methodensignatur. In der XML-Konfigurationsdatei wird
über das Attribut pattern der reguläre Ausdruck angegeben [Wa08, Kap. 4.1].
<!-Advice-->
<bean id="loggingInterceptor" class="aop.LoggingAdvice" />
<!—Pointcut-->
<bean id="loggingPointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value="services.*" />
</bean>
<!—Advisor-->
<bean id="loggingAdvisor"
class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="loggingInterceptor" />
<property name="pointcut" ref="loggingPointcut" />
</bean>
Listing 6: Konfiguration von Advice, Pointcut und Advisor
In Listing 6 ist der Pointcut loggingPointcut definiert, der für jede Methode in Package
services angewandt wird. Neben dem Pointcut ist noch die Verschmelzung mit dem
Advice zu konfigurieren. Dieser Aspekt wird in Spring als Advisor gehandhabt. Über
die <property>-Tags des Advisor werden der Pointcut und Advice in Listing 6 vereinigt.
3.6 Model View Controller
Für den Aufbau einer webbasierten Anwendung wird in Spring das Modul MVC
eingesetzt. Dieses beruht auf dem Entwurfsmuster Model View Controller (MVC).
Stellt ein Benutzer eine Anfrage, wenn er z. B. alle Bücher der Bibliothek einsehen
möchte, wird diese in Spring zunächst an das Dispatcher Servlet gesendet, welcher auch
als Frontend-Controller bezeichnet wird. Dieser agiert, wie der Begriff schon vermuten
lässt, als Verteiler von Aufgaben. Diese Weitergabe und Entgegennahme der Ergebnisse
visualisiert Abbildung 1.
11
Kapitel 3: Spring Framework
Abbildung 1: Anfrageverarbeitung über Dispatcher Servlet
Wie jedes Servlet muss auch der Dispatcher in der web.xml-Datei deklariert werden.
Über das <url-pattern>-Tag wird angegeben, welche URLs das Servlet behandeln soll.
Die Webanfragen behandelt ein Controller im Auftrag des Dispatcher Servlets (siehe
Nr. 3 in Abbildung 1). Zur Abwicklung der Anfrage verteilt dieser die Aufgaben an die
Serviceschicht und führt die übermittelten Ergebnisse anschließend zusammen. Der
Controller wird als normale Java-Klasse implementiert und im Anwendungskontext als
Bean konfiguriert. Für die vorgestellte Bibliotheksanwendung wird eine Webseite
benötigt, die alle vorhandenen Bücher anzeigt. Der BookController in Listing 7 erbt von
der Klasse AbstractController. Darin wird die Methode handleRequestInternal()
implementiert. In dieser wird die Methode getBooks() des BookService aufgerufen und
ein ModelAndView-Objekt zurückgeben [Wa08 , Kap. 13.1].
package mvc;
public class BookController extends AbstractController{
private BookService bookService;
private String view = "booklist";
public BookService getBookService() {
return bookService;
}
public void setBookService(BookService bookService) {
this.bookService = bookService;
}
protected ModelAndView handleRequestInternal(HttpServletRequest
request, HttpServletResponse response) throws Exception {
Map model = new HashMap();
model.put("books", bookService.getBooks());
return new ModelAndView(view, model);
}
<bean name=”/book.htm“ class=“mvc.BookController“>
<property name=”bookService” ref=”bookService”/>
</bean>
Listing 7: Implementierung und Konfiguration eines Controllers
12
Kapitel 3: Spring Framework
Des Weiteren wird der BookController in Listing 7 als Bean konfiguriert. Im Bean wird
anstelle des Attributes id unter name die URL des Controllers mit vorausgesetztem
Schrägstrich angegeben z. B. /book.htm. So wird jede Anfrage, die mit dieser URL an
das Dispatcher Servlet gestellt wird, an diesen Controller weitergegeben. Anstelle der
Verwendung
der
name-Eigenschaft,
könnte
auch
ein
Handler-Mapping-Bean
konfiguriert werden (siehe Nr. 2 in Abbildung 1). Ein Handler-Mapping-Bean
entscheidet, welcher Controller die Anfrage bearbeitet. Im Controller-Bean wird der
Service, der Aufgaben ausführen soll, über Dependency Injection injiziert. Im Beispiel
ist das BookService. Die Methode handleRequestInternal() zur Behandlung der Anfrage
muss ein Objekt der Klasse ModelAndView zurückgeben. Dieses Objekt kapselt die
View und die Modelldaten, die vom View dargestellt werden sollen. Das
ModelAndView-Objekt wird vom Dispatcher Servlet an den View-Resolver weiter
gegeben (siehe Nr. 5 in Abbildung 1). Der ordnet dem im ModelAndView-Objekt
zurückgegebenen View-Namen eine View zu. Die View ist in den meisten Fällen eine
JSP-Seite z. B. booklist.jsp. Listing 8 zeigt wie der View-Resolver über den
Anwendungskontext
zu
konfigurieren
ist.
Dabei
wird
für
JSP-Seiten
InternalResourceViewResolver verwendet. Mit ihm können über die Attribute prefix
und suffix an den View-Namen Werte vor- bzw. angehangen werden, so dass sich eine
vollständige URL ergibt. Für das Beispiel in Listing 8 ergibt sich die URL „/WEBINF/jsp/booklist.jsp“. An diese URL leitet das Dispatcher Servlet die Anfrage weiter,
um die Webseite für den Benutzer dazustellen. Die JSP-Seite die dabei aufgerufen wird
findet sich in Listing 9. In der JSP-Seite wird dabei auf das Model „books“
zurückgegriffen. Somit bekommt der Benutzer eine Liste mit allen Büchern angezeigt
[Wa08 , Kap. 13.1].
<bean id="viewResolver" class="org.springframework.web.servlet.view.
InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
Listing 8: Konfiguration eines View-Resolvers
<%@ page language="Java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1">
<title>Bücherliste</title>
13
Kapitel 3: Spring Framework
</head>
<body>
<h1>Willkommen in der Bibliothek</h1><br>
<h2>Hier sehen Sie alle verfügbaren Bücher</h2><br>
<table>
<tr><th>Buch</th><th>Autor</th></tr>
<c:forEach items="${books}" var="book">
<tr><td><c:out value="${book.description}"/></td>
<td><c:out value="${book.autor}"/></td>
</tr>
</c:forEach>
</table>
</body>
</html>
Listing 9: View books.jsp
3.7 Datenbankzugriff
Für eine praktische Verwendung benötigt fast jede Anwendung eine Datenpersistenz.
Spring integriert eine ganze Reihe an Datenzugriffs-Frameworks, die mit vielen
Datenzugriffstechnologien verbunden werden können. Dies sind u. a. JDBC oder O/R
Mapper wie Hibernate, iBATIS oder JPA. In diesem Unterkapitel wird die Anbindung
von JDBC und als Beispiel für einen O/R Mapper JPA vorgestellt.
Data Access Objects (DAO) kapseln den Datenbankzugriff in Spring. Dies geschieht
unter Verwendung des Entwurfsmusters der Schablonenmethode (Template Method).
Die generelle Struktur eines Datenzugriffs wird unabhängig von der verwendeten
Technologie in einer abstrakten Oberklasse (Template) definiert. Bestimmte
Operationen des Datenbankzugriffs können in den Unterklassen überschrieben werden,
ohne die Struktur des Zugriffs zu ändern. Zu den feststehenden Teilen des Zugriffs
zählen die Steuerung von Transaktionen, die Ressourcenverwaltung und die
Exceptionbehandlung.
Des
Weiteren
bietet
Spring noch
Supportklassen
zur
Vereinfachung des Zugriffs auf das Template. Den Zusammenhang zeigt Abbildung 2
[Wa08, Kap. 5.1].
Abbildung 2: Beziehung zwischen Anwendungs-DAO, DAO-Supportklasse und Schablonenklasse
[Wa08, Kap. 5.1]
14
Kapitel 3: Spring Framework
Diese Implementierung trennt die Datenzugriffschicht in Spring vom Rest der
Anwendung. Dies hat zur Folge, dass eine konsistente Exception-Hierarchie über alle
DAO-Frameworks hinweg verfügbar ist. Es werden nicht die Exceptions aus JDBC
oder anderen OR-Mappern in das Spring Framework übernommen, da JDBC
Exceptions universell sind und Exceptions von OR-Mapper spezifisch auf die
Datenbank bzw. das Framework zu geschnitten sind. Diese Exception werden
abgefangen und intern verarbeitet. Zurückgegeben werden Spring eigene Exceptions,
die von RuntimeException abgeleitet sind. Für diese ist keine Behandlung notwendig.
So kann sich der Programmierer den catch-Block sparen [Wa08, Kap. 5.1].
JDBC-Anbindung
Grundlage für den Datenbankzugriff ist die Definition eines Datenquellen-Beans
DataSource.
Dafür
stehen
in
Spring
zwei
Klassen
zur
Verfügung.
DriverManagerDataSource gibt bei jeder Anfrage eine neue Verbindung zurück,
während SingleConnectionDataSource immer dieselbe Verbindung wiedergibt. Da
beide Verbindungen über keinen Pool zur Verbindungsverwaltung verfügen, sind sie für
komplexere Anwendungen nicht geeignet. Das Beispiel in Listing 10 zeigt wie das
dataSource-Bean konfiguriert wird. Das Attribut driverClassName enthält den JDBCDatendatenbanktreiber, url die Adresse der verwendete Datenbank und username und
password den zugehörigen Benutzer und das Kennwort [Wa08, Kap. 5.3].
<bean id="datasource" class=
"org.springframework.jdbc.datasource.SingleConnectionDataSource">
<property name=”driverClassName” value=”com.mysql.jdbc.Driver” />
<property name=”url” value=”jdbc:mysql://localhost/bib” />
<property name=”username” value=”sa” />
<property name=”password” vaue=”123” />
</bean>
Listing 10: JDBC Anbindung
Anschließend ist ein AnwendungsDAO zu erstellen. Dafür wird zunächst eine DAOKlasse implementiert. Diese kann von der von Spring bereitgestellten Supportklasse für
JDBC-Datenbanken NamedParameterJdbcDaoSupport abgeleitet werden. Mit dieser
Klasse ist neben der Bereitstellung von Standard-JDBC-Funktionen in SQL-Statements
die Verwendung von benannten Parametern anstatt von indizierten Parametern möglich.
Dafür werden der Methode zur Ausführung eines SQL-Statements das Statement und
eine HashMap mit den Parametern des Statements übergeben. Listing 11 zeigt dies am
15
Kapitel 3: Spring Framework
Beispiel für das Auslesen aller Bücher für die Bibliotheksanwendung. Diese werden mit
Hilfe eines RowMappers in einer Liste von Büchern konvertiert [Wa08, Kap. 5.3].
package jdbc;
public class BookDAO extends NamedParameterJdbcDaoSupport implements
IBookDao{
public List getAll() {
return (List) getJdbcTemplate().query(
"SELECT DESCRIPTION, ISBN, AUTOR, ID FROM Book ",
new BookRowMapper());
}
…}
Listing 11: JDBC Anfrage
JPA-Anbindung
Die JPA-Anbindung erfolgt sowohl in JPA direkt als auch in Spring über einen EntityManager. Des Weiteren wird für die JPA-Anbindung wieder mit einem Template
gearbeitet (siehe Listing 12). Das JpaTemplate erfüllt die gleichen Aufgaben wie andere
Templates für Datenbankanbindungen in Spring auch. Zudem sorgt es dafür, dass der
Entity-Manager wie benötigt geschlossen und geöffnet wird und stellt weitestgehend
dieselben Persistenzmethoden wie ein Entity-Manager für JPA zur Verfügung [Wa08,
Kap. 5.5].
Als Entity-Manager kann ein anwendungsverwaltender oder containerverwaltender
Entity-Manager eingesetzt werden. Der Unterschied zwischen beiden liegt in der
Konfiguration. Diese erfolgt für anwendungsverwaltende Entity-Manager über eine
Konfigurationsdatei namens persistenxe.xml, bei containerverwaltender Konfiguration
im Spring-Anwendungskontext. Listing 12 zeigt wie ein containterverwaltender EntityManager
in
Spring
mithilfe
von
LocalContainerEntityManagerFactoryBean
konfiguriert wird.
<bean id=”jpaTemplate” class=”org.springframework.orm.jpa.jpaTemplate”>
<property name=”entityManagerFactory” ref=”entityManagerFactory” />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.
LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.
TopLinkJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
<property name="database" value="MYSQL" />
</bean>
</property>
16
Kapitel 3: Spring Framework
</bean>
Listing 12: Konfiguration einer containerverwaltenden EntityManagerFactory-Bean
Die Konfiguration der Datenquelle, welche vom Entity-Manager benutzt wird, erfolgt
über die Eigenschaft dataSource und ist vergleichbar mit der Konfiguration in Listing
10. Über die Eigenschaft jpaVendorAdapter können Details zur verwendeten JPAImplementierung angeführt werden. In diesem Fall ist das TopLink Essentials.
Nach der Konfiguration des Entity-Managers wird die DAO-Klasse eingerichtet. Wie
bereits bemerkt, bietet Spring dafür Support-Klassen an, von denen die selbst zu
erstellende DAO-Klasse erbt. Für die JPA-Anbindung wird JpaDaoSupport verwendet.
Diese Verwendung zeigt Listing 13 am Beispiel der Klasse JpaUserDAO. Für die
dauerhafte Speicherung eines Objekts in der Datenbank muss die Methode persist des
JpaTemplate Objekts aufgerufen werden [Wa08, Kap. 5.5].
public class JpaUserDAO extends JpaDaoSupport implements IUserDAO {
public void createUser(User user) throws CreateFailureException
{
getJpaTemplate().persist(user);
}
}
Listing 13: Klasse JpaUserDAO
3.8 Sicherheit
Das Sicherheits-Framework in Spring stellt umfangreiche Sicherheitslösungen für die
Authentifizierung und Autorisierung auf Webanfrage- und Methodenaufrufebene zur
Verfügung. Spring Security nutzt dafür DI und AOP. Mit Hilfe des AuthentificationManagers erfolgt die Überprüfung des Benutzernamens und des dazugehörigen
Passworts. Nach erfolgreicher Authentifizierung wird über den Access-DecisionManager kontrolliert, ob die Anfrage oder Methode von Benutzer ausgeführt werden
darf [Wa08, Kap. 7.1].
Authentifizierung
Der Authentication-Manager verifiziert die eingegebenen Benutzerdaten über die
Methode authenticate(). Bei erfolgreicher Authentifizierung gibt die Methode ein
Authentication-Objekt zurück, das Information über den Benutzer und dessen Rechte
enthält. Andernfalls wird eine AuthenticationException ausgelöst. Für die meisten Fälle
der Benutzerprüfung wird der ProviderManager, eine Implementierung des
17
Kapitel 3: Spring Framework
AuthenticationManager, benutzt. Dabei kann der Benutzer gegenüber mehreren Quellen
zur Identitätsverwaltung authentifiziert werden. Dazu werden die Benutzereingaben an
Provider weitergegeben. Der ProviderManager erhält im Attribut provider eine Liste der
Authentifizierungsdienste. Im Allgemeinen wird nur ein Dienst benötigt. Für die
Authentifizierung
mit
Hilfe
einer
Datenbank
bietet
Spring
Security
den
DaoAuthenticationProvider. Die Konfiguration des ProviderManagers findet sich im
oberen Teil von Listing 14. Dieser kann über die authenticationManager referenziert
werden und beinhaltet einen AuthenticationProvider, der weiter unten definiert ist
[Wa08, Kap. 7.2].
<!--ProviderManager-->
<bean id="authenticationManager"
class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="authenticationProvider" />
</list>
</property>
</bean>
<!-- Authentication-->
<bean id="authenticationProvider" class="org.springframework.
security.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="authenticationDAO" />
</bean>
<bean id="authenticationDAO"
class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource" />
<property name="usersByUsernameQuery">
<value>
SELECT USERNAME, PASSWORD, ENABLE as enabled
FROM USER
WHERE USERNAME = ?
</value>
</property>
<property name="authoritiesByUsernameQuery">
<value>
SELECT u.USERNAME as username, AUTORITY as authority
FROM AUTORITY a , USER u
WHERE a.USERNAME = u.USERNAME
AND u.USERNAME = ?
</value>
</property>
</bean>
Listing 14: Konfiguration des Authentification Managers
Des Weiteren wird in Listing 14 das AuthenticationDAO konfiguriert, das eine
Instanzierung von JdbcDaoImpl ist. Diese erwartet, dass die Benutzerangaben in den
Tabellen Users und Authorities mit den Attributen username, password und enabled
bzw. username und authority vorliegt. Falls dies nicht der Fall ist, können wie in Listing
18
Kapitel 3: Spring Framework
14 über die Eigenschaften usersByUsernameQuery und authoritiesByUsernameQuery
die entsprechenden SQL-Statements angegeben werden.
Autorisierung
Als nächster Schritt nach der Authentifizierung erfolgt die Autorisierung. Dafür wird
zunächst festgelegt, wie die Entscheidung für den Zugriff auf das angefragte Objekt
getroffen
werden
soll.
Es
gibt
dazu
drei
Implementierungen
des
AccessDecisionManager. Die Konfiguration für alle ist gleich.
Listing 15 zeigt den Typ UnianimousBased, der den Zugriff gewährt, wenn alle
Abstimmenden für den Zugriff gestimmt haben. Als nächstes werden eine oder mehrere
Instanzen konfiguriert, welche kontrollieren, ob die Benutzerrechte mit den Rechten, die
für den Zugriff auf das Objekt benötigt werden, übereinstimmen. Jede Instanz
implementiert dabei das Interface AccessDecisionVoter. In
Listing 15 wird eine Instanz roleVoter konfiguriert. Diese stimmt für den Zugriff, wenn
der Benutzer zur Rolle mit dem Präfix „ROLE_“ gehört [Wa08, Kap. 7.3].
Weitere Möglichkeiten, die der Authentication-Manager bietet sind PasswortVerschlüsselung, Caching von Benutzerdaten und Authentifizierung gegenüber LDAP
[Wa08, Kap. 7.2].
<!--RoleVoter-->
<bean id="roleVoter"
class="org.springframework.security.vote.RoleVoter">
<property name="rolePrefix" value="ROLE_" />
</bean>
<!--Access-Decision-Manager-->
<bean id="accessDecisionManager"
class="org.springframework.security.vote.UnanimousBased">
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
Listing 15: Konfiguration des AccessDecisionManagers
Zuordnung zu Methoden
<!--SecurityInterceptor-->
<bean id="securityInterceptor" class="org.springframework.security.
method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager">
<ref bean="authenticationManager"/></property>
19
Kapitel 3: Spring Framework
<property name="accessDecisionManager">
<ref bean="accessDecisionManager"/></property>
<property name="objectDefinitionSource">
<value>
services.BookService.getBooks*=ROLE_USER
services.LendingService.getLendings*=ROLE_USER
services.UserService.getUsers*=ROLE_ADMIN
</value>
</property>
</bean>
<!--AutoProxy-->
<bean id="autoProxyCreator" class="org.springframework.aop.
framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>securityInterceptor</value>
</list>
</property>
<property name="beanNames">
<list>
<value>services.BookService</value>
<value>services.LendingService</value>
<value>services.UserService</value>
</list>
</property>
</bean>
Listing 16: Zuordnung und Konfiguration von Zugriffrechten zu Methoden
Listing 16 zeigt wie Zugriffsrechte für Methoden vergeben werden. Dazu wird ein
securityInterceptor
konfiguriert,
dem
der
AuthenticationManager
und
AccessDecisionManager zugeordnet wird. Über die Eigenschaft objectDefinitionSource
werden einzelnen Methoden Rollen zugeordnet, z. B. darf nur der Admin die Methode
getUsers() aufrufen und damit alle Benutzer einsehen. Außerdem dürfen Benutzer mit
der Rolle ROLE_USER Bücher und Ausleihen aufrufen ebenso wie der Admin. Neben
der Zugriffrechtezuordnung muss zudem ein Proxy für den securityInterceptor definiert
werden. Somit werden bei Aufruf der Beans BookService, LendingService und
UserService zunächst über den Proxy die Zugriffsrechte des Benutzers überprüft. Hier
findet also AOP ihre Anwendung. Wenn der Benutzer nicht authentifiziert ist, wird eine
AuthentificationException ausgelöst. Eine AccessDecisionException wird geworfen,
wenn der Benutzer nicht die erforderlichen Rechte aufweist [Wa08, Kap. 7.6].
20
Kapitel 4: Vergleich Spring und Java EE
4 Vergleich Spring und Java EE
Spring ist entstanden, um die Entwicklung von Java EE-Anwendungen zu vereinfachen
ohne auf Vorteile der Java-EE-Umgebung verzichten zu müssen. Aus diesem Grund
wird es auch als Lightweight-Framework bezeichnet. Dieses leichtgewichtige Modell
baut auf POJOs (Plain Old Java Objects) auf, also auf „ganz normalen“ Objekten.
Dennoch verlangt auch Spring eine gründliche Auseinandersetzung mit dem Framework
damit diese angewandt werden kann. Java EE ist neben seiner Komplexität auch sehr
flexibel. Deshalb werden größere Anwendungen in Java EE implementiert. Mittlerweile
unterstützt auch Java EE durch die Spezifikation EJB 3.0 ein POJO-basiertes
Programmiermodell, das die Anwendung schlichter macht [Wo07, Kap. 1.3].
Java EE und Spring nutzen beide Dependency Injection. In Spring erfolgt die
Konfiguration meistens über Deskriptoren also XML. Dahingegen werden in Java EE
meistens Annotationen verwendet. Deskriptoren können aber für das Überschreiben von
Methoden verwendet werden. Ebenso ist es in Spring möglich Annotationen zu
benutzen. Java EE verwendet für die Geschäftslogik EJBs, die nur zusammen mit einem
Java-Server lauffähig sind. In Spring wird die Geschäftslogik unabhängig vom Server
verwaltet. Ein weiteres Ziel von Spring ist die Integration anderer Frameworks zu
vereinfachen [Wo07, Kap 1.3].
Das Interceptor-Konzept für EJB 3.0 erlaubt die Behandlung von Querschnittsbelangen
wie Sicherheitsaspekten. Dies ist die erste und einfache Form von AOP in einer JavaEE-Anwendung. In Spring stellt AOP einer Grundfunktionalität dar. Dabei werden
Methoden Pointcuts unterstützt. Für einen größeren Funktionsumfang kann AspectJ in
Spring eingebunden werden. Somit bietet Spring eine umfangreichere AOP Umsetzung
als Java EE an [Wo07, Kap 1.3].
Bei Java EE handelt es sich um einen Standard. Dies hat zum Vorteil, dass
Herstellerunabhängigkeit erreicht wird. Des Weiteren ist viel Wissen über Java EE auf
dem Markt verfügbar. Das reduziert den Einarbeitungsaufwand und es können schneller
Lösungen für Probleme gefunden werden. Diese Vorteile können auch in einem Spring
Projekt genutzt werden. Dazu muss Spring als Abstraktion über eine Java-EEUmgebung verwendet werden [Wo07, Kap 1.3].
21
Kapitel 5: Zusammenfassung und Ausblick
5 Zusammenfassung und Ausblick
In dieser Arbeit wurde zunächst der Begriff Framework näher erläutert. Dabei handelt
es sich um ein Rahmenwerk, das für die Softwareentwicklung bestimmte
Funktionalitäten zur Verfügung stellt. Diese können genutzt und erweitert werden. So
wird vermieden, dass wieder verwendbare Funktionalitäten jedes Mal neu
implementiert werden müssen.
Anschließend
wurden
Aspektorientierte
die
Programmiertechniken
Programmierung
erläutert.
Dependency
Dieses
sind
Injektion
die
und
wichtigsten
Programmiertechniken, die im Spring Framework verwendet werden. In Kapitel 3
wurde dieses Framework anhand einer Bibliotheksanwendung näher dargelegt. Dabei
wurden zunächst Spring-Beans und der Spring-Container anhand von Beispielen
veranschaulicht. Spring-Beans stellen Anwendungsobjekte dar, die über eine
Konfigurationsdatei erstellt und über den Spring-Container verwaltet werden. Im
Anschluss daran wurden Dependency Injektion und AOP in Spring vorgestellt. In
Spring können die Typen Setter- und Constructor-Injektion verwendet werden.
Nachfolgend wurde die Konfiguration einer Datenbankanbindung über JDBC und JPA
beschrieben. Außerdem wird für die Gewährleistung der Sicherheit im System eine
verlässige Authentifizierung von Benutzer benötigt. Dazu werden in Spring auch
Aspekte eingesetzt.
Nach deren Vorstellung, schließt die Arbeit mit einem Vergleich zwischen den
Frameworks Spring und Java EE. Dabei wurden herausgestellt, dass Spring als
leichtgewichtige Entwicklung zu Java EE entwickelt wurde. Mittlerweile ist in Java EE
durch die Spezifikation EJB 3.0 die Java-Entwicklung einfacher. In beiden Frameworks
kann Dependency Injection und AOP eingesetzt werden.
In dieser Arbeit wurden einige Funktionen bzw. Module des Spring Frameworks näher
dargelegt. Diese können je nach Bedarf in das Framework eingebunden werden. Sie
vereinfachen die Entwicklung von Java-Anwendungen und sind für den Entwickler eine
gute Unterstützung. Die Funktionen bzw. Module, die in dieser Arbeit nicht vorgestellt
wurden, sind in zahlreichen Büchern über das Spring Framework und auf Webseiten
dokumentiert. Ausblickend lässt sich festhalten, dass aufgrund des hohen Interesses, das
am Spring Framework derzeit besteht, davon ausgegangen werden kann, dass es auch
zukünftig verwendet und weiterentwickelt wird.
22
Anhang A: Klassendiagramm
Anhang A: Klassendiagramm
Abb. 1: Klassendiagramm Bibliothek
23
Anhang B: Quellcode
Anhang B: Quellcode
LoggingAdvice.java
package aop;
import java.util.Arrays;
import
import
import
import
org.aopalliance.intercept.MethodInterceptor;
org.aopalliance.intercept.MethodInvocation;
org.apache.commons.logging.Log;
org.apache.commons.logging.LogFactory;
/**
* Logging Advice, den zu den zugeordneten Methoden
Tracinginformationen
* wie Methodenname, Parameter und Startzeit hinzufügt
* @author Birgit
*
*/
public class LoggingAdvice implements MethodInterceptor {
private static long invocationID = 0;
private static synchronized long getInvocationID() {
return invocationID++;
}
/**
* Methode, den zu den zugeordneten Methoden
Tracinginformationen
* wie Methodenname, Parameter und Startzeit hinzufügt
* @param methodInvocation Methode, die erweitert wird
*/
public Object invoke(MethodInvocation methodInvocation)
throws Throwable {
Object result;
long startMillis;
long endMillis;
long id;
Log log;
log =
LogFactory.getLog(getClassName(methodInvocation));
id = getInvocationID();
startMillis = System.currentTimeMillis();
logMethodStart(log, methodInvocation, id,
startMillis);
try {
result = methodInvocation.proceed();
} catch (Throwable throwable) {
endMillis = System.currentTimeMillis();
logMethodException(log, methodInvocation, id,
startMillis, endMillis, throwable);
throw throwable;
}
endMillis = System.currentTimeMillis();
logMethodEnd(log, methodInvocation, id, startMillis,
endMillis, result);
return result;
}
/**
24
Anhang B: Quellcode
* ermittelt die Parameterwerte der aufgerufenen Methode
* @param methodInvocation Methodenaufruf
* @return gibt die Parameterwerte zurück
*/
private static String getArguments(MethodInvocation
methodInvocation) {
String result;
Object[] args;
args = methodInvocation.getArguments();
if (args == null) {
result = "[]";
} else {
result = Arrays.asList(args).toString();
}
return result;
}
/**
* ermittelt den Methodennamen der aufgerufenen Methode
* @param methodInvocation Methodenaufruf
* @return Methodenname
*/
private static String getMethodName(MethodInvocation
methodInvocation) {
return methodInvocation.getMethod().getName();
}
/**
* ermittelt den Klassenname der aufgerufenen Methode
* @param methodInvocation Methodenaufruf
* @return Klassename
*/
private static String getClassName(MethodInvocation
methodInvocation) {
String result;
Object target;
target = methodInvocation.getThis();
if (target == null) {
result = "null";
} else {
result = target.getClass().getName();
}
return result;
}
/**
* gibt Logginginformation zur Exception des Methodenaufrufes
aus
* @param log Logger
* @param methodInvocation Methodenaufruf
* @param id
Aufrufid
* @param startMillis Startzeit der Methode
* @param endMillis
Endzeit der Methode
* @param throwable
Exception
*/
private void logMethodException(Log log,
MethodInvocation methodInvocation,
long id, long startMillis, long endMillis,
Throwable throwable) {
if (log.isInfoEnabled()) {
StringBuffer sb = new StringBuffer();
sb.append("MethodInvocation " + id
+ " EXCEPTION");
25
Anhang B: Quellcode
sb.append("\n Class : " +
getClassName(methodInvocation));
sb.append("\n Method : " +
getMethodName(methodInvocation));
sb.append("\n Arguments : " +
getArguments(methodInvocation));
sb.append("\n Start:" + startMillis
+ " ms");
sb.append("\n End:" + endMillis + " ms");
sb.append("\n Duration : " +
(endMillis - startMillis) + " ms");
sb.append("\n Result : " + throwable);
log.info(sb);
}
}
/**
* gibt Logginginformation zum Start des Methodenaufrufes aus
* @param log Logger
* @param methodInvocation Methodenaufruf
* @param id
Aufrufid
* @param startMillis Startzeit der Methode
* @param endMillis
Endzeit der Methode
*/
private void logMethodStart(Log log, MethodInvocation
methodInvocation, long id, long startMillis) {
if (log.isInfoEnabled()) {
StringBuffer sb = new StringBuffer();
sb.append("MethodInvocation " + id
+ " START");
sb.append("\n Class : " +
getClassName(methodInvocation));
sb.append("\n Method : " +
getMethodName(methodInvocation));
sb.append("\n Arguments : " +
getArguments(methodInvocation));
sb.append("\n Start : " + startMillis
+ " ms");
log.info(sb);
}
}
/**
* gibt Logginginformation zum Ende des Methodenaufrufes aus
* @param log Logger
* @param methodInvocation Methodenaufruf
* @param id
Aufrufid
* @param startMillis Startzeit der Methode
* @param endMillis
Endzeit der Methode
* @param result
Ergebnis des Methodenaufrufs
*/
private static void logMethodEnd(Log log,
MethodInvocation methodInvocation, long id,
long startMillis, long endMillis, Object result) {
if (log.isInfoEnabled()) {
StringBuffer sb = new StringBuffer();
sb.append("MethodInvocation " + id
+ " END");
sb.append("\n Class : " +
getClassName(methodInvocation));
sb.append("\n Method : " +
26
Anhang B: Quellcode
getMethodName(methodInvocation));
sb.append("\n Arguments : " +
getArguments(methodInvocation));
sb.append("\n Start : " + startMillis
+ " ms");
sb.append("\n End:" + endMillis + " ms");
sb.append("\n Duration : " +
(endMillis - startMillis) + " ms");
sb.append("\n Result : " + result);
log.info(sb);
}
}
}
Book.java
package bib;
/**
* Klasse Buch
* @author Birgit
*
*/
public class Book{
/**
* BuchID
*/
protected int id;
/**
* Beschreibung/Titel des Buchs
*/
protected String description;
/**
* ISBN des Buchs
*/
protected String isbn;
/**
* Author des Buchs
*/
protected String autor;
public Book(String description, String isbn, String autor) {
this.description = description;
this.isbn = isbn;
this.autor = autor;
}
public int getId(){return id;}
public void setId(int id){
this.id = id;
}
public String getDescription(){return description;}
public void setDescription(String description){
this.description = description;
}
public String getISBN(){return isbn;}
public void setISBN(String nr){isbn = nr;}
public String getAutor(){return autor;}
public void setAutor(String a){autor = a;}
}
27
Anhang B: Quellcode
Exemplar.java
package bib;
/**
* Klasse Exemplar (Exemplar eines Buches)
* @author Birgit
*
*/
public class Exemplar {
/**
* ID des Exemplars
*/
protected int id;
/**
* Buch, das zu dem Exemplar gehört
*/
protected Book book;
/**
* mögliche Ausleihe des Exemplars
*/
protected Lending lending;
public Exemplar(int id, Book book) {
super();
this.id = id;
this.book = book;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public Book getBook()
{
return book;
}
public void setBook(Book b)
{
book = b;
}
public Lending getLending()
{
return lending;
}
public void setLending(Lending l)
{
lending = l;
}
}
Lending.java
28
Anhang B: Quellcode
package bib;
import java.util.Date;
/**
* Klasse Ausleihe
* @author Birgit
*
*/
public class Lending {
/**
* ID der Ausleihe
*/
protected int id;
/**
* Startdatum der Ausleihe
*/
protected Date date;
/**
* Benutzer, des das Exemplar ausgeliehen hat
*/
protected User user;
/**
* Exemplar, das ausgeliehen wurde
*/
protected Exemplar exemplar;
public Lending(int id, Date date, User user, Exemplar exemplar)
{
super();
this.id = id;
this.date = date;
this.user = user;
this.exemplar = exemplar;
}
public int getId(){return id;}
public void setId(int id){this.id = id;}
public Date getDate(){return date;}
public void setDate(Date d){date = d;}
public User getUser(){return user;}
public void setUser(User u){
user = u;
}
public Exemplar getExemplar(){return exemplar;}
public void setExemplar(Exemplar e){
exemplar = e;
}
}
User.java
package bib;
import java.util.ArrayList;
import java.util.Collection;
/**
* Klasse Benutzer
29
Anhang B: Quellcode
* @author Birgit
*
*/
public class User {
/**
* BenutzerID
*/
protected int id;
/**
* Anmeldename/User
*/
protected String username;
/**
* Vorname
*/
protected String firstname;
/**
* Nachname
*/
protected String lastname;
/**
* Password
*/
protected String password;
protected Collection<Lending> lending =
new ArrayList<Lending>();
public User (String firstname,String lastname){
this.firstname = firstname;
this.lastname = lastname;
}
public int getId(){return id;}
public void setId(int id){this.id = id;}
public String getFirstname(){return firstname;}
public void setFirstname(String firstname){
this.firstname = firstname;
}
public String getLastname(){return lastname;}
public void setLastname(String lastname){
this.lastname = lastname;
}
public Collection<Lending> getLending(){return lending;}
public void setLending(Collection<Lending> coll){
lending = coll;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
30
Anhang B: Quellcode
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
IBookDAO.java
package dao;
import java.util.List;
import bib.Book;
/**
* Schnittstelle für DAO für Bücher
* @author Birgit
*
*/
public interface IBookDAO {
/**
* gibt Liste aller Bücher zurück
* @return Bücherliste
*/
List<Book> getAll();
/**
* gibt alle Bücher zur eingegeben Beschreibung zurück
* @param description Beschreibung
* @return Bücherliste
*/
List<Book> getByDescription(String description);
/**
* speichert das eingegebene Buch
* @param book Buch
* @return gespeicherte Buch
*/
Book save(Book book);
/**
* löscht alle Bücher zur eingegeben Beschreibung
* @param description Beschreibung
*/
void deleteByDescription(String description);
/**
* gibt ein Buch zur eingegebenen ID zurück
* @param id
* @return Buch
*/
Book getByID(int id);
/**
* löscht das Bücher zur eingegeben ID
* @param id
*/
void deleteByID(int id);
/**
* ändert das eingegebene Buch
* @param book Buch
*/
void update(Book book);
}
31
Anhang B: Quellcode
IExemplarDAO.java
package dao;
import java.util.List;
import bib.Exemplar;
/**
* Schnittstelle für DAO Exemplar
* @author Birgit
*
*/
public interface IExemplarDAO {
/**
* gibt Exemplare zur BuchID zurück
* @param bookid BuchID
* @return Exemplarliste
*/
List<Exemplar> getByBook(String bookid);
/**
* speichert ein Exemplar
* @param exemplar Exemplar
* @return gespeichertes Exemplar
*/
Exemplar save(Exemplar exemplar);
/**
* gibt zur ID Exemplar zurück
* @param id
* @return Exemplar
*/
Exemplar getByID(int id);
/**
* löscht Exemplar zur ID
* @param id
*/
void deleteByID(int id);
/**
* ändert ein Exemplar
* @param exemplar Exemplar
*/
void update(Exemplar exemplar);
}
ILendingDAO.java
package dao;
import java.util.List;
import bib.Lending;
/**
* Schnittstelle für DAO Ausleihe
* @author Birgit
*
*/
public interface ILendingDAO {
/**
* speichert eine Ausleihe
* @param lending Ausleihe
* @return gespeicherte Ausleihe
32
Anhang B: Quellcode
*/
public Lending save(Lending lending);
/**
* gibt Ausleihen zu einer Benutzerid zurück
* @param userid Benutzerid
* @return Ausleihenliste
*/
public List getByUserID(int userid);
/**
* gibt alle Ausleihen zurück
* @return Ausleihenliste
*/
public List getAll();
}
IUserDAO.java
package dao;
import java.util.List;
import bib.User;
/**
* Schnittstelle DAO Benutzer
* @author Birgit
*
*/
public interface IUserDAO {
/**
* gibt den Benutzer zum Anmeldenamen zurück
* @param username Anmeldename
* @return Benutzer
*/
User getByName(String username);
/**
* speichert einen Benutzer
* @param user Benutzer
* @return gespeicherter Benutzer
*/
User save(User user);
/**
* löscht einen Benutzer zum Anmeldenamen
* @param name Anmeldename
*/
void deleteByName(String name);
/**
* gibt einen Benutzer zur ID zurück
* @param id
* @return Benutzer
*/
User getByID(int id);
/**
* löscht eine Benutzer zur ID
* @param id
*/
void deleteByID(int id);
/**
* gibt alle Benutzer zurück
* @return Benutzerliste
*/
List<User> getAll();
}
33
Anhang B: Quellcode
bookDAO.java
package jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import
org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSourc
e;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import
org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import bib.Book;
import dao.IBookDAO;
/**
* DAO für Bücher
* @author Birgit
*
*/
public class BookDAO extends NamedParameterJdbcDaoSupport implements
IBookDAO {
/**
* innere Klasse die eine RowMapper für Bücher zur Verfügung
stellt
*
*/
private static final class BookRowMapper implements RowMapper {
/**
* Methode zu Mappen von Bücher
* @param rs Ergebnis der SQL-Abfrage
* @param rowNum Anzahl Zeilen
* @return Buchobjekt
*/
public Object mapRow(ResultSet rs, int rowNum) throws
SQLException {
Book Book = new
Book(rs.getString(1),rs.getString(2),rs.getString(3));
Book.setId(rs.getInt(4));
return Book;
}
}
public BookDAO(){
}
/**
* Konstruktor
* @param dataSource Datenquelle
*/
public BookDAO(DataSource dataSource) {
setDataSource(dataSource);
}
/**
* gibt alle Bücher zur eingegeben Beschreibung zurück
* @param description Beschreibung
* @return Bücherliste
34
Anhang B: Quellcode
*/
public List getByDescription(String description) {
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("description", description);
return getNamedParameterJdbcTemplate()
.query(
"SELECT DESCRIPTION, ISBN, AUTOR, ID FROM Book
WHERE DESCRIPTION=:description",
params, new BookRowMapper());
}
/**
* gibt ein Buch zur eingegebenen ID zurück
* @param id
* @return Buch
*/
public Book getByID(int id) {
return (Book) getNamedParameterJdbcTemplate().
queryForObject(
"SELECT DESCRIPTION, ISBN, AUTOR, ID FROM Book
WHERE ID=:id",
new MapSqlParameterSource("id", id), new
BookRowMapper());
}
/**
* speichert das eingegebene Buch
* @param book Buch
* @return gespeicherte Buch
*/
public Book save(Book Book) {
initTemplateConfig();
getNamedParameterJdbcTemplate().update(
"INSERT INTO Book (DESCRIPTION, ISBN, AUTOR)
VALUES(:description, :isbn, :autor)",
new BeanPropertySqlParameterSource(Book));
return Book;
}
/**
* löscht alle Bücher zur eingegeben Beschreibung
* @param description Beschreibung
*/
public void deleteByDescription(String description) {
getNamedParameterJdbcTemplate().update(
"DELETE FROM Book WHERE DESCRIPTION=:description",
new MapSqlParameterSource("description",
description));
}
/**
* löscht das Bücher zur eingegeben ID
* @param id
*/
public void deleteByID(int id) {
getNamedParameterJdbcTemplate().update("DELETE FROM Book WHERE
ID=:id",
new MapSqlParameterSource("id", id));
}
/**
* ändert das eingegebene Buch
* @param book Buch
*/
public void update(Book Book) {
getNamedParameterJdbcTemplate()
35
Anhang B: Quellcode
.update(
"UPDATE Book SET
DESCRIPTION=:description,ISBN=:isbn,AUTOR=:autor
WHERE ID=:id",
new BeanPropertySqlParameterSource(Book));
}
/**
* gibt Liste aller Bücher zurück
* @return Bücherliste
*/
public List getAll() {
return (List) getJdbcTemplate().query(
"SELECT DESCRIPTION, ISBN, AUTOR, ID FROM Book ",
new BookRowMapper());
}
}
ExemplarDAO.java
package jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import
org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import bib.Book;
import bib.Exemplar;
import dao.IBookDAO;
/**
* Klasse für DAO Exemplar
* @author Birgit
*
*/
public class ExemplarDAO extends NamedParameterJdbcDaoSupport
implements dao.IExemplarDAO{
/**
* BuchDAO
*/
private static IBookDAO bookDAO;
/**
*
* innere Klasse, die einen RowMapper für Exemplar zur Verfügung
stellt
*
*/
private static final class ExemplarRowMapper implements
RowMapper {
/**
* Methode zu mappen einer SQL Abfrage auf Exemplar-Objekte
* @param rs Ergebnis der SQL-Abfrage
* @param rowNum Anzahl Zeilen
* @return Exemplarobjekt
*/
public Object mapRow(ResultSet rs, int rowNum) throws
36
Anhang B: Quellcode
SQLException {
Book book = bookDAO.getByID(rs.getInt(2));
Exemplar exemplar = new Exemplar(rs.getInt(1),book);
return exemplar;
}
}
public ExemplarDAO() {
}
/**
* Konstruktor
* @param dataSource Datenquelle
*/
public ExemplarDAO(DataSource dataSource) {
setDataSource(dataSource);
}
/**
* gibt Exemplare zur BuchID zurück
* @param bookid BuchID
* @return Exemplarliste
*/
public List<Exemplar> getByBook(String bookid){
MapSqlParameterSource params = new
MapSqlParameterSource();
params.addValue("bookid", bookid);
return getNamedParameterJdbcTemplate()
.query( "SELECT ID, BOOKID FROM Exemplar WHERE
BOOKID=:bookid",
params, new ExemplarRowMapper());
}
/**
* speichert ein Exemplar
* @param exemplar Exemplar
* @return gespeichertes Exemplar
*/
public Exemplar save(Exemplar exemplar){
initTemplateConfig();
getJdbcTemplate()
.update(
"INSERT INTO Exemplar (ID, BOOKID)
VALUES(?, ?)",
new Object[] {exemplar.getId(),
exemplar.getBook().getId()});
return exemplar;
}
/**
* gibt zur ID Exemplar zurück
* @param id
* @return Exemplar
*/
public Exemplar getByID(int id){
return (Exemplar) getNamedParameterJdbcTemplate().
queryForObject(
"SELECT ID, BOOKID FROM Exemplar WHERE ID=:id",
new MapSqlParameterSource("id", id), new
ExemplarRowMapper());
}
/**
* löscht Exemplar zur ID
* @param id
37
Anhang B: Quellcode
*/
public void deleteByID(int id){
getNamedParameterJdbcTemplate().update("DELETE FROM
Exemplar WHERE ID=:id",
new MapSqlParameterSource("id", id));
}
/**
* ändert ein Exemplar
* @param exemplar Exemplar
*/
public void update(Exemplar exemplar){
getJdbcTemplate()
.update(
"UPDATE Exemplar SET ID=?,BOOKID=? WHERE ID=?",
new Object[] {exemplar.getId(),
exemplar.getBook().getId()});
}
public IBookDAO getBookDAO() {
return bookDAO;
}
public void setBookDAO(IBookDAO bookDAO) {
this.bookDAO = bookDAO;
}
}
LendingDAO.java
package jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import
org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import bib.User;
import bib.Exemplar;
import bib.Lending;
import dao.IExemplarDAO;
import dao.IUserDAO;
/**
* Klasse für DAO Ausleihe
* @author Birgit
*
*/
public class LendingDAO extends NamedParameterJdbcDaoSupport
implements dao.ILendingDAO {
/**
* Exemplar DAO
38
Anhang B: Quellcode
*/
private static IExemplarDAO exemplarDAO;
/**
* Benutzer DAO;
*/
private static IUserDAO userDAO;
/**
* innere Klasse, die RowMapper für Ausleihen zur Verfügung stellt
*
*/
private static final class LendingRowMapper implements RowMapper
{
/**
* Methode zum Mappen von SQL-Ergebnis auf AusleihObjekte
* @param rs Ergebnis der SQL-Abfrage
* @param rowNum Anzahl Zeilen
* @return Ausleihobjekt
*/
public Object mapRow(ResultSet rs, int rowNum) throws
SQLException {
User user = userDAO.getByID(rs.getInt(3));
Exemplar exemplar = exemplarDAO.getByID(rs.getInt(4));
Lending lending = new Lending(rs.getInt(1),rs.getDate(2),
user, exemplar);
return lending;
}
}
public LendingDAO() {
super();
}
public LendingDAO(DataSource dataSource, IExemplarDAO
exemplarDAO, IUserDAO userDAO) {
setDataSource(dataSource);
this.exemplarDAO = exemplarDAO;
this.userDAO = userDAO;
}
/**
* speichert eine Ausleihe
* @param lending Ausleihe
* @return gespeicherte Ausleihe
*/
public Lending save(Lending lending) {
initTemplateConfig();
getJdbcTemplate()
.update(
"INSERT INTO Lending (ID, DATE, USERID,
EXEMPLARID) VALUES(?, ?, ?, ?)",
new Object[] {lending.getId(),
lending.getDate(), lending.getUser().getId(),
lending.getExemplar().getId()});
return lending;
}
/**
* gibt Ausleihen zu einer Benutzerid zurück
39
Anhang B: Quellcode
* @param userid Benutzerid
* @return Ausleihenliste
*/
public List getByUserID(int userid) {
MapSqlParameterSource params = new
MapSqlParameterSource();
params.addValue("userid", userid);
return getNamedParameterJdbcTemplate()
.query(
"SELECT ID, DATE, USERID, EXEMPLARID
FROM Lending WHERE USERID=:userid",
params, new LendingRowMapper());
}
/**
* gibt alle Ausleihen zurück
* @return Ausleihenliste
*/
public List getAll() {
MapSqlParameterSource params = new
MapSqlParameterSource();
return getNamedParameterJdbcTemplate()
.query(
"SELECT ID, DATE, USERID, EXEMPLARID
FROM Lending",
params, new LendingRowMapper());
}
public IExemplarDAO getExemplarDAO() {
return exemplarDAO;
}
public void setExemplarDAO(IExemplarDAO exemplarDAO) {
this.exemplarDAO = exemplarDAO;
}
public IUserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(IUserDAO userDAO) {
this.userDAO = userDAO;
}
}
UserDAO.java
package jdbc;
import
import
import
import
import
java.sql.ResultSet;
java.sql.SQLException;
java.util.HashMap;
java.util.List;
java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.RowMapper;
import
org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
40
Anhang B: Quellcode
import bib.User;
import dao.IUserDAO;
/**
* Klasse für DAO Benutzer
* @author Birgit
*
*/
public class UserDAO extends NamedParameterJdbcDaoSupport implements
IUserDAO{
/**
* innere Klasse, die RowMapper für Benutzer zur Verfügung stellt
*
*/
private static final class UserResultSetRowMapper implements
RowMapper {
/**
* Methode zum Mappen von SQL-Ergebnis auf BenutzerObjekte
* @param rs Ergebnis der SQL-Abfrage
* @param rowNum Anzahl Zeilen
* @return Benutzerobjekt
*/
public Object mapRow(ResultSet rs, int rowNum) throws
SQLException {
User user = new User( rs.getString(3),rs.getString(4));
user.setId(rs.getInt(1));
user.setUsername(rs.getString(2));
return user;
}
}
public UserDAO() {
super();
}
public UserDAO(DataSource dataSource) {
setDataSource(dataSource);
}
/**
* löscht eine Benutzer zur ID
* @param id
*/
public void deleteByID(int id) {
Map parameters = new HashMap();
parameters.put("id", id);
getNamedParameterJdbcTemplate().update
("DELETE FROM USER WHERE ID=?",
parameters);
}
/**
* gibt einen Benutzer zur ID zurück
* @param id
* @return Benutzer
*/
public User getByID(int id) {
Map parameters = new HashMap();
parameters.put("ID", id);
return (User) getNamedParameterJdbcTemplate().queryForObject(
"SELECT * FROM USER WHERE ID = (:ID)" ,
parameters
41
Anhang B: Quellcode
, new UserResultSetRowMapper());
}
/**
* gibt den Benutzer zum Anmeldenamen zurück
* @param username Anmeldename
* @return Benutzer
*/
public User getByName(String username) {
Map parameters = new HashMap();
parameters.put("USERNAME", username);
return (User) getNamedParameterJdbcTemplate()
.queryForObject(
"SELECT * FROM USER WHERE USERNAME=
(:USERNAME)",
parameters, new UserResultSetRowMapper());
}
/**
* löscht einen Benutzer zum Anmeldenamen
* @param name Anmeldename
*/
public void deleteByName(String name) {
getJdbcTemplate().update("DELETE FROM USER WHERE USERNAME=?",
new Object[] { name });
}
/**
* speichert einen Benutzer
* @param user Benutzer
* @return gespeicherter Benutzer
*/
public User save(User user) {
Map parameters = new HashMap();
parameters.put("firstname", user.getFirstname());
parameters.put("lastname", user.getLastname());
parameters.put("username", user.getUsername());
parameters.put("password", user.getPassword());
getNamedParameterJdbcTemplate().update(
"INSERT INTO
USER(FIRSTNAME,LASTNAME,USERNAME,PASSWORD) " +
"VALUES(:firstname,:lastname,:username,:password)",
parameters);
return user;
}
/**
* gibt alle Benutzer zurück
* @return Benutzerliste
*/
public List getAll() {
List result = (List) getJdbcTemplate().query(
"SELECT * FROM USER",
new Object[] {}, new UserResultSetRowMapper());
return result;
}
}
42
Anhang B: Quellcode
BookController.java
package mvc;
import services.BookService;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
/**
* Controller, der das Modell und die View zu einer Bücheranfrage
zurückgibt
* @author Birgit
*
*/
public class BookController extends AbstractController{
/**
* Verweis auf Bücherservice
*/
private BookService bookService;
/**
* Viewname
*/
private String view = "booklist";
public BookService getBookService() {
return bookService;
}
public void setBookService(BookService bookService) {
this.bookService = bookService;
}
/**
* gibt das Modell und die View zu einer Bücheranfrage
zurückgibt
* @param request HttpRequest
* @param response HTTP Response
*/
protected ModelAndView handleRequestInternal(HttpServletRequest
request, HttpServletResponse response) throws Exception {
Map model = new HashMap();
model.put("books", bookService.getBooks());
return new ModelAndView(view,model);
}
}
LendingController.java
package mvc;
import java.util.HashMap;
import java.util.Map;
43
Anhang B: Quellcode
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import services.LendingService;
/**
* Controller, der das Modell und die View zu einer Ausleihanfrage
zurückgibt
* @author Birgit
*
*/
public class LendingController extends AbstractController{
/**
* Viewname
*/
private String view = "lendinglist";
/**
* Service Ausleihe
*/
private LendingService lendingService;
/**
* gibt das Modell und die View zu einer Anfrage zurückgibt
* @param request HttpRequest
* @param response HTTP Response
*/
protected ModelAndView
handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String user = (String)
SecurityContextHolder.getContext().
getAuthentication().getName();
Map model = new HashMap();
model.put("lendings",
lendingService.getLendings(user));
return new ModelAndView(view,model);
}
public LendingService getLendingService() {
return lendingService;
}
public void setLendingService(LendingService lendingService) {
this.lendingService = lendingService;
}
}
LoginController.java
package mvc;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
44
Anhang B: Quellcode
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
/**
* Controller, der das Modell und die View zu einer LoginAnfrage
zurückgibt
* @author Birgit
*
*/
public class LoginController extends AbstractController{
/**
* gibt die View zu einer LoginAnfrage zurückgibt
* @param request HttpRequest
* @param response HTTP Response
*/
protected ModelAndView handleRequestInternal(HttpServletRequest
request,HttpServletResponse response) throws Exception {
return new ModelAndView("login");
}
}
StartController.java
package mvc;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import
import
import
import
org.springframework.security.Authentication;
org.springframework.security.context.SecurityContextHolder;
org.springframework.web.servlet.ModelAndView;
org.springframework.web.servlet.mvc.AbstractController;
import services.UserService;
/**
* Controller, der das Modell und die View zu einer Anfrage zurückgibt
* @author Birgit
*
*/
public class StartController extends AbstractController{
/**
* gibt das Modell und die View zu einer Anfrage zurückgibt
* @param request HttpRequest
* @param response HTTP Response
*/
protected ModelAndView handleRequestInternal(HttpServletRequest
request, HttpServletResponse response) throws Exception {
Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
if(SecurityContextHolder.getContext()
.getAuthentication().getName().toString().
equals("anonymousUser")
|| ! (auth.isAuthenticated()))
return new ModelAndView("login");
for (int i= 0; i<auth.getAuthorities().length;i++){
if (auth.getAuthorities()[i].getAuthority().
equals("ROLE_ADMIN")){
45
Anhang B: Quellcode
return new ModelAndView("admin/user");
}
}
return new ModelAndView("start");
}
}
Usercontroller.java
package mvc;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import services.UserService;
/**
* Controller gibt das Modell und die View zu einer Anfrage
zurückgibt
* @author Birgit
*
*/
public class UserController extends AbstractController{
/**
* Viewname
*/
private String view = "admin/userlist";
/**
* Service User
*/
private UserService userService;
/**
* gibt das Modell und die View zu einer Anfrage zurückgibt
* @param request HttpRequest
* @param response HTTP Response
*/
protected ModelAndView
handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
Map model = new HashMap();
model.put("users", userService.getUsers());
return new ModelAndView(view,model);
}
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
BookService.java
package services;
46
Anhang B: Quellcode
import java.util.List;
import dao.IBookDAO;
/**
* Serivceschicht Book
* @author Birgit
*
*/
public class BookService {
/**
* DAO Book
*/
private IBookDAO bookDAO;
/**
* gibt alle Bücher zurück
* @return Bücherliste
*/
public List getBooks(){
return bookDAO.getAll();
}
public IBookDAO getBookDAO() {
return bookDAO;
}
public void setBookDAO(IBookDAO bookDAO) {
this.bookDAO = bookDAO;
}
}
LendingService.java
package services;
import java.util.List;
import dao.ILendingDAO;
import dao.IUserDAO;
import bib.Lending;
import bib.User;
/**
* Serviceschicht Lending
* @author Birgit
*
*/
public class LendingService {
/**
* DAO Lending
*/
private ILendingDAO lendingDAO;
/**
* DAO User
*/
private IUserDAO userDAO;
/**
* gibt alle Ausleihen eines Benutzers zurück
* @param username Benutzername
* @return Ausleihliste
*/
public List<Lending> getLendings(String username){
User user = (User) userDAO.getByName(username);
47
Anhang B: Quellcode
int userid = user.getId();
return lendingDAO.getByUserID(userid);
}
public ILendingDAO getLendingDAO() {
return lendingDAO;
}
public void setLendingDAO(ILendingDAO lendingDAO) {
this.lendingDAO = lendingDAO;
}
public IUserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(IUserDAO userDAO) {
this.userDAO = userDAO;
}
}
UserService.java
package services;
import java.util.List;
import bib.User;
import dao.IUserDAO;
/**
* Serviceschicht User
* @author Birgit
*
*/
public class UserService {
/**
* User DAO
*/
private IUserDAO userDAO;
/**
* gibt alle Benutzer zurück
* @return Benutzerliste
*/
public List getUsers(){
return userDAO.getAll();
}
/**
* legt einen Benutzer in der DB an
* @param user Benutzer
*/
public void createUser(User user) {
userDAO.save(user);
}
public IUserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(IUserDAO userDAO) {
this.userDAO = userDAO;
48
Anhang B: Quellcode
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security2.0.2.xsd">
<!-- =============== AOP ==============================-->
<!--LoggingInterceptor -->
<bean id="loggingInterceptor" class="aop.LoggingAdvice" />
<bean id="loggingPointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value="services.*" />
</bean>
<!-- Verbindung Adivce und Pointcut -->
<bean id="loggingAdvisor"
class="org.springframework.aop.support.DefaultPointcutAdvisor">
49
Anhang B: Quellcode
<property name="advice" ref="loggingInterceptor" />
<property name="pointcut" ref="loggingPointcut" />
</bean>
<!-- =============== Datenschicht ==============================-->
<!-- DataSource -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.SingleConnectionDataSource"
>
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/bib" />
<property name="username" value="root" />
<property name="password" value="admin" />
</bean>
<!-- JDBC Template -->
<bean id="template"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
<property name="nativeJdbcExtractor">
<bean
class="org.springframework.jdbc.support.nativejdbc.
CommonsDbcpNativeJdbcExtractor" />
</property>
</bean>
<!--=============== DAO ==============================-->
<bean id="bookDAO" class="jdbc.BookDAO">
<property name="jdbcTemplate" ref="template" />
</bean>
<bean id="userDAO" class="jdbc.UserDAO">
<property name="jdbcTemplate" ref="template" />
</bean>
<bean id="exemplarDAO" class="jdbc.ExemplarDAO">
<property name="jdbcTemplate" ref="template" />
<property name="bookDAO" ref="bookDAO" />
</bean>
<bean id="lendingDAO" class="jdbc.LendingDAO">
<property name="jdbcTemplate" ref="template" />
<property name="userDAO" ref="userDAO" />
<property name="exemplarDAO" ref="exemplarDAO" />
</bean>
<!--=============== Beginn Sicherheitskonfiguration================-->
<!-- Beans für Authentifikation und Autorisierung -->
<bean id="authenticationDAO"
class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource" />
<property name="usersByUsernameQuery">
<value>
SELECT USERNAME, PASSWORD, ENABLE as enabled
FROM USER
WHERE USERNAME = ?
</value>
</property>
50
Anhang B: Quellcode
<property name="authoritiesByUsernameQuery">
<value>
SELECT u.USERNAME , a.AUTORITY as authority
FROM AUTORITY a , USER u
WHERE a.USERNAME = u.USERNAME
AND u.USERNAME = ?
</value>
</property>
</bean>
<bean id="authenticationProvider"
class="org.springframework.security.providers.dao.DaoAuthenticationPro
vider">
<property name="userDetailsService" ref="authenticationDAO" />
</bean>
<!-- ProviderManager -->
<bean id="authenticationManager"
class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="authenticationProvider" />
</list>
</property>
</bean>
<!-- RoleVoter -->
<bean id="roleVoter"
class="org.springframework.security.vote.RoleVoter">
<property name="rolePrefix" value="ROLE_" />
</bean>
<!-- Access-Decision-Manager -->
<bean id="accessDecisionManager"
class="org.springframework.security.vote.UnanimousBased">
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
<!--=============== Absicherung Webschicht=========================-->
<!--Filter für die Rechtevergabe auf der Webschicht -->
<bean id="filterSecurityInterceptor"
class="org.springframework.security.intercept.
web.FilterSecurityInterceptor">
<property name="authenticationManager">
<ref bean="authenticationManager"/></property>
<property name="accessDecisionManager">
<ref bean="accessDecisionManager"/></property>
<property name="objectDefinitionSource">
<value>
CONVERT_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/book.htm=ROLE_USER
/lending.htm=ROLE_USER
/admin/user.htm=ROLE_ADMIN
</value>
51
Anhang B: Quellcode
</property>
</bean>
<!--bei Fehler wird authenticationEntryPoint aufgerufen -->
<bean id="exceptionTranslationFilter"
class="org.springframework.security.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint"
ref="authenticationEntryPoint" />
</bean>
<!-- falls der Benutzer nicht authentifiziert ist, wird er nach
loginFormUrl umgeleitet -->
<bean id="authenticationEntryPoint"
class="org.springframework.security.ui.webapp.
AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/login.htm" />
<property name="forceHttps" value="false" />
</bean>
<!-- Bean zur Durchführung der Authentifizierung -->
<bean id="authenticationProcessingFilter"
class="org.springframework.security.ui.webapp.
AuthenticationProcessingFilter">
<property name="filterProcessesUrl"
value="/j_acegi_security_check" />
<property name="authenticationManager"
ref="authenticationManager" />
<property name="authenticationFailureUrl" value="/login.htm" />
<property name="defaultTargetUrl" value="/" />
</bean>
<!-- Benutzerdaten werden in der HTTPSession gespeichert -->
<bean id="httpSessionContextIntegrationFilter"
class="org.springframework.security.context.
HttpSessionContextIntegrationFilter">
<property name="contextClass"
value="org.springframework.security.
context.SecurityContextImpl" />
</bean>
<bean id="filterChainProxy"
class="org.springframework.security.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,logoutFilter,
authenticationProcessingFilter,exceptionTranslationFilter,
filterSecurityInterceptor
</value>
</property>
</bean>
<bean id="logoutFilter"
class="org.springframework.security.ui.logout.LogoutFilter">
<constructor-arg value="/index.htm"/>
<!-- URL redirected to after logout -->
<constructor-arg>
<list>
<bean class="org.springframework.security.ui.
52
Anhang B: Quellcode
logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
<property name="filterProcessesUrl" value="/j_acegi_logout" />
</bean>
<!--=============== Absicherung Geschäftslogik
==============================-->
<bean id="securityInterceptor"
class="org.springframework.security.intercept.method.
aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager">
<ref bean="authenticationManager"/></property>
<property name="accessDecisionManager">
<ref bean="accessDecisionManager"/></property>
<property name="objectDefinitionSource">
<value>
services.BookService.getBooks=ROLE_USER
services.LendingService.getLendings=ROLE_USER
services.UserService.getUsers=ROLE_ADMIN
</value>
</property>
</bean>
<!-- AutoProxy für die Interceptoren-->
<bean id="autoProxyCreator" class="org.springframework.aop.framework.
autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>securityInterceptor</value>
<value>loggingInterceptor</value>
</list>
</property>
<property name="beanNames">
<list>
<value>bookService</value>
<value>lendingService</value>
<value>userService</value>
</list>
</property>
</bean>
<!--=============== Ende Konfiguration Sicherheit =================-->
<!--=============== Services /Geschäftslogik ======================-->
<bean id="lendingService" class="services.LendingService">
<property name="lendingDAO" ref="lendingDAO" />
<property name="userDAO" ref="userDAO" />
</bean>
<bean id="bookService" class="services.BookService">
<property name="bookDAO" ref="bookDAO" />
</bean>
<bean id="userService" class="services.UserService">
<property name="userDAO" ref="userDAO" />
</bean>
</beans>
53
Anhang B: Quellcode
springapp-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security2.0.2.xsd">
<import resource="applicationContext.xml" />
<!--=============== Web-Konfiguration =============================-->
<bean name="/login.htm" class="mvc.LoginController"/>
<bean name="/lending.htm" class="mvc.LendingController">
<property name="lendingService" ref="lendingService" />
</bean>
<bean name="/admin/userlist.htm" class="mvc.UserController">
<property name="userService" ref="userService" />
</bean>
<bean name="/book.htm" class="mvc.BookController">
<property name="bookService" ref="bookService" />
</bean>
<bean name="/start.htm" class="mvc.StartController"/>
<bean id="viewResolver" class="org.springframework.web.
servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.
servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp"/>
</bean>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
version="2.5">
<listener>
<listener-class>
org.springframework.web.context.
54
Anhang B: Quellcode
ContextLoaderListener
</listener-class>
</listener>
<filter>
<filter-name>filterChainProxy</filter-name>
<filter-class>org.springframework.security.util.
FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.springframework.security.
util.FilterChainProxy</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filterChainProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<display-name>SpringApp</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>springapp</servlet-name>
<servlet-class>org.springframework.web.servlet.
DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
</web-app>
index.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:redirect url="/start.htm"/>
start.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ page
import="org.springframework.security.context.SecurityContextHolder" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel="stylesheet" href="../style/style.css"
type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1">
<title>Bibliothek</title>
</head>
55
Anhang B: Quellcode
<body>
<div id="main">
Willkommen <%= SecurityContextHolder.getContext()
.getAuthentication().getName() %>
auf der Webseite der Bibliothek!<br>
<img src="../image/buecherei.jpg" /><br>
Was möchten Sie tun?<br>
<a href="book.htm">Bücherliste einsehen</a><br>
<a href="lending.htm">aktuelle Ausleihen</a><br>
<a href="SpringApp/j_acegi_logout">Logout</a>
</div>
</body>
</html>
login.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel="stylesheet" type="text/css" href="/style/style.css" />
<meta http-equiv="Content-Type" content="text/html; charset=ISO-88591">
<title>Bibliothek: Anmeldung</title>
</head>
<body>
<div id="main">
<h1>Willkommen der Webseite der Bibliothek!</h1><br>
<img src="/image/buecherei.jpg" width="300" height="200" /><br>
Sie müssen sich anmelden bevor Sie unseren Service
in Anspruch nehmen können.<br>
<form action="j_acegi_security_check" method="POST">
<p>Benutzer: <input type="text" name="j_username">
<p>Password: <input type="password" name="j_password">
<p><input type="submit" value="Anmelden">
</form>
</div>
</body>
</html>
lending.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"
%>
<%@ page
import="org.springframework.security.context.SecurityContextHolder" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel="stylesheet" href="./style/style.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-88591">
<title>Ausleihen</title>
</head>
56
Anhang B: Quellcode
<body>
<h1>Willkommen: <%=
SecurityContextHolder.getContext().getAuthentication().getName()
%></h1>
<h2>Hier sehen Sie alle von Ihnen ausgeliehenen Bücher</h2>
<br>
<table>
<tr><th>Datum</th><th>Benutzer</th><th>Exemplar</th></tr>
<c:forEach items="${lendings}" var="lending">
<tr><td><c:out value="${lending.date}"/></td>
<td><c:out value="${lending.user.username}"/></td>
<td>
<c:out value="${lending.exemplar.book.description}"/></td>
</tr>
</c:forEach>
</table>
</body>
</html>
booklist.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"
%>
<%@ page
import="org.springframework.security.context.SecurityContextHolder" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css" />
<meta http-equiv="Content-Type" content="text/html; charset=ISO-88591">
<title>Bücherliste</title>
</head>
<body>
<div id="main">
<h1>Willkommen: <%=
SecurityContextHolder.getContext().getAuthentication().getName()
%></h1>
<h2>Hier sehen Sie alle verfügbaren Bücher</h2>
<br>
<table>
<tr><th>Buch</th><th>Autor</th></tr>
<c:forEach items="${books}" var="book">
<tr><td><c:out value="${book.description}"/></td>
<td><c:out value="${book.autor}"/></td>
</tr>
</c:forEach>
</table>
</div>
</body>
</html>
user.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
57
Anhang B: Quellcode
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"
%>
<%@ page
import="org.springframework.security.context.SecurityContextHolder" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel="stylesheet" href="./style/style.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-88591">
<title>Bibliothek: Userverwaltung</title>
</head>
<body>
<div id="main">
<h1>Willkommen: <%=
SecurityContextHolder.getContext().getAuthentication().getName()
%></h1>
Was möchten Sie tun?<br>
<a href="admin/userlist.htm">Benutzerliste einsehen</a><br>
<a href="SpringApp/j_acegi_logout">Logout</a>
</div>
</body>
</html>
userlist.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"
%>
<%@ page
import="org.springframework.security.context.SecurityContextHolder" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel="stylesheet" href="./style/style.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-88591">
<title>Bibliothek: Benutzer</title>
</head>
<body>
<div id="main">
<h1>Willkommen: <%=
SecurityContextHolder.getContext().getAuthentication().getName()
%></h1>
<h2>Hier sehen Sie eine Liste aller Benutzer</h2>
<br>
<table>
<tr><th>ID</th><th>Username</th><th>Vorname</th>
<th>Nachname</th></tr>
<c:forEach items="${users}" var="user">
<tr><td><c:out value="${user.id}"/></td>
<td><c:out value="${user.username}"/></td>
<td><c:out value="${user.firstname}"/></td>
<td><c:out value="${user.lastname}"/></td>
</tr>
</c:forEach>
58
Anhang B: Quellcode
</table>
</div>
</body>
</html>
59
Literaturverzeichnis
[FaSc97]
Fayad, Mohamed E.; Schmidt, Douglas C.: Object-Oriented Application
Frameworks. ACM, 40, S. 32-38, ACM, 1997.
[Fo04]
Fowler, Martin: Inversion of Control Containers and the Dependency
Injection Pattern. http://martinfowler.com/articles/injection.html.
Abrufdatum: 28.09.2008.
[GHJV04]
Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John:
Entwurfsmuster - Elemente wiederverwendbarer objektorientierter
Software. Addison Wesley Verlag, 2004.
[Wa08]
Walls, Craig: Spring im Einsatz. Hanser Verlag, 2008.
[Wo07]
Wolff, Eberhard: Spring 2. dpunkt, 2007.
Herunterladen