Spring 2.x: Grundsätzliches, DI und AOP

+ andere TechDocs
+ Java, JEE, EJB
+ Spring 2.x: DB + TX
+ Spring 2.x: Remote
+ Spring 2.x: MVC
+ Springframework.org
+


Diese Webseite behandelt die mittlerweile veraltete Version Spring 2.x.
Infos zur aktuellen Spring-Version finden Sie unter: Spring-DI-AOP.html.

Das Spring Framework ist ein Framework, welches insbesondere die Programmierung von Java-EE-Anwendungen erleichtert. Spring ist ähnlich wie Java EE eine überwiegend durch Schnittstellen definierte Architektur für Unternehmensanwendungen, aber hat anders als Java EE ein POJO-basierendes Programmiermodell.

Zu den wichtigsten Architekturmerkmalen von Spring zählen:

Einige Vorteile von Spring sind:

In diesem ersten Teil zu Spring geht es um Begriffe im Spring-Umfeld, DI und AOP.

Weitere Spring-Themen finden Sie unter:



Inhalt

  1. Begriffe im Spring-Umfeld
    POJO, IoC, DI, AOP, Pointcut, Advice, Advisor, RMI, Hessian, Burlap, SOAP, TO, DAO, JdbcTemplate, ApplicationContext
  2. Installation
  3. Einfaches Programmierbeispiel für Dependency Injection mit Spring
    MeineHauptBean, MeineBean2, Main, di.xml, Projektverzeichnis, Ausführung, Bemerkungen
  4. Einfaches Programmierbeispiel für AOP
    MeineBean1, MeineBean2, Main, MeinMethodInterceptor, aop.xml, Projektverzeichnis, Ausführung, Bemerkungen
  5. Weitere Spring-Themen
  6. Links auf weiterführende Informationen


Begriffe im Spring-Umfeld

POJO (Plain old Java Objects)

POJOs sind einfache Java-Objekte, meistens ähnlich JavaBeans, also mit Gettern und Settern.

IoC (Inversion of Control)

Bei dem Design Pattern IoC sind Java-Klassen nicht per Java-Code fest verknüpft, sondern werden von einem Framework aufgerufen ("Don't call me, I'll call you.").

DI (Dependency Injection)

DI ist ein von Martin Fowler definiertes zu IoC nahezu identisches Design Pattern. DI hat weniger explizite Abhängigkeiten zum Container-API als IoC. Konfigurationsparameter und zu verwendende andere Beans werden vom Framework zur Laufzeit bei der Instanziierung von Java-Objekten injiziert. Die Konfiguration dazu erfolgt meistens deklarativ in XML-Dateien. Es wird unterschieden zwischen Setter Injection (Injektion über JavaBean-Setters) und Constructor Injection (Injektion über Konstruktorargumente).

Außer in Spring wird DI auch von HiveMind, PicoContainer, Fortress und EJB 3.x unterstützt, aber teilweise eingeschränkter als in Spring, zum Beispiel bei EJB 3.x nicht mit POJOs.

Weiteres zu IoC und DI finden Sie unter: http://martinfowler.com/articles/injection.html.

Weiter unten folgt ein Programmierbeispiel zu DI.

AOP (Aspect oriented Programming)

AOP ermöglicht die Verwaltung und Implementierung allgemeiner querschnittlicher Aspekte (Cross-Cutting Concerns), wie Tracing, Logging, Transaktionssteuerung und Sicherheit, an zentraler Stelle, ohne dass in den vielen betroffenen Java-Klassen Code implementiert werden muss.

Diese Aspekte werden in eigenen Klassen definiert und per Interception integriert. Bei Verwendung von Interfaces wird AOP über Dynamic Proxies implementiert. Ohne Interfaces werden mit Hilfe von CGLIB Subklassen generiert, deren Methoden die Advices und die Originalmethoden aufrufen (letzteres ist nur möglich, wenn die Originalklasse nicht final ist).

Spring implementiert die AOP Alliance Interception Interfaces (siehe http://sourceforge.net/projects/aopalliance).

Weiter unten finden Sie ein Programmierbeispiel zu AOP.

Pointcut, Advice und Advisor

Die per Interception eingeschleusten Aktivitäten heißen bei Spring Advice und lassen sich in vier Arten untergliedern:

- AroundAdvice: als MethodInterceptor rund um Methodenaufrufe mit beliebiger Funktionalität

- BeforeAdvice: vor den Methodenaufrufen

- AfterReturningAdvices: nach den Methodenaufrufen (kann aber Rückgabewerte nicht modifizieren)

- ThrowsAdvices: fängt Exceptions

Diese Advices können in der Spring-Konfigurations-XML-Datei entweder direkt mit bestimmten Beans verknüpft werden oder alternativ mit einem Pointcut zu einem Advisor kombiniert werden, über den dann die Verbindung zu den Beans hergestellt wird.

Während in Advices definiert wird, was geschehen soll, wird in Pointcuts definiert, für welche Beans oder Methoden ein Advice eingesetzt werden soll (z.B. mit JdkRegexpMethodPointcut).

Üblicherweise werden Advices und Pointcuts zu Advisors kombiniert (z.B. DefaultPointcutAdvisor).

Weiter unten im AOP-Beispiel wird dieses Zusammenspiel verdeutlicht.

RMI, Hessian, Burlap und SOAP

Falls die Komponenten auch in einer verteilten Umgebung interagieren sollen, wird ein Netzwerkprotokoll benötigt. Spring ist mit vielen verschiedenen Protokollen verwendbar, zum Beispiel:

- RMI-IIOP: Standardprotokoll für EJBs

- Hessian: HTTP-basierendes Binär-Protokoll

- Burlap: HTTP-basierendes XML-Protokoll

- SOAP: Programmiersprachenunabhängiges, herstellerunabhängiges und standardisiertes Web-Services-Protokoll (z.B. mit Axis und XFire)

Weiteres siehe Remote-Programmierbeispiele.

TO (Transfer Object)

Zur Weitergabe von Daten werden gerne so genannte TO-JavaBeans eingesetzt. Meistens werden die Attribute 'private' deklariert und nur über Getter und Setter angesprochen. Üblich ist auch der Begriff VO (Value Object).

Siehe hierzu auch die Java EE Patterns.

DAO (Data Access Object)

Der Zugriff auf persistente Speicher (meistens Datenbanken) wird vorzugsweise über so genannte DAO-Objekte entkoppelt.

Siehe auch hierzu die Java EE Patterns.

Spring JdbcTemplate

Die Spring JdbcTemplate-Klasse und weitere JDBC- und DAO-Hilfsklassen vereinfachen Datenbankzugriffe:

- Leicht austauschbare Datasourcen

- Connection-Initialisierung und -Beendigung braucht nicht programmiert zu werden

- Datenbankunabhängige vereinheitlichte Sequenzen (siehe DataFieldMaxValueIncrementer)

- Vereinheitlichter Umgang mit BLOBs und Stored Procedures

- Vereinheitlichte Exceptions (abgeleitet von DataAccessException)

Weiteres siehe JdbcTemplate-Programmierbeispiel.

Spring ApplicationContext

Der Spring ApplicationContext (Subinterface von BeanFactory) stellt üblicherweise den Einstiegspunkt in das Spring Framework dar und bietet Unterstützung für:

- Portabler Zugriff auf Dateisysteme, Datenbanken, Transaktionen, Sicherheit und andere Ressourcen

- Event-Mechanismus (Publishing, Registering, Notification)

- Message Lookup inkl. Internationalisierung



Installation

  1. Downloaden Sie ein aktuelles Java SE JDK von http://www.oracle.com/technetwork/java/javase/downloads und installieren Sie es zum Beispiel wie beschrieben unter java-install.htm.
  2. Downloaden Sie das Spring Framework von http://www.springframework.org (z.B. 'spring-framework-2.0.4-with-dependencies.zip').
  3. Legen Sie eine Projektverzeichnisstruktur an, zum Beispiel so:

    [\MeinWorkspace]
      '- [MeinSpringProjekt]
           |- [bin]
           |- [lib]
           |    |- commons-logging.jar
           |    |- spring.jar
           |    '- ... weitere benötigte .jar-Libs
           '- [src]
                |- [beispielaop]
                |- [beispieldb]
                '- [beispieldi]
    

    Kopieren Sie die benötigten .jar-Libraries in das 'lib'-Verzeichnis, insbesondere 'spring.jar' aus 'spring-framework...\dist' und 'commons-logging.jar' aus 'spring-framework...\lib\jakarta-commons' aus dem Spring-Archiv.



Einfaches Programmierbeispiel für Dependency Injection mit Spring

  1. Lesen Sie die Vorbemerkungen zu DI.
  2. Führen Sie die unter Installation genannten Schritte durch.
  3. Erzeugen Sie im Package-Verzeichnis 'beispieldi' folgende JavaBean-Datei 'MeineHauptBean.java':

    package beispieldi;
    
    public class MeineHauptBean
    {
      private MeineBean2 meineBean2;
      private int        meinProperty;
    
      public MeineBean2 getMeineBean2()                  { return meineBean2; }
      public int        getMeinProperty()                { return meinProperty; }
      public void setMeineBean2( MeineBean2 meineBean2 ) { this.meineBean2   = meineBean2; }
      public void setMeinProperty( int meinProperty )    { this.meinProperty = meinProperty; }
    
      public MeineHauptBean()
      {
        System.out.println( "Konstruktor von MeineHauptBean");
      }
    }
    
  4. Erzeugen Sie im Package-Verzeichnis 'beispieldi' folgende JavaBean-Datei 'MeineBean2.java':

    package beispieldi;
    
    public class MeineBean2
    {
      private String meinStr;
    
      public String getMeinStr() { return meinStr; }
      public void setMeinStr( String meinStr ) { this.meinStr = meinStr; }
    
      public MeineBean2()
      {
        System.out.println( "Konstruktor von MeineBean2");
      }
    }
    
  5. Erzeugen Sie im Package-Verzeichnis 'beispieldi' folgende Java-Datei 'Main.java':

    package beispieldi;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main
    {
      public static void main( String[] args )
      {
        System.out.println( "---- ApplicationContext erzeugen ----" );
        ApplicationContext appContext =
          new ClassPathXmlApplicationContext( "beispieldi/di.xml" );
        System.out.println( "---- ApplicationContext fertig ----" );
        MeineHauptBean bn1 = (MeineHauptBean) appContext.getBean( "idMeineHauptBean" );
        MeineBean2     bn2 = bn1.getMeineBean2();
        System.out.println( "bn1.getMeinProperty() = " + bn1.getMeinProperty() );
        System.out.println( "bn2.getMeinStr()      = " + bn2.getMeinStr() );
      }
    }
    
  6. Erzeugen Sie im Package-Verzeichnis 'beispieldi' folgende Spring-Konfigurations-XML-Datei 'di.xml':

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
      "http://www.springframework.org/dtd/spring-beans.dtd">
    
    <beans>
    
      <bean id="idMeineHauptBean" class="beispieldi.MeineHauptBean">
        <property name="meinProperty"> <value>42</value> </property>
        <property name="meineBean2"> <ref bean="idMeineBean2"/> </property>
      </bean>
    
      <bean id="idMeineBean2" class="beispieldi.MeineBean2" singleton="true">
        <property name="meinStr"> <value>Hallo injizierter String</value> </property>
      </bean>
    
    </beans>
    
  7. Ihr Projektverzeichnis sollte jetzt in etwa folgendermaßen aussehen:

    [\MeinWorkspace]
      '- [MeinSpringProjekt]
           |- [bin]
           |- [lib]
           |    |- commons-logging.jar
           |    '- spring.jar
           '- [src]
                '- [beispieldi]
                     |- di.xml
                     |- Main.java
                     |- MeineBean2.java
                     '- MeineHauptBean.java
    
  8. Kompilation und Ausführung können folgendermaßen im Kommandozeilenfenster aus dem Projektverzeichnis 'MeinSpringProjekt' heraus erfolgen:

    cd \MeinWorkspace\MeinSpringProjekt

    set CLASSPATH=src;bin;lib/commons-logging.jar;lib/spring.jar

    javac -d bin src/beispieldi/*.java

    java beispieldi.Main

  9. Natürlich können Sie das Projekt auch in Eclipse laden und bearbeiten. Legen Sie das Projektverzeichnis 'MeinSpringProjekt' in Ihrem Eclipse-Workspace-Verzeichnis an und erzeugen Sie darin die genannten Dateien wie oben beschrieben. Laden Sie das Projekt in Eclipse über: 'File' | 'New' | 'Project...' | 'Java Project' | 'Project name = MeinSpringProjekt' | 'Next' | 'Default output folder = MeinSpringProjekt/bin' | 'Finish'. Klicken Sie mit der rechten Maustaste in Eclipse im Package Explorer auf den Projektnamen 'MeinSpringProjekt' und weiter auf: 'Properties' | 'Java Build Path' | 'Libraries'. In der Liste müssen die benötigten .jar-Libraries 'spring.jar' und 'commons-logging.jar' enthalten sein, sonst fügen Sie sie über 'Add JARs...' hinzu. Öffnen Sie 'Main.java', setzen Sie den Textcursor in diese Datei und starten Sie die Anwendung über 'Run' | 'Run as...' | 'Java Application'.
  10. Sie erhalten folgendes Ergebnis:

    Konstruktor von MeineHauptBean
    Konstruktor von MeineBean2
    new ClassPathXmlApplicationContext()
    bn1.getMeinProperty() = 42
    bn2.getMeinStr()      = Hallo injizierter String
    
  11. Bemerkungen:



Einfaches Programmierbeispiel für AOP

  1. Lesen Sie die Vorbemerkungen zu AOP, Pointcut, Advice und Advisor.
  2. Führen Sie die unter Installation genannten Schritte durch.
  3. Erzeugen Sie im Package-Verzeichnis 'beispielaop' folgende JavaBean-Datei 'MeineBean1.java':

    package beispielaop;
    
    public class MeineBean1
    {
      public String meineMethode( String a, String b )
      {
        System.out.println( "MeineBean1.meineMethode("+a+","+b+")" );
        return "r1";
      }
    }
    
  4. Erzeugen Sie im Package-Verzeichnis 'beispielaop' folgende JavaBean-Datei 'MeineBean2.java':

    package beispielaop;
    
    public class MeineBean2
    {
      public String meineMethode( String s )
      {
        System.out.println( "MeineBean2.meineMethode("+s+")" );
        if( null == s ) throw new IllegalArgumentException( "String s ist null." );
        return "r2";
      }
    }
    
  5. Erzeugen Sie im Package-Verzeichnis 'beispielaop' folgende Java-Datei 'Main.java':

    package beispielaop;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main
    {
      public static void main( String[] args )
      {
        ApplicationContext appContext =
          new ClassPathXmlApplicationContext( "beispielaop/aop.xml" );
        System.out.println( "---- ApplicationContext fertig ----\n" );
        MeineBean1 meineBean1 = (MeineBean1) appContext.getBean( "idMeineBean1" );
        MeineBean2 meineBean2 = (MeineBean2) appContext.getBean( "idMeineBean2" );
        meineBean1.meineMethode( "a1", "b1" );
        meineBean2.meineMethode( "s2" );
      }
    }
    
  6. Erzeugen Sie im Package-Verzeichnis 'beispielaop' folgende Java-Datei 'MeinMethodInterceptor.java':

    package beispielaop;
    
    import org.aopalliance.intercept.*;
    
    public class MeinMethodInterceptor implements MethodInterceptor
    {
      public Object invoke( MethodInvocation invocation ) throws Throwable
      {
        System.out.println( "Vorher:" );
        System.out.println( "Method  = " + invocation.getMethod() );
        Object[] args = invocation.getArguments();
        for( int i=0; i<args.length; i++ ) {
          System.out.println( "args["+i+"] = " + args[i] );
        }
        try {
          Object rval = invocation.proceed();
          System.out.println( "Nachher:" );
          System.out.println( "rval    = " + rval );
          return rval;
        } catch (Throwable ex) {
          System.out.println( "Exception:" );
          System.out.println( "Class   = " + ex.getClass() );
          System.out.println( "Message = " + ex.getMessage() );
          throw ex;
        }
      }
    }
    
  7. Erzeugen Sie im Package-Verzeichnis 'beispielaop' folgende Spring-Konfigurations-XML-Datei 'aop.xml':

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
              "http://www.springframework.org/dtd/spring-beans.dtd">
    
    <beans>
    
      <bean id="idMeinAdvice" class="beispielaop.MeinMethodInterceptor" />
    
      <bean id="idMeinPointcut"
            class="org.springframework.aop.support.JdkRegexpMethodPointcut">
        <property name="pattern" value=".*aop.MeineBe.*" />
      </bean>
    
      <bean class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="advice"   ref="idMeinAdvice" />
        <property name="pointcut" ref="idMeinPointcut" />
      </bean>
    
      <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
    
      <bean id="idMeineBean1" class="beispielaop.MeineBean1" />
    
      <bean id="idMeineBean2" class="beispielaop.MeineBean2" />
    
    </beans>
    
  8. Ihr Projektverzeichnis sollte jetzt in etwa folgendermaßen aussehen (ergänzen Sie fehlende .jar-Libraries aus 'spring-framework...\lib\jakarta-commons' aus dem Spring-Archiv):

    [\MeinWorkspace]
      '- [MeinSpringProjekt]
           |- [bin]
           |- [lib]
           |    |- cglib-nodep-2.1_3.jar
           |    |- commons-logging.jar
           |    '- spring.jar
           '- [src]
                '- [beispielaop]
                     |- aop.xml
                     |- Main.java
                     |- MeineBean1.java
                     |- MeineBean2.java
                     '- MeinMethodInterceptor.java
    
  9. Kompilation und Ausführung können folgendermaßen im Kommandozeilenfenster aus dem Projektverzeichnis 'MeinSpringProjekt' heraus erfolgen:

    cd \MeinWorkspace\MeinSpringProjekt

    set CLASSPATH=src;bin;lib/cglib-nodep-2.1_3.jar;lib/commons-logging.jar;lib/spring.jar

    javac -d bin src/beispielaop/*.java

    java beispielaop.Main

  10. Sie erhalten folgendes Ergebnis:

    Vorher:
    Method  = public String beispielaop.MeineBean1.meineMethode(String,String)
    args[0] = a1
    args[1] = b1
    MeineBean1.meineMethode(a1,b1)
    Nachher:
    rval    = r1
    Vorher:
    Method  = public String beispielaop.MeineBean2.meineMethode(String)
    args[0] = s2
    MeineBean2.meineMethode(s2)
    Nachher:
    rval    = r2
    
  11. Ersetzen Sie in 'Main.java' die Zeile 'meineBean2.meineMethode("s2")' durch 'meineBean2.meineMethode(null)', um das Abfangen von Exceptions in 'MeinMethodInterceptor' zu prüfen.
  12. Ersetzen Sie in 'MeinMethodInterceptor.java' das Regex-Pattern 'value=".*aop.MeineBe.*"' durch andere, um nur bestimmte Beans oder Methoden zu definieren.
  13. Natürlich können Sie auch dieses Projekt auch in Eclipse laden und bearbeiten. Verfahren Sie wie oben beschrieben. Achten Sie darauf, dass alle benötigten .jar-Libs im 'Java Build Path' eingetragen sind.
  14. Bemerkungen:



Weitere Spring-Themen

Weitere Spring-Themen finden Sie unter:



Links auf weiterführende Informationen





Weitere Themen: andere TechDocs | EJB | SQL | Hibernate
© 1998-2007 Torsten Horn, Aachen