2009 03 17 Spring101
Upcoming SlideShare
Loading in...5
×
 

2009 03 17 Spring101

on

  • 1,285 views

 

Statistics

Views

Total Views
1,285
Views on SlideShare
1,284
Embed Views
1

Actions

Likes
0
Downloads
8
Comments
0

1 Embed 1

http://www.slideshare.net 1

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    2009 03 17 Spring101 2009 03 17 Spring101 Presentation Transcript

    • Spring Framework Jens Rühmkorf Tech Talk, DLR Köln-Porz, 17. März 2009
    • Inhalt
      • Überblick und Einführung
      • Grundlegende Prinzipien
      • Funktionsweise eines DI-Frameworks am Beispiel
      • Beans & ihre Konfiguration in Spring
    • Überblick und Einführung
    • Spring Framework Geschichte
      • Begonnen 2002/2003 von Rod Johnson und Jürgen Höller
      • Spring 1.0: März 2004
      • Spring 1.2.6: Jolt Productivity Award 2006 (Dr. Dobb‘s Journal)
      • Spring 2.5: November 2007, aktuell 2.5.6
      • Grundlagen und Prinzipien erschienen in Rod Johnsons Buch „Expert One-on-One J2EE Design and Development“
      • Auslöser für die Entwicklung:
      • Hohe Komplexität von Enterprise JavaBeans (EJB bis v2.0)
    • Spring Framework Anforderungen an die Systemumgebung
      • Spring 2.5 läuft auf JavaSE 6 und JavaEE 5
      • Für bestimmte Konfigurationsmechanismen JavaSE 5 nötig (alles alternativ auch ohne JavaSE 5 konfigurierbar)
      • Abwärtskompatibel zu Java 1.4 bzw. Java EE 1.3
      • Spring-IDE 2.1 unterstützt Eclipse 3.4 (Ganymede)
    • Übersicht des Spring Frameworks http://static.springframework.org/spring/docs/2.5.x/reference/
    • Spring Hello (1) Hello World!
      • public class HelloWorld {
      • private String message ;
      • public void setMessage(String message) {
      • this . message = message;
      • }
      • public void hello() {
      • System. out .println( "Hello! " + message );
      • }
      • }
    • Spring Hello (2) Main
      • import org.springframework.context.ApplicationContext;
      • import org.springframework.context.support. ClassPathXmlApplicationContext;
      • public class Main {
      • public static void main(String[] args) {
      • ApplicationContext context = new ClassPathXmlApplicationContext( "beans.xml" );
      • HelloWorld helloWorld = (HelloWorld) context.getBean( "helloWorld" );
      • helloWorld.hello();
      • }
      • }
    • Spring Hello (3) The Glue
      • < beans xmlns = &quot;http://www.springframework.org/schema/beans&quot; xmlns:xsi = &quot; [...] &quot; xmlns:schemaLocation = &quot; [...] &quot; >
      • < bean id = &quot;helloWorld&quot; class = &quot;de.dlr.spring101.hello.HelloWorld&quot; >
      • < property name = &quot;message&quot; value = &quot;TechTalk Time!&quot; />
      • </ bean >
      • </ beans >
    • Spring Hello (4) Ausgabe
      • 17.03.2009 15:06:45 org.springframework.context.support.AbstractApplicationContext prepareRefresh
      • INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@ab50cd: display name [org.springframework.context.support.ClassPathXmlApplicationContext@ab50cd]; startup date [Tue Mar 17 15:06:45 CET 2009]; root of context hierarchy
      • 17.03.2009 15:06:45 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
      • INFO: Loading XML bean definitions from class path resource [beans.xml]
      • 17.03.2009 15:06:46 org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
      • INFO: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext@ab50cd]: org.springframework.beans.factory.support.DefaultListableBeanFactory@2ce908
      • 17.03.2009 15:06:46 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
      • INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2ce908: defining beans [helloWorld]; root of factory hierarchy
      • Hello! TechTalk Time!
    • Grundlegende Prinzipien
    • „ Separation of Concerns “ (Dijkstra, 1974)
    • Inversion of Control (1) Don‘t call us, we‘ll call you!
      • Hollywood-Prinzip: „ Don‘t call us, we‘ll call you! “
        • (R.E. Sweet, SIGPLAN Notices, 1985)
      • IoC-Ansatz ist typisch für Frameworks:
        • „ Frameworks […] emphasize design reuse over code reuse [… leading to] an inversion of control between the application and the software on which it‘s based. “
        • „ When you use a toolkit […] you call the […] code you want to reuse. “
        • „ When you use a framework, you […] write the code it calls. “
        • (Gang of Four, Design Patterns, 1994)
    • Inversion of Control (2) Verwendung in Frameworks
      • Anwendungsentwickler:
        • implementiert Callback -Methoden
        • definiert was zu tun ist
      • Framework:
        • übernimmt die Verantwortung
        • steuert zeitliche Abfolge
        • definiert wann etwas zu tun ist
    • Inversion of Control (3) Bekannte Beispiele
      • Interface ActionListener in Swing
        • Zu implementieren: Methode void actionPerformed(ActionEvent e)
        • Die Java-Runtime ruft die Methode auf bei Eintreten von Event
      • Interface Runnable bei Threads
        • Zu implementieren: Methode public void run()
        • Java-Runtime entscheidet, wann run() aufgerufen wird
    • Inversion of Control (4) Typische Einsatzgebiete
      • Benutzeroberflächen
      • Netzwerkdienste
      • Systemdienste, die System- bzw. Hardwareereignisse überwachen
    • Inversion of Control (5) Konzeptuelle Unterscheidung
      • Dependency Lookup (aktiv)
        • Komponente akquiriert aktiv Referenz
        • „ Dependency“ wird durch Lookup-Mechanismus nachgeschlagen
      • Dependency Injection (passiv)
        • Komponente stellt Methoden zur Injektion bereit
        • Der IoC-Container injiziert die „Dependency“ buchstäblich
        • kurz: DI
      • Bemerkung
        • Fowler benutzt DI synonym zu IoC http://www.martinfowler.com/articles/injection.html
        • Hier wird DI als Spezialfall von IoC aufgefasst, vgl. http://www.picocontainer.org/inversion-of-control-history.html
    • Inversion of Control (5) Elementare Begrifflichkeiten
      • Komponente = Objekt bzw. Teil einer Software, dessen Ressourcen verwaltet wird von einem:
      • Container = Teil einer Software, der Komponenten überwacht
    • Dependency Lookup (1) Dependency Pull
      • Bekannteste Form von IoC
      • Lese Abhängigkeiten von einer (zentralen) Registry
      • public void start(BundleContext context) {
      • this . context = context;
      • }
      • public String getCustomerName( long id) {
      • ServiceReference ref = context .getServiceReference(
      • ILookupService. class .getName());
      • if (ref != null ) {
      • ILookupService lookup = (ILookupService) context .getService(ref);
      • if (lookup != null ) {
      • Customer customer = lookup.findById(id);
      • context .ungetService(ref);
      • return customer.getName();
      • }
      • }
      • return null ;
      • }
      Dependency Lookup (2) Dependency Pull am Beispiel von OSGi zur Kommunikation mit zentraler „Service Registry“ verwende Service schlage Service nach
    • Dependency Lookup (3) Contextualized Dependency Lookup (CDL)
      • Keine zentrale Registry
      • Lookup geschieht mittels eines Container s
      • Container „enthält“ die Ressource / Abhängigkeit
      • Jede interessierte Komponente implementiert ein Interface
      public interface ManagedComponent { public void performLookup(Container container); }
    • Dependency Lookup (4) Contextualized Dependency Lookup (CDL)
      • Abhängigkeit ändert sich im Container
      • Container ruft component.performLookup(this) auf
      • Komponente liest und verarbeitet Abhängigkeit selbst
      public class ContextualizedDependencyLookup implements ManagedComponent { private Dependency dep ; public void performLookup(Container container) { this . dep = (Dependency) container.getDependency( &quot;myDependency&quot; ); } } Aufruf durch Container
    • Dependency Lookup (5) CDL = Interface Injection = Type 1 IoC
      • Alternative Bezeichungen für CDL
        • Interface Injection
        • Type 1 IoC
      • CDL hat Nachteile
        • Jede Komponente muss ein Interface implementieren
        • Das Interface wird vom Container festgelegt
        • Verwendung der Komponente in anderem Kontext nicht möglich
      • Diese Art der Injizierung wird „intrusive“ genannt (intrusive meint hier eindringend, stark eingreifend)
    • Dependency Injection (1) Setter Injection (= Type 2 IoC)
      • Abhängigkeiten werden über Setter injiziert
      • Komponente definiert set-Methoden mit Abhängigkeiten als Parameter
      public class SetterInjection { private Dependency dep ; public void setMyDependency(Dependency dep) { this . dep = dep; } }
    • Dependency Injection (2) Constructor Injection (= Type 3 IoC)
      • Abhängigkeiten werden über Konstruktor injiziert
      • Komponente definiert Konstruktor(en) mit Abhängigkeiten als Parameter
      public class ConstructorInjection { private Dependency dep ; public ConstructorInjection(Dependency dep) { this . dep = dep; } }
    • Funktionsweise eines DI-Frameworks am Beispiel
    • Arbeitsweise des Frameworks Beispiel: ein Reportgenerator
      • Aufgabe: erzeuge Reports in HTML oder PDF
      • Separiere Schnittstelle und Implementierung über ein Interface
      • Dienst wird über einen ReportService bereitgestellt
    • Reportgenerator (1) Problem: Abhängig von Implementierung
      • Problem: Klasse ReportService besitzt interne Abhängigkeit auf Implementierungsklassen (HTML, PDF)
      public class ReportService { private ReportGenerator reportGenerator = new PdfReportGenerator(); public void generateAnnualReport( int year) { String[][] statistics = null ; // Erzeuge Statistiken fuer das Jahr reportGenerator .generate(statistics); } // Rest weggelassen }
    • Reportgenerator (2) Schritt 1: Führe Container ein (UML)
      • Entkopplung, Schritt 1: Führe Container-Klasse ein. Diese Klasse hat (Quelltext-)Abhängigkeiten auf alle Komponenten, die sie überwacht
    • Reportgenerator (3) Schritt 1: Führe Container ein (cont‘d) public class Container { public static Container instance ; private Map<String, Object> components ; public Container() { components = new HashMap<String, Object>(); instance = this ; components .put( &quot;reportGenerator&quot; , new PdfReportGenerator()); components .put( &quot;reportService&quot; , new ReportService()); } public Object getComponent(String id) { return components .get(id); } }
    • Reportgenerator (4) Schritt 2: Verwende Service-Locator Pattern (UML)
      • Entkopplung, Schritt 2: Verwende Service-Locator Pattern. Liegt OSGi zugrunde, Bestandteil von Suns Core J2EE Pattern Catalog.
    • Reportgenerator (5) Schritt 2: Verwende Service-Locator Pattern (cont‘d) public class ServiceLocator { private static Container container = Container. instance ; public static ReportGenerator getReportGenerator() { return (ReportGenerator) container .getComponent( &quot;reportGenerator&quot; ); } } public class ReportService { private ReportGenerator reportGenerator = ServiceLocator. getReportGenerator (); // Rest weggelassen }
    • Reportgenerator (6) Schritt 3: Realisiere DI via Reflection (UML)
      • Entkopplung, Schritt 3: Verwende Konfigurationsdatei um Abhängigkeiten von außen zu definieren.
      • Instanziiere JavaBeans via Reflection.
    • Reportgenerator (7) Schritt 3: Realisiere DI via Reflection (cont‘d) public class Container { // Ausschnitt! private void processEntry(String key, String value) throws Exception { String[] parts = key.split( &quot;&quot; ); if (parts. length == 1) { // erzeuge Komponente Object component = Class. forName (value).newInstance(); components .put(parts[0], component); } else { // injiziere Dependency Object component = components .get(parts[0]); Object reference = components .get(value); PropertyUtils. setProperty (component, parts[1], reference); } } }
    • Reportgenerator (8) Schritt 3: Realisiere DI via Reflection (cont‘d)
      • Konfigurationsdatei: configuration.properties
      • Verwendet BeanUtils aus Apache Commons
      # Definiere neue Komponente &quot; reportGenerator &quot; reportGenerator= de.dlr.spring101.report.step03. HtmlReportGenerator # Definiere neue Komponente &quot; reportService &quot; reportService= de.dlr.spring101.report.step03.ReportService # Injiziere die Komponente &quot; reportGenerator &quot; # in das Attribut &quot; reportGenerator &quot; reportService.reportGenerator= reportGenerator
    • Reportgenerator (9) Schritt 4: Konfiguration mit Spring Beans (UML)
      • Entkopplung, Schritt 4: Verwende Spring Beans
    • Reportgenerator (10) Schritt 4: Konfiguration mit Spring Beans (cont‘d) import org.springframework.context.ApplicationContext; import org.springframework.context.support. ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( &quot;beans.xml&quot; ); ReportService reportService = (ReportService) context.getBean( &quot;reportService&quot; ); reportService.generateAnnualReport(2008); } }
    • Reportgenerator (10) Schritt 4: Konfiguration mit Spring Beans (cont‘d) <? xml version = &quot;1.0&quot; encoding = &quot;UTF-8&quot; ?> < beans xmlns = &quot;http://www.springframework.org/schema/beans&quot; xmlns:xsi = &quot;http://www.w3.org/2001/XMLSchema-instance&quot; xsi:schemaLocation = &quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot; > < bean id = &quot;defaultGenerator&quot; class = &quot;de.dlr.spring101.report.step04.PdfReportGenerator&quot; /> < bean id = &quot;reportService&quot; class = &quot;de.dlr.spring101.report.step04.ReportService&quot; > < property name = &quot;reportGenerator&quot; > < ref local = &quot;defaultGenerator&quot; /> </ property > </ bean > </ beans >
    • Beans & ihre Konfiguration in Spring
    • Begrifflichkeiten Spring und J2EE POJO vs. JavaBean vs. (Spring) Bean
      • POJO (= Plain Old Java Object). Ein POJO ist ein „simples“ Java Objekt, das:
        • Keine vorgegebenen Klassen erweitert,
        • keine vorgegebenen Interfaces implementiert,
        • keine vorgegebenen Annotations enthält.
      • JavaBean (J2EE Kontext): Ein JavaBean ist ein POJO das:
        • Öffentlichen Standardkonstruktor besitzt,
        • serialisierbar ist,
        • öffentliche Getter/Setter-Methoden besitzt.
      • Spring Beans:
        • Komponenten heißen in Spring Beans.
        • Beans == POJOs.
      • Bean == POJO mit eindeutiger ID
      • Unterscheidung zweier Typen anhand des scope (= Gültigkeitsbereich)
        • singleton
          • Eine Instanz wird angelegt & referenziert bei jedem Zugriff
        • prototype (nicht- singleton )
          • neue Bean wird jedes Mal erzeugt
          • analog zu new KlassenName()
      • Weiter Typen bei Web- und Portal-Applikationen (ab Spring 2.x): request , session , globalSession
      • Spring legt Beans i.d.R. so spät wie möglich an
      Komponenten in Spring Spring Beans
    • Bean Definition (1) Bekanntmachen von Komponenten
      • Eine Bean Definition macht Spring Komponenten bekannt
      • Schlüsseleigenschaften:
        • class (required): Java Klassenname (fully qualified, d.h. mit Package)
        • id : eindeutiger Bezeichner für dieses Bean
        • Konfigurationsparameter ( scope , init-method etc.)
        • constructor-arg : An den Konstruktor zu übergebende Argumente
        • property : An die Setter bei Erzeugung (durch Spring) zu übergebende Argumente
        • Referenzen: andere (durch Spring verwaltete) Beans können in Konstruktor oder Setter-Methode referenziert werden
      • Bean typischerweise in einer XML-Datei definiert
      • < bean id = &quot;exampleBean&quot; class = &quot;org.example.ExampleBean&quot; >
      • < property name = &quot;beanOne&quot; >
      • < ref bean = &quot;anotherBean&quot; />
      • </ property >
      • < property name = &quot;beanTwo&quot; >
      • < ref bean = &quot;yetAnotherBean&quot; />
      • </ property >
      • < property name = &quot;integerProperty&quot; >
      • < value > 1 </ value >
      • </ property >
      • </ bean >
      Bean Definition (2) Beispiel einer XML-Konfiguration
    • Bean-Definition (3) Zum Beispiel gehörige Klassendefinition
      • public class ExampleBean {
      • private AnotherBean beanOne ;
      • private YetAnotherBean beanTwo ;
      • private int i ;
      • public void setBeanOne(AnotherBean beanOne) {
      • this . beanOne = beanOne;
      • }
      • public void setBeanTwo(YetAnotherBean beanTwo) {
      • this . beanTwo = beanTwo;
      • }
      • public void setIntegerProperty( int i) {
      • this . i = i;
      • }
      • }
    • Konfiguration des Spring IoC-Containers BeanFactory vs. Application Context
      • Konfiguration des IoC-Containers via
        • XML
        • Property-Dateien
        • Direkter Zugriff auf die Spring-API
      • BeanFactory und ApplicationContext sind zwei abstrakte Interfaces
      • ApplicationContext erweitert BeanFactory (und weitere Interfaces)
      • Ist für die meisten Anwendungsfälle zu bevorzugen
    • Initialisierung des Spring IoC-Containers Lokalisieren, Erzeugen und Verwalten von Beans
      • Spring verwendet eine BeanFactory (!= FactoryBean) um Beans zu lokalisieren, zu erzeugen und zu verwalten
      • Typischerweise verwendet man eine XML-BeanFactory:
      • ApplicationContext context = new ClassPathXmlApplicationContext( &quot;beans.xml&quot; );
    • Erzeugung von Beans (1) Ablauf
      • Ausgehend von Definitionen legt Spring Abhängigkeits-Digraphen an
      • Topologische Sortierung des Graphen (falls kreisfrei) bestimmt Reihenfolge
        • Typischerweise werden Beans erzeugt bei Laden der Definition
        • Ändere dies durch Attribut lazy-init : < bean class = &quot;className&quot; lazy-init = &quot;true&quot; />
      • Graph darf Kreise enthalten, ABER:
        • Initialisierungsreihenfolge dann abhängig von der Auflösung der Kreise, nicht mehr transparent für den Nutzer
        • Auch zirkuläre Referenzen möglich, sofern die Beans per Constructor und nicht durch eigene Factories initialisert werden (sog. FactoryBean ) Vgl. http://jira.springframework.org/browse/SPR-689
    • Erzeugung von Beans (2) Zeitpunkt der Initialisierung
      • Scope singleton – werden bei Start des Containers angelegt
      • Lazy via lazy-init – initialisiere erst, wenn die abhängige Bean angelegt wird
      • „ Very lazy “ – wird erst bei Zugriff auf Bean (via Container) im Quelltext angelegt. Dazu FactoryBean: LazyBean bean = (LazyBean) context.getBean( &quot;myBean&quot; );
    • Injizierung von Beans Ablauf
      • Ausgangslage ist wieder der Abhängigkeitsgraph
      • Beans werden durch Konstructoren oder Nutzer-spezifizierte Factory-Methoden erzeugt
      • Abhängigkeiten, die nicht bereits durch den Konstruktor angegeben wurde, werden via Setter-Methoden injiziert
      • Auch direktes injizieren via Annotation möglich (dann kein Setter oder Constructor notwendig)
      • Beispiel:
      • @Resource (name = &quot;someOtherBean&quot; )
      • private OtherBean otherBean ;
    • Setzen von Bean-Attributen Bean Properties
      • Häufigste Methode von DI: Injizierung via Properties
      • Injiziert werden kann eine andere Bean, ein Wert (primitiver Datentyp, String), eine Collection
      • < bean id = &quot;exampleBean&quot; class = &quot;de.dlr.spring101.example.ExampleBean&quot; >
      • < property name = &quot;anotherBean&quot; >
      • < ref bean = &quot;someOtherBean&quot; />
      • </ property >
      • </ bean >
      • Kurzschreibweise:
      • < bean id = &quot;exampleBean&quot; class = &quot;de.dlr.spring101.example.ExampleBean&quot; >
      • < property name = &quot;anotherBean&quot; ref = &quot;someOtherBean&quot; />
      • </ bean >
    • JavaBean Antipattern Überprüfen von gesetzten Attributen via Spring
      • Beim Arbeiten mit JavaBeans wird ein „leeres“ Objekt initialisiert, das über Setter „befüllt“ wird
      • Zustand ist erst valide nach Aufruf aller oder mindestens einiger Setter
      • Überprüfung per Konfiguration: none *, simple , objects , all
      • Annotation der Setter-Methoden mit @Resource : (einschalten via < context:annotation-config /> )
      • @Required
      • public void setMessage(String message) {
      • this . message = message;
      • }
      • @Required
      • public void setValue( int value) {
      • this . value = value;
      • }
    • Lifecycle-Management von Beans via Interface
      • Implementiere InitializingBean oder OtherBean :
      • public class LifeCycle implements InitializingBean, DisposableBean {
      • @Override
      • public void afterPropertiesSet() throws Exception {
      • openResource();
      • }
      • @Override
      • public void destroy() throws Exception {
      • closeResource();
      • }
      • }
    • Lifecycle-Management von Beans via Konfiguration oder Annotation
      • Konfiguration:
      • < bean id = &quot;lifeCycleBean&quot; class = &quot;de.dlr.spring101.scratchbook.LifeCycle&quot; init-method = &quot;openResource&quot; destroy-method = &quot;closeResource&quot; >
      • < property name = &quot;message&quot; value = &quot;TechTalk Time!&quot; />
      • </ bean >
      • Annotation:
      • @PreDestroy
      • private void closeResource() {
      • System. out .println( &quot;close resource&quot; );
      • }
      • @PostConstruct
      • private void openResource() {
      • System. out .println( &quot;open resource&quot; );
      • }
    • Zusammenfassung & Ressourcen
    • Spring Core (DI) Alternativen
      • GoogleGuice: http://code.google.com/p/google-guice /
        • unterstützt DI via Annotations
        • viel „ Autowiring “
        • klein & schnell
        • keine Unterstützung von Lifecycles (dazu: GuiceyFruit)
      • PicoContainer: http://www.picocontainer.org /
        • unterstützt DI von Typ 2+3 sowie
        • unterstützt DI mittels Annotations
        • Unterstützung von Lifecycles
        • klein: ~260K, etabliert
      • Weitere Frameworks:
        • Java-Source.net
        • http://java-source.net/open-source/containers
    • Spring Framework Pros and Cons
      • Für Dependency Injection andere starke Frameworks verfügbar
      • Spring bietet viele Module für J2EE-Kontext ( „ Plumbing “ )
      • Neue Konfigurationskonzepte seit Spring 2.5:
        • Annotations (Java SE 5)
        • Autowiring: finde passende Komponenten „automagisch“
      • Spring-Konfiguration üblicherweise sehr explizit (autowiring möglich)
      • Macht das Nachvollziehen (auch für nicht-Experten) einfacher
    • Literatur Webressourcen & Artikel
      • [Tarr et al. 1999] Peri Tarr, Harold Ossher, William Harrison, and Stanley M. Sutton, Jr. N Degrees of Separation: Multi-dimensional Separation of Concerns Intl. Conf. on Software Engineering (ICSE-11, 1999) http://dx.doi.org/10.1145/302405.302457
      • [Foote, and Yoder 1997] Brian Foote, and Joseph Yoder Big Ball of Mud 4th Conf. on Patterns Languages of Programs (PLoP ’97) http://www.laputan.org/mud/
      • [Fowler 2004] Martin Fowler Inversion of Control Containers and the Dependency Injection Pattern martinfowler.com, Januar 2004 http://www.martinfowler.com/articles/injection.html
      • [Weiskotten 2006] Jeremy Weiskotten Dependency Injection & Testable Objects Dr. Dobbs Journal, April 2006 http://www.ddj.com/185300375
    • Literatur Bücher zum Thema
      • [Gamma et al. 1994] Erich Gamma et al. (Gang of Four) Design Patterns Addison Wesley, November 1994
      • [Mak 2008] Gary Mak Spring Recipes Apress, Juni 2008
    • Vielen Dank.