Eine Fachkomponentenarchitektur mit Spring 3.0/3.1 – Part Two: Ressourcen

1 Kommentar

Im ersten Blogpost dieser Reihe habe ich die Fachkomponentenarchitektur vorgestellt und beschrieben, welche Unterprojekte benötigt werden und welche Funktion sie haben. Für die Konfiguration greife ich dabei auf die Java-basierte Konfiguration von Spring 3 zurück.
Eine Fachkomponente benötigt natürlich in aller Regel Zugriff auf Ressourcen, sei es eine Datenbank oder eine Messaging Middleware, und in aller Regel unterscheiden sich diese Ressourcen je nach Umgebung. In diesem Blogpost geht es um die umgebungsabhängige Einbindung dieser Ressourcen.

In den meisten Unternehmen greifen viele Komponenten auf dieselben Ressourcen zu. Bei Datenbanken beispielsweise hat das den Vorteil eines vereinfachten Transaktionsmanagements, wenn nur auf eine DataSource zugegriffen wird. Auch bei Messaging Middleware gibt es meist einen Provider, der die Queues bereitstellt.
Es ist also sinnvoll, wenn ein übergreifendes (Architektur-)Team diese Ressourcen so bereitstellt, dass sie direkt verwendet, aber ebenso leicht erweitert und ausgetauscht werden können. Diese Komponenten sind im Gegensatz zu den Fachkomponenten reine Infrastrukturkomponenten. Ich unterscheide hier Low-Level-Data-Access-Komponenten und High-Level-Data-Access-Komponenten.

Low-Level-Data-Access-Komponenten und Spring’s Bean Definition Profiles

Für die Einfachheit des Beispiels betrachte ich nur den Datenbankzugriff, aber es sollte kein Problem sein, die Konzepte auf andere Ressourcen zu erweitern.
Es wird ein Interface für den Datenzugriff bereitgestellt:

public interface LowLevelDataAccessConfig {
 
	public DataSource dataSource() throws Exception;
 
	public PlatformTransactionManager transactionManager();
 
}

Das verantwortliche Architekturteam liefert Default-Implementierungen zu diesem Interface wie die folgende:

@Profile("websphere")
@Configuration
public class JndiDataAccessConfig implements LowLevelDataAccessConfig {
 
	@Bean
	public DataSource dataSource() throws Exception {
		InitialContext initialContext = new InitialContext();
		return (DataSource) initialContext
				.lookup("java:comp/env/jdbc/datasource");
	}
 
	@Bean
	public PlatformTransactionManager transactionManager() {
		return new WebSphereUowTransactionManager();
	}
 
}

Wenn man sich die Bean-Definitionen ansieht, so wird schnell klar, dass diese Konfiguration für die Verwendung in einem Websphere-Applicationserver erstellt wurde. Das mag in unserem Beispielunternehmen der definierte Standard für die Produktionsumgebung sein.
Interessant ist diese Zeile:

@Profile("websphere")

Hier kommen die in Spring 3.1 neu eingeführten Bean Definition Profiles ins Spiel, für mehr Informationen dazu siehe diesen Artikel. Durch die Annotation @Profile auf Klassenebene geben wir an, dass die Bean-Definitionen nur dann dem ApplicationContext hinzugefügt werden sollen, wenn das Profil websphere aktiviert ist. Dieses Profil ist ein unternehmensweit definiertes Default-Profil. Eine Aktivierung kann beispielsweise über das Setzen einer JVM-Property mit dem Namen spring.profiles.active erfolgen.
Eine weitere Implementierung, die bereitgestellt wird, ist die folgende:

@Profile("standalone")
@Configuration
public class StandaloneDataAccessConfig implements LowLevelDataAccessConfig {
 
	@Bean
	public DataSource dataSource() {
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setUrl("someURL");
		dataSource.setUsername("username");
		dataSource.setPassword("password");
		return dataSource;
	}
 
	@Bean
	public PlatformTransactionManager transactionManager() {
		return new DataSourceTransactionManager(dataSource());
	}
 
}

Diese Konfiguration stellt die Ressourcen auch außerhalb eines Application-Servers zur Verfügung, wenn das Profil standalone aktiviert ist. Anmerkung: URL, Username und Passwort sollten natürlich in eine Properties-Datei ausgelagert werden, das betrachte ich im nächsten Blogpost dieser Reihe.

High-Level-Data-Access-Komponenten

Low-Level-Data-Access-Komponenten wie eine DataSource sollten nicht direkt von Fachkomponenten verwendet werden. Hier kommen die High-Level-Data-Access-Komponenten ins Spiel, die die Low-Level-Data-Access-Komponenten verwenden. Im folgenden Beispiel wird ein JdbcTemplate als High-Level-Komponente definiert, allerdings könnten hier auch andere, selbst geschriebene Komponenten stehen, die spezielle Anforderungen erfüllen.

@EnableTransactionManagement
@Import({ JndiDataAccessConfig.class, StandaloneDataAccessConfig.class })
@Configuration
public class HighLevelDataAccessConfig {
 
	@Autowired
	private LowLevelDataAccessConfig dataConfig;
 
	@Bean
	public JdbcTemplate jdbcTemplate() throws Exception {
		return new JdbcTemplate(dataConfig.dataSource());
	}
}

Was passiert hier? Gehen wir die einzelnen Elemente einmal durch.

@EnableTransactionManagement

@EnableTransactionManagement sorgt dafür, dass @Transactional – Annotationen in Komponenten für das Transaktionsmanagement ausgewertet werden.

@Import({ JndiDataAccessConfig.class, StandaloneDataAccessConfig.class })

@Import sorgt dafür, dass die beiden angegebenen Konfigurationsklassen importiert werden. Das geschieht allerdings nur, wenn eines der in den Klassen angegebenen Profile aktiviert ist.

	@Autowired
	private LowLevelDataAccessConfig dataConfig;

Aus dem ApplicationContext wird eine Konfiguration per Autowiring in die Instanzvariable geladen, die das Interface LowLevelDataAccessConfig implementiert. Es muss genau ein solches Konfigurationsobjekt geben, ansonsten kann der ApplicationContext nicht erzeugt werden. In unserem Fall ist es also entweder die JndiDataAccessConfig bei Aktivierung des Profils websphere, die StandaloneDataAccessConfig bei Aktivierung des Profils standalone oder eine beliebige andere Konfiguration, die LowLevelDataAccessConfig implementiert und dem ApplicationContext hinzugefügt wurde.

Verwendung der High-Level-Data-Access-Komponenten in Fachkomponenten

Im ersten Teil der Reihe habe ich eine Partner-Fachkomponente zum Laden von Partnern definiert, den Datenzugriff aber ausgespart. Schauen wir uns die Implementierung dieser Komponente mit Datenzugriff an:

public class PartnerServiceImpl implements PartnerService {
 
	private JdbcTemplate jdbcTemplate;
 
	public PartnerServiceImpl(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
 
	@Override
	public Partner getPartner(long id) {
		return this.jdbcTemplate.queryForObject("SELECT ....",
				new PartnerRowMapper(), id);
	}
 
}

Die Konfigurationsklasse sieht dann so aus:

@Import(HighLevelDataAccessConfig.class)
@Configuration
public class PartnerConfig {
 
	@Autowired
	private HighLevelDataAccessConfig dataAccessConfig;
 
	@Bean
	public PartnerService partnerService() throws Exception {
		return new PartnerServiceImpl(dataAccessConfig.jdbcTemplate());
	}
 
}

Navigation in Konfigurationen

Ohne Probleme können wir uns von der InkassoConfig

in die PartnerConfig und von dort in die HighLevelDataAccessConfig begeben.

In der HighLevelDataAccessConfig können wir nicht mehr direkt weiter navigieren, da es nun einige Auswahlmöglichkeiten gibt.

Diese einfache Art der Navigation durch Konfigurationen ist mit XML nicht möglich.

Fazit

Fachkomponenten benötigen Ressourcen. Die bekommen sie, indem sie allgemein definierte Infrastrukturkomponenten einbinden und benutzen. Einstiegspunkt dazu ist eine high level Konfiguration, die je nach aktiviertem Profil automatisch die passenden low level Ressourcen injiziert bekommt. Für alle Standardfälle sind die low level Ressourcen klar definiert, so dass sie out-of-the-box verwendet werden können. Für Nicht-Standardfälle ist es aber sehr einfach, andere low level Ressourcen einzubinden. Wenn immer der Weg über das Interface LowLevelDataAccessConfig gegangen wird, wird sichergestellt, dass in einem ApplicationContext immer nur eine bestimmte low level Ressourcenkonfiguration enthalten ist.
Im folgenden Blogpost behandle ich das Thema Properties:
Eine Fachkomponentenarchitektur mit Spring 3.0/3.1 – Teil 3: Properties

Tobias Flohre

Tobias Flohre arbeitet als Senior-Softwareentwickler/Architekt bei der codecentric AG. Seine Schwerpunkte sind Java-Enterprise-Anwendungen und Architekturen mit JEE/Spring. Er ist Autor diverser Artikel und schreibt regelmäßig Blogbeiträge zu den Themen Architektur und Spring. Zurzeit beschäftigt er sich mit Integrations- und Batch-Themen im Großunternehmen sowie mit modernen Webarchitekturen.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.