© Excellent backgrounds/Shutterstock.com
Kolumne: Java-Trickkiste

Classpath-Scan im Eigenbau


Viele moderne Frameworks können zur Initialisierung den Classpath nach annotierten Klassen durchsuchen (z. B. EJB 3, JPA, Spring und sogar Servlet-API 3!). Ich halte diesen Architekturstil für problematisch (Startzeit, Testbarkeit, Security etc.), aber das ist ein Thema für eine andere Kolumne. Und egal wie man zu ihnen steht, Classpath-Scans begegnen einem heutzutage auf Schritt und Tritt – ein Grund, sich die Mechanismen dahinter anzuschauen.

Codeanalyse

Ich persönlich schreibe häufiger Code, der Systeme scannt, um Architekturinformationen aus dem Code zu extrahieren. Das erfüllt einen komplett anderen Zweck als die Scans von Frameworks, aber es beruht auf derselben Technologie.

Nehmen wir ein fiktives Spring-basiertes System an. Dort soll die Konvention gelten, dass alle Serviceklassen auf den Namen Service enden, und nur solche Service-Beans sollten mit der Annotation @Service versehen sein. Zum Überprüfen dieser Konventionen müssen wir für jede Klasse des Systems überprüfen, ob sie den Namenskonventionen für Services genügt und ob sie die @ Service-Annotation hat. Bauen wir also einen Scanner, der das tut.

Wo liegen die Klassen?

Der erste Teil dieses Problems besteht darin, eine Liste aller Klassen im System zu erstellen. Java hat kein API dafür, also müssen wir selbst Hand anlegen. Im Allgemeinen sind die Klassen auf mehrere JAR-Dateien oder Verzeichnisse verteilt. Die Information, was alles zu unserem System gehört, steht in den Build- und Deploy-Skripten. Wenn alle Stricke reißen, können wir diese auswerten. Es gibt aber einen einfacheren Weg, alle Stellen zusammenzusuchen, an denen Code liegt. Dazu müssen wir den Analysecode als Teil der Anwendung starten, z. B. aus der IDE heraus (Listing 1).

Listing 1

public List<URL> getRootUrls () { List<URL> result = new ArrayList<> (); ClassLoader cl = Thread.currentThread().getContextClassLoader(); while (cl != null) { if (cl instanceof URLClassLoader) { URL[] urls = ((URLClassLoader) cl).getURLs(); result.addAll (Arrays.asList (urls)); } cl = cl.getParent(); } return result; }

Diese Methode nimmt an, dass alle Klassen des Systems von einem einzigen Class Loader geladen werden. Das gilt z. B. für Spring-Anwendungen und einfache Main-Klassen, die aus der IDE gestartet werden. Für EJB- und OSGi-Anwendungen gelten kompliziertere Regeln, aber wir beschränken uns hier auf den einfachen Fall.

Die meisten Container verwenden die JDK-Klasse URLClassLoader direkt oder eine Subklasse von ihr, und das macht sich de...

Exklusives Abo-Special

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang