~/blog/2010/07/spring-3-annotationen-statt-xml.md

Spring 3.0: Annotationen statt XML-Wüste

3 min
Titelbild: Spring 3.0: Annotationen statt XML-Wüste

Keine Zeit? Hier die Kurzversion in 30 Sekunden.

  • Spring 3.0 ist seit Dezember 2009 draußen und braucht mindestens Java 5; alles hier läuft mit Java 6.
  • Mit @Component, @Service und @Repository markiert man Klassen, <context:component-scan> findet sie automatisch.
  • @Autowired ersetzt die ref-Einträge in der XML-Datei, bei mehreren Kandidaten hilft @Qualifier.
  • Mit @Configuration und @Bean kann man die Konfiguration komplett in Java schreiben.
  • @Value holt Werte aus Properties-Dateien oder per Ausdruck (SpEL) direkt ins Feld.

Als PHP- und C#-Mensch bin ich gerade dabei, mich in Java einzuarbeiten, und Spring kommt da irgendwie an jeder Ecke vor. Meine ersten Beispiele aus Büchern waren aber ellenlange XML-Dateien, bei denen ich nach zehn Beans den Überblick verloren habe. Seit Spring 3.0 geht das auch kürzer, und das möchte ich euch mal zeigen.

Die klassische Variante

Erst mal zum Vergleich, so sieht eine kleine Konfiguration in XML aus:

<bean id="kundenDao" class="de.beispiel.KundenDaoImpl"/>

<bean id="kundenService" class="de.beispiel.KundenService">
    <constructor-arg ref="kundenDao"/>
    <property name="mailHost" value="smtp.example.org"/>
</bean>

Das ist bei zwei Beans noch harmlos. Bei einem echten Projekt stehen da schnell hundert Einträge, und ein Tippfehler im Klassennamen fällt erst zur Laufzeit auf.

Klassen per Annotation markieren

Stattdessen schreibt man die Information direkt an die Klasse:

@Repository
public class KundenDaoImpl implements KundenDao {
    // ...
}

@Service
public class KundenService {

    private final KundenDao dao;

    @Autowired
    public KundenService(KundenDao dao) {
        this.dao = dao;
    }
}

@Repository, @Service und @Controller sind im Grunde Spezialfälle von @Component. Technisch macht es keinen großen Unterschied, welche ihr nehmt, aber es dokumentiert die Schicht, und @Repository übersetzt zum Beispiel Datenbank-Exceptions in die Spring-eigenen. In die XML-Datei kommt nur noch das hier:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="de.beispiel"/>
</beans>

Spring durchsucht jetzt das Paket de.beispiel samt Unterpaketen und legt für jede markierte Klasse eine Bean an. Der Name ist standardmäßig der Klassenname mit kleinem Anfangsbuchstaben, also kundenService. @Autowired darf übrigens auch auf einem Feld oder Setter stehen. Gibt es zwei Implementierungen desselben Interfaces, weiß Spring nicht, welche gemeint ist, und wirft beim Start eine Exception. Dann hilft @Qualifier("kundenDaoImpl") am Parameter.

Gestartet wird wie gewohnt:

ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
KundenService service = ctx.getBean(KundenService.class);

JavaConfig mit @Configuration und @Bean

Wer gar kein XML mehr will, schreibt die Konfiguration als Java-Klasse. Das ist neu im Kern von Spring 3.0 (vorher gab es das als separates Projekt):

@Configuration
public class AppConfig {

    @Bean
    public KundenDao kundenDao() {
        return new KundenDaoImpl();
    }

    @Bean
    public KundenService kundenService() {
        return new KundenService(kundenDao());
    }
}
ApplicationContext ctx =
    new AnnotationConfigApplicationContext(AppConfig.class);

Cool daran: Der Compiler prüft die Konfiguration mit, und die IDE kann umbenennen. Dass kundenDao() mehrfach aufgerufen wird, ist übrigens kein Problem, Spring fängt das ab und liefert dieselbe Bean zurück. Dafür braucht es CGLIB im Klassenpfad, falls ihr die Fehlermeldung beim Start seht.

Werte per @Value

Zum Schluss noch die Einstellungen. Mit einem Property-Placeholder und @Value kommen sie direkt ins Feld:

<context:property-placeholder location="classpath:app.properties"/>
@Value("${mail.host}")
private String mailHost;

@Value("#{systemProperties['user.home']}")
private String home;

Das erste liest mail.host aus der Properties-Datei, das zweite ist ein Ausdruck in der neuen Spring Expression Language (SpEL) und holt sich eine Systemeigenschaft. Mehr kann SpEL natürlich auch, aber das schaue ich mir ein anderes Mal an.

Ich finde die Annotationen deutlich angenehmer als die XML-Wüste, auch wenn die Konfiguration dann nicht mehr an einer Stelle steht. Wie haltet ihr es, XML oder Annotationen? Schreibt es gern in die Kommentare.

$ whoami
Christian Ackermann

Softwareentwickler (C++, C, Java, C#) aus der Oberpfalz, bloggt seit 2001 über Linux, Technik und Kochen.