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:
POJOs sind einfache Java-Objekte, meistens ähnlich JavaBeans, also mit Gettern und Settern.
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 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 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.
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.
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.
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.
Der Zugriff auf persistente Speicher (meistens Datenbanken) wird vorzugsweise über so genannte DAO-Objekte entkoppelt.
Siehe auch hierzu die Java EE Patterns.
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.
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
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.
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"); } }
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"); } }
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() ); } }
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>
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
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
Sie erhalten folgendes Ergebnis:
Konstruktor von MeineHauptBean Konstruktor von MeineBean2 new ClassPathXmlApplicationContext() bn1.getMeinProperty() = 42 bn2.getMeinStr() = Hallo injizierter String
Bemerkungen:
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"; } }
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"; } }
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" ); } }
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; } } }
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>
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
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
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
Bemerkungen:
Weitere Spring-Themen finden Sie unter: