Java-Tipp 105: Beherrschen des Klassenpfads mit JWhich

Zu der einen oder anderen Zeit sind Entwickler frustriert, wenn sie mit dem Java-Klassenpfad umgehen. Es ist nicht immer klar, welche Klasse der Klassenladeprogramm lädt, insbesondere wenn der Klassenpfad Ihrer Anwendung mit Verzeichnissen und Dateien überschwemmt wird. In diesem Artikel werde ich ein Tool vorstellen, das den absoluten Pfadnamen der geladenen Klassendatei anzeigen kann.

Grundlagen des Klassenpfades

Die Java Virtual Machine (JVM) verwendet einen Klassenlader, um Klassen, die von einer Anwendung verwendet werden, nach Bedarf zu laden. Die CLASSPATHUmgebungsvariable teilt dem Klassenladeprogramm mit, wo sich Klassen von Drittanbietern und benutzerdefinierte Klassen befinden. Sie können den Klassenpfad auch auf Anwendungsbasis mit dem -classpathJVM-Befehlszeilenargument angeben, das den in der CLASSPATHUmgebungsvariablen angegebenen Klassenpfad überschreibt .

Klassenpfadeinträge können Verzeichnisse sein, die Klassendateien für Klassen enthalten, die nicht in einem Paket enthalten sind, das Paketstammverzeichnis für Klassen in einem Paket oder Archivdateien (z. B. ZIP- oder JAR-Dateien), die Klassen enthalten. Klassenpfadeinträge werden auf Unix-Systemen durch Doppelpunkte und auf MS Windows-Systemen durch Semikolons getrennt.

Klassenlader sind in einer Delegierungshierarchie organisiert, wobei jeder Klassenlader einen übergeordneten Klassenlader hat. Wenn ein Klassenladeprogramm aufgefordert wird, eine Klasse zu finden, delegiert er die Anforderung zunächst an sein übergeordnetes Klassenladeprogramm, bevor er versucht, die Klasse selbst zu finden. Der Systemklassenlader, der Standardklassenlader, der von dem auf Ihrem System installierten JDK oder JRE bereitgestellt wird, lädt Klassen von Drittanbietern und benutzerdefinierte Klassen mithilfe der CLASSPATHUmgebungsvariablen oder des -classpathJVM-Befehlszeilenarguments. Der Systemklassenlader delegiert an die Erweiterungsklasse, um Klassen zu laden, die den Java-Erweiterungsmechanismus verwenden. Der Erweiterungsklassenlader delegiert an den Bootstrap-Klassenladeprogramm (das Geld bleibt hier stehen!), Um die JDK-Kernklassen zu laden.

Sie können spezielle Klassenlader entwickeln, um anzupassen, wie die JVM Klassen dynamisch lädt. Beispielsweise verwenden die meisten Servlet-Engines einen benutzerdefinierten Klassenlader, um Servlet-Klassen, die in in einem benutzerdefinierten Klassenpfad angegebenen Verzeichnissen geändert wurden, dynamisch neu zu laden.

Von besonderer Bedeutung und mit großer Bestürzung lädt der Klassenlader Klassen in der Reihenfolge, in der sie im Klassenpfad angezeigt werden. Beginnend mit dem ersten Klassenpfadeintrag besucht der Klassenlader jedes angegebene Verzeichnis oder jede angegebene Archivdatei, um die zu ladende Klasse zu finden. Die erste Klasse, die mit dem richtigen Namen gefunden wird, wird geladen, und alle verbleibenden Klassenpfadeinträge werden ignoriert.

Klingt einfach, oder?

Klassenpfad-Trick

Ob sie es zugeben würden oder nicht, sowohl Anfänger als auch erfahrene Java-Entwickler wurden irgendwann (normalerweise im schlimmsten Moment!) Durch den lästigen Klassenpfad ausgetrickst. Da die Anzahl der abhängigen Klassen von Drittanbietern und benutzerdefinierten Klassen für eine Anwendung zunimmt und der Klassenpfad zu einem Speicherauszug für jede denkbare Verzeichnis- und Archivdatei wird, ist es nicht immer offensichtlich, welche Klasse der Klassenlader zuerst lädt. Dies gilt insbesondere für den unglücklichen Fall, dass der Klassenpfad doppelte Klasseneinträge enthält. Denken Sie daran, dass der Klassenlader die erste ordnungsgemäß benannte Klasse lädt, die er im Klassenpfad findet, und alle anderen ordnungsgemäß benannten Klassen mit niedrigerer Priorität effektiv "verbirgt".

Es ist allzu leicht, diesem Trick auf dem Klassenweg zum Opfer zu fallen. Nach einem langen Tag des Slaves über eine Hot-Tastatur hängen Sie ein Verzeichnis an den Klassenpfad an, um die neueste und beste Version einer Klasse in die Anwendung zu laden, ohne zu wissen, dass sich eine andere Version der Klasse in einem Verzeichnis von befindet höhere Priorität im Klassenpfad. Erwischt!

JWhich: Ein einfaches Klassenpfad-Tool

Das einer Flat-Path-Deklaration inhärente Vorrangproblem betrifft nicht nur den Java-Klassenpfad. Um eine Lösung für das Problem zu finden, müssen Sie nur auf den Schultern legendärer Software-Giganten stehen. Der whichBefehl des Unix-Betriebssystems nimmt einen Namen an und zeigt den Pfadnamen der Datei an, die ausgeführt werden würde, wenn der Name als Befehl ausgegeben würde. Es durchläuft im Wesentlichen die PATHUmgebungsvariable, um das erste Auftreten des Befehls zu lokalisieren. Das klingt auch nach einem leistungsstarken Tool zum Verwalten des Java-Klassenpfads. Inspiriert von dieser Vorstellung machte ich mich daran, ein Java-Dienstprogramm zu schreiben, das einen Java-Klassennamen annehmen und den absoluten Pfadnamen der Klassendatei anzeigen kann, die der Klassenlader laden würde, wie im Klassenpfad vorgeschrieben.

Das folgende Beispiel für die Verwendung von JWhichzeigt den absoluten Pfadnamen des ersten Vorkommens der com.clarkware.ejb.ShoppingCartBeanKlasse an, die vom Klassenladeprogramm geladen werden soll, das sich zufällig in einem Verzeichnis befindet:

 > java JWhich com.clarkware.ejb.ShoppingCartBean Klasse 'com.clarkware.ejb.ShoppingCartBean' gefunden in '/home/mclark/classes/com/clarkware/ejb/ShoppingCartBean.class' 

Das folgende Beispiel für die Verwendung von JWhichzeigt den absoluten Pfadnamen des ersten Vorkommens der javax.servlet.http.HttpServletKlasse an, die vom Klassenladeprogramm geladen werden soll und das zufällig in eine Archivdatei gepackt wird:

 > java JWhich javax.servlet.http.HttpServlet Klasse 'javax.servlet.http.HttpServlet' gefunden in 'Datei: /home/mclark/lib/servlet.jar! /javax/servlet/http/HttpServlet.class' 

Wie funktioniert das?

Um eindeutig zu bestimmen, welche Klasse zuerst in den Klassenpfad geladen wird, müssen Sie sich mit dem Klassenladeprogramm vertraut machen. Das ist nicht so schwierig, wie es sich anhört - fragen Sie einfach! Der relevante Quellcode für JWhichfolgt. Den vollständigen Quellcode finden Sie unter Ressourcen.

1: public class JWhich {2: 3: / ** 4: * Gibt den absoluten Pfadnamen der Klassendatei 5: * aus, der den angegebenen Klassennamen enthält, wie im aktuellen Klassenpfad 6: * vorgeschrieben. 7: * 8: * @param className Name der Klasse. 9: * / 10: public static void which (String className) {11: 12: if (! ClassName.startsWith ("/")) {13: className = "/" + className; 14:} 15: className = className.replace ('.', '/'); 16: className = className + ".class"; 17: 18: java.net.URL classUrl = 19: new JWhich (). GetClass (). GetResource (className); 20: 21: if (classUrl! = Null) {22: System.out.println ("\ nClass '" + className + 23: "' gefunden in \ n '" + classUrl.getFile () + "'"); 24:} else {25: System.out.println ("\ nClass '" + className + 26: "' nicht gefunden in \ n '"+ 27: System.getProperty (" java.class.path ") +" '"); 28:} 29:} 30: 31: public static void main (String args []) {32: if (args.length > 0) {33: JWhich.which (args [0]); 34:} else {35: System.err.println ("Verwendung: java JWhich"); 36:} 37:} 38:}

Zuerst müssen Sie den Klassennamen ein wenig massieren, um die Akzeptanz des Klassenladers zu erreichen (Zeilen 12-16). Wenn Sie dem Klassennamen ein "/" voranstellen, wird der Klassenlader angewiesen, den Klassennamen wörtlich im Klassenpfad abzugleichen, anstatt implizit den Paketnamen der aufrufenden Klasse voranzustellen. Konvertieren jedes Vorkommens von "." to "/" formatiert den Klassennamen als gültigen URL-Ressourcennamen, der vom Klassenlader benötigt wird.

Als nächstes wird der Klassenlader (Zeilen 18-19) nach der Ressource abgefragt, die dem richtig formatierten Klassennamen entspricht. Jedes ClassObjekt enthält einen Verweis auf das ClassLoaderObjekt, das es geladen hat, sodass der Klassenlader, der die JWhichKlasse selbst geladen hat, hier abgefragt wird. Die Class.getResource()Methode delegiert tatsächlich an den Klassenlader, der die Klasse geladen hat, und gibt eine URL zum Lesen der Klassendateiressource zurück oder nullwenn eine Klassendateiressource mit dem angegebenen Klassennamen im aktuellen Klassenpfad nicht gefunden werden konnte.

Schließlich wird der absolute Pfadname der Klassendatei mit dem angegebenen Klassennamen angezeigt, wenn er im aktuellen Klassenpfad gefunden wurde (Zeilen 21-24). Wenn die Klassendatei im aktuellen Klassenpfad nicht gefunden wurde, erhalten Sie als Debugging-Hilfe den Wert der java.class.pathSystemeigenschaft, um den aktuellen Klassenpfad anzuzeigen (Zeilen 24-28).

Es ist leicht vorstellbar, wie dieser einfache Codeabschnitt in einem Java-Servlet mithilfe des Klassenpfads der Servlet-Engine oder in einer Enterprise JavaBean (EJB) mithilfe des Klassenpfads des EJB-Servers aufgerufen werden kann. Wenn die JWhichKlasse beispielsweise vom benutzerdefinierten Klassenlader in einer Servlet-Engine geladen würde, würde der Klassenlader der Servlet-Engine zum Suchen von Klassen verwendet. Wenn der Klassenlader der Servlet-Engine keine Klasse finden kann, wird er an den übergeordneten Klassenlader delegiert. Wenn JWhiches von einem Klassenladeprogramm geladen wird, kann es im Allgemeinen alle Klassen finden, die von seinem Klassenladeprogramm oder einem übergeordneten Klassenladeprogramm geladen wurden.

Fazit

Wenn die Notwendigkeit die Mutter aller Erfindungen ist, ist ein Tool zur Verwaltung des Java-Klassenpfads längst überfällig. Java-bezogene Newsgroups und Mailinglisten sind voller Fragen zum Klassenpfad. Wir müssen die Eintrittsbarriere für neue Entwickler senken, damit wir alle auf höheren Abstraktionsebenen weiterarbeiten können. JWhichist ein einfaches, aber leistungsstarkes Tool, mit dem Sie den Java-Klassenpfad in jeder Umgebung beherrschen können.

Mike Clark ist ein unabhängiger Berater für Clarkware Consulting, der sich auf Java-basierte Architektur, Design und Entwicklung unter Verwendung von J2EE-Technologien spezialisiert hat. Vor kurzem hat er die Entwicklung und Bereitstellung eines B2B-XML-Austauschservers (Business-to-Business) abgeschlossen und ist derzeit Berater für ein Projekt zur Entwicklung eines J2EE-Performance-Management-Produkts.

Erfahren Sie mehr über dieses Thema

  • Obtain the full source code for this article

    //images.techhive.com/downloads/idge/imported/article/jvw/2000/12/jwhich.zip

  • A full-featured version of JWhich, including a classpath validator, is available at

    //www.clarkware.com/software/jwhich.zip

  • Official documentation for the Sun JDK and how it deals with the classpath for the various officially supported platforms is available at

    //java.sun.com/j2se/1.3/docs/tooldocs/findingclasses.html

  • For details on how to set the classpath on Unix and Windows platforms, see "Setting the classpath" at:
  • Unix

    //java.sun.com/j2se/1.3/docs/tooldocs/solaris/classpath.html

  • Windows

    //java.sun.com/j2se/1.3/docs/tooldocs/win32/classpath.html

  • View all previous Java Tips and submit your own

    //www.javaworld.com/javatips/jw-javatips.index.html

  • Abonnieren Sie den kostenlosen Java Tutor- Newsletter von ITworld.com, um weitere Java-Tricks zu erhalten

    //www.itworld.com/cgi-bin/subcontent12.cgi

  • Sprechen Sie in der Java-Anfängerdiskussion, die vom JavaWorld- Autor Geoff Friesen moderiert wird

    //www.itworld.com/jump/jw-javatip105/forums.itworld.com/[email protected]@.ee6b804/1195!skip=1125

Diese Geschichte "Java-Tipp 105: Beherrschen des Klassenpfads mit JWhich" wurde ursprünglich von JavaWorld veröffentlicht.