2010-02-01 24 views
16

Also spricht jedes Java-Lehrbuch darüber, wie flexibel Java ist, da es Klassen zur Laufzeit laden kann. Einfach eine Schnur zusammenbasteln und sie an Class.forName() übergeben, und die ClassNotFoundException fangen und damit umgehen. So viel zur Theorie.Wofür wird die Klassenladung verwendet?

Können Sie Beispiele dafür geben, wie Sie das Laden von Java-Klassen genutzt haben, um eine Funktion zu erreichen, die sonst nicht möglich oder einfach gewesen wäre? Beachten Sie, dass ich bin nicht fragen "was für großartige Dinge könnte wir tun?" - Ich suche nach realen Beispielen, sei es eine Open-Source-Anwendung oder - wenn Sie dies beschreiben können, ohne zu viele Details zu geben - eine proprietäre Anwendung.

Bearbeiten: Sicher, die VM lädt Klassen träge, wie es sie braucht. Das ist eine Sache hinter den Kulissen, solange ich sicher bin, dass alle Klassen, die ich jemals brauchen werde, da sind. Wie gehe ich mit ClassNotFoundException um? Angenommen, ich habe zehn Seiten Text geschrieben, und die Klasse PrinterDriver kann nicht gefunden werden.

+0

Hibernate und AspectJ sind Beispiele für das Erstellen von Klassen im laufenden Betrieb. –

Antwort

10

Plugins ist die erste Sache, die in den Sinn kommt. Das Laden von Java-Klassen macht es im Vergleich zu Sprachen wie C++ sehr einfach.

Ein Punkt, der Ihnen vielleicht nicht bewusst ist, ist, dass jede Java Virtual Machine intern stark auf das Laden von Klassen angewiesen ist. Jedes Mal, wenn der Bytecode-Interpreter einen Verweis auf eine Methode sieht, prüft er, ob die Klasse, zu der die Methode gehört, bereits geladen ist. Wenn dies nicht der Fall ist, wird er mit dem gleichen Mechanismus hinter Class.forName() geladen, bevor die Methode aufgelöst wird. Dieser Mechanismus ist sehr leistungsfähig, da jede Java-Anwendung tatsächlich als ein Satz austauschbarer Komponenten fungiert, die alle dynamisch geladen sind. Wenn die VM gut geschrieben ist, kann sie beispielsweise Klassen über einen benutzerdefinierten Klassenlader laden, der Klassen aus dem Netzwerk anstelle von regulären Dateien abruft.

Die Ladezeit der Klasse hängt von der Implementierung der virtuellen Maschine ab, aber die meisten verlassen sich auf diesen späte Bindungsmechanismus, der eine Klasse lädt, wenn die VM das erste Mal darauf trifft.

+0

Nun, GKrellM, in C implementiert, tut Plugins gut. Legen Sie sie in Ihr ~/.gkrellm2/plugins/-Verzeichnis und starten Sie GKrellM erneut, um sie zu finden (es kann auch jedes Mal nach neuen Plugins suchen, wenn das Konfigurationsdialogfeld geöffnet wird). – doppelfish

+0

Es ist sicherlich - mein Punkt ist, dass die Implementierung von Plugins mit Klassenlade viel einfacher und portabler ist als die traditionelle dynamische Bibliothek laden von C-und C++ - Programme verwenden - ich versuche nicht zu sagen, dass Sie mehr als in C (und ja, GKremmM macht es super!). – Gnurou

3

Nun, ich habe es zum dynamischen Laden von JDBC-Treibern in eine J2EE-Anwendung verwendet. Ob das besser hätte gemacht werden können, weiß ich nicht.

Es war einfach zu der Zeit, den forName() Anruf zu tun.

+0

Eigentlich habe ich noch nie einen anderen Weg gesehen, einen JDBC-Treiber zu bekommen. Dort muss ein besserer Weg sein, aber ich habe es nicht gesehen. Stell dir das vor. – doppelfish

+1

Eine Alternative besteht darin, den Treiber in der Systemeigenschaft jdbc.drivers festzulegen. –

0

Ich verwende es, wenn ich eine Standardanwendung erstelle, die von mir selbst oder dem Kunden maßgeschneidert werden muss, um die spezifischen Anforderungen des Kunden zu erfüllen.

2

Ich bin ziemlich sicher, dass das Laden von Plugins in Java sehr davon abhängt.

Die Anwendung überprüft für benannte Funktionen und führt sie

Eclipse actually uses that for plugins

Die Schlüsselidee ist, Code auszuführen, die nicht in developpement Zeit geplant war.

2

Es kann sehr nützlich in Situationen sein, in denen Sie eine API verwenden und die API-Designer tatsächlich einige Klassen von einer Version zur nächsten verwarfen (zum Beispiel die in Android).

Ohne Reflektion und dynamisches Laden der Klasse basierend auf dem String-Namen, wäre es in diesem Fall unmöglich, das gleiche Programm auf beiden Versionen der Plattform ausführen zu lassen, ohne dass eine Klasse nicht gefunden wird. Aber damit wurde das gleiche Programm ein wenig optimiert und konnte dann auf beiden Plattformen laufen.

5

"PLUGIN" und das ist das große Wort.

Im Grunde können Sie eine Klasse laden, die Sie nicht kennen, wenn sie nicht existiert oder nicht existiert, wenn Sie Ihr Programm schreiben und kompilieren.

Wenn Sie beispielsweise möchten, dass ein Programm eine Rechtschreibprüfung durchführt, können Sie eine Schnittstelle SpellChecker schreiben und dann eine Klasse aus einer Konfigurationsdatei laden, die die Schnittstelle implementiert. Danach können Sie einen beliebigen SpellChecker schreiben und in der Konfigurationsdatei den tatsächlichen Dateinamen eingeben. Auf diese Weise muss Ihr Programm nicht wissen, welche Klasse die Rechtschreibprüfung durchführt.

DB-Treiber, Eclipse-Plugin, Skriptsprache, Kryptografiemethoden werden alle auf diese Weise ausgeführt, da der ursprüngliche Verfasser nicht weiß (und in einigen Fällen keine Ahnung hat), welche Klasse tatsächlich verwendet wird.

Hoffe, das hilft.

2

Der ClassLoader wird auch für Nicht-Klassenressourcen verwendet. Konfigurationsdateien kommen mir in den Sinn. Da es eine wohldefinierte Suchreihenfolge gibt, ist es einfach, Ihre eigenen "log4j.xml" oder "hibernate.properties" einzutragen, und die Anwendung wird sie finden und verwenden.

1

ich denke, JUnit kann auch eine Menge von Reflexionsfunktionen verwendet, um das Test-Framework generisch zu machen.

-2

Sie können die Class :: forName-Methode verwenden, wenn sich die Klasse im Klassenpfad befindet. Wenn Sie jedoch einen Pfad zusammen mit dem Klassennamen angeben müssen, d. H. C: \ document \ xyz.class, müssen Sie die Klasse URLClassLoader verwenden.

+1

Die Antwort ist nicht falsch, aber sie beantwortet die Frage nicht wirklich. –

0

Siehe die Unterstützung für Oracle LOB handling in Spring Framework zum Beispiel. Nur weil das Framework spezifische Unterstützung für Oracle bietet, möchten Sie wahrscheinlich keine Oracle-Datenquelle als Abhängigkeit für Ihre z. MySQL-Projekte Daher laden Sie die Oracle-Treiber im Rahmen einer Instanz des LOB-Handlers reflektiv.

5

Anwendungsserver verlassen sich auch stark auf ClassLoaders, um die verschiedenen implementierten Module zu isolieren. Z.B.

  • können Sie den gleichen Web-App zweimal unter anderem Weg
  • zwei Anwendungen können auf zwei verschiedene Version derselben Bibliothek ohne Konflikte abhängig einzusetzen.

Dank der Magie der Klassenlader ...

+0

Wahr, aber (1) Ich kann die gleiche Binärdatei zweimal, jedes Mal mit einer anderen Konfiguration, und (2) Ich kann verschiedene Version einer DLL (gemeinsames Objekt) haben, und Binärdateien können mit seiner Lieblingsversion verknüpfen. Das nächste, was du weißt, ist die Hölle. – doppelfish

0

Servlet-Container wie Tomcat lesen Sie Ihre Krieg/Webapp Konfigurationsdatei von WEB-INF/web.xml und laden Sie Ihre Servlet/Filter/etc. Unterklassen basierend auf den String-Werten, die Sie in die XML-Datei eingeben. Bei Datenbankverbindungen ziehen sie den Klassennamen, um ihn von Ihrer Konfiguration zu laden, z. "com.mysql.jdbc.Driver" für MySQL.

2

Ich erinnere mich daran, einen Klassenlader zu erstellen, um Klassen remote zu laden. Die Anwendung wurde auf einem Knoten ausgeführt, während die Klassen auf einem anderen Knoten gespeichert waren.

Und auch durch das Anpassen des Klassenladers können Sie Klassen konvertieren, wie sie geladen werden. Dies wird von einigen ORM-Frameworks sowie einigen AOP-Frameworks verwendet.

0

Real-World Beispiel (wie in Ihrer Frage angefordert), proprietäre Anwendung (wie ausdrücklich durch Ihre Frage erlaubt) ...

Nach dem Start kontaktiert die clientseitige Software unsere Server und sagt "Standardimplementierung der Interface-Leiste, die ich habe, ist Foo (weil tatsächlich jede Version 1.03 zum Beispiel Foo verwendet), haben Sie eine bessere ein?" Wenn wir in der Zwischenzeit eine bessere Implementierung geschrieben haben, antworten wir "yup, Bar ist alt, benutze Buz, es ist besser".

Dann wird auf der Clientseite ein Klassenlader zum Laden der neuesten Implementierung verwendet.

Es ist vereinfacht, aber es ist ein Beispiel der realen Welt. Es ist nicht ganz unähnlich zu dem Beispiel, das JRL erwähnt hat: Wo veraltete Klassen automatisch durch neuere ersetzt werden.

2

Die JDBC API ist ein hervorragendes Beispiel dafür. So können Sie den JDBC-Treiber konfigurieren kann extern in zum Beispiel eine Eigenschaftendatei:

 
driver = com.dbvendor.jdbc.Driver 
url = jdbc:dbvendor://localhost/dbname 
username = stackoverflow 
password = youneverguess 

..welche können Sie verwenden, wie:

Properties properties = new Properties(); 
properties.load(Thread.currentThread().getResourceAsStream("jdbc.properties")); 

String driver = properties.getProperty("driver"); 
String url = properties.getProperty("url"); 
String username = properties.getProperty("username"); 
String password = properties.getProperty("password"); 

Class.forName(driver); 
Connection connection = DriverManager.getConnection(url, username, password); 

Jede JDBC-Treiber Implementierung registriert sich im Grunde in der DriverManager im Inneren ein static Initialisierungsblock. Es ist nämlich derjenige, der während Class#forName() ausgeführt wird.

package com.dbvendor.jdbc; 

public class Driver implements java.sql.Driver { 

    static { 
     java.sql.DriverManager.registerDriver(new Driver()); 
    } 

    private Driver() { 
     // ... 
    } 

    public boolean acceptsURL(String url) { 
     return url.startsWith("jdbc:dbvendor"); 
    } 

} 

Da die DriverManagergrob sieht wie folgt aus (es tatsächlich die altmodische Vector verwendet)

private static final Set<Driver> drivers = new HashSet<Driver>(); 

public static void registerDriver(Driver driver) { 
    drivers.add(driver); 
} 

public static Connection getConnection(String url, String username, String password) throws SQLException { 
    for (Driver driver : drivers) { 
     if (driver.acceptsURL(url)) { 
      return driver.connect(url, username, password); 
     } 
    } 
    throw new SQLException("No suitable driver"); 
} 

... Sie können eine Verbindung, um sie ohne die Notwendigkeit erhalten die Fahrer selbst zu instanziiert !

Auf diese Weise ist der JDBC-Code sehr portabel. Sie können die DB ändern oder den Code unter Benutzern mit unterschiedlichen DBs verteilen, ohne den Code selbst ändern/hacken/neu erstellen zu müssen.

Nicht nur JDBC verwendet diesen Ansatz, sondern auch andere APIs wie Servlet API, ORMs wie Hibernate/JPA, Dependency-Injection-Frameworks usw. Reflektion zum Laden der Klassen basierend auf extern konfigurierbaren Eigenschaftsdateien, XML-Konfigurationsdateien und/oder Anmerkungen. Alles macht den Code viel portabler und steckbarer.

1

Der Java-Classloader-Mechanismus ist mächtig, weil es genau an der Stelle eine Abstraktionspunkt liefert, wenn der Code geladen wird, die Sie Dinge wie tun kann:

  • Finden die Klasse Bits irgendwo anders als der Classpath (db , Remote-uRL, Dateisystem, etc.)
  • Laden Quellcode Sie gerade erstellt und selbst kompiliert (mit dem javac api)
  • Laden Bytecode Sie gerade selbst erzeugt (etwa mit ASM)
  • Ladecode und ABäNDERUNG es bevor Sie i t (mit ASM, Java-Agenten, etc etc)
  • RE-Last Code on the fly
  • Kettenlader zusammen in den Bäumen (normale Delegation) oder Bahnen (Geschwister-basierte OSGi-Stil) oder was auch immer Sie wollen

Um den Code während des Ladens zu modifizieren, gibt es eine ganze Reihe interessanter Dinge, die Sie tun können, um Ihren Code neu zu mischen - AOP, Profiling, Tracing, Verhaltensänderungen, etc. Bei Terracotta haben wir uns auf die Classloader-Abstraktion verlassen, um eine Klasse dynamisch zu laden , dann alle Zugriffe auf Felder abzufangen und dynamisch die Möglichkeit hinzuzufügen, den Status von demselben Objekt später auf einem entfernten Knoten im Cluster zu laden. Cooles Zeug.

0

Die Verwendung der dynamischen Klassenlade ist auch sehr nützlich für das Laden von Konfigurationsdateien, wie Thilo erwähnt. Allgemeiner gesagt, dynamisches Laden von Klassen kann in vielen Situationen eine nette Abstraktionsschicht für Dateisysteme bilden, was Schreibpräferenzen vereinfacht und sensiblen Code einstellt. Stellen Sie nur sicher, dass sich die benötigte Ressource im Klassenpfad befindet und laden Sie sie als InputStream.

Darüber hinaus ist es mit einem benutzerdefinierten Protokollhandler in Java möglich, über URL auf Elemente im Klassenpfad zuzugreifen. Dies ist kein Vorteil für das dynamische Laden von Klassen, aber es demonstriert, wie auf Klassenpfadressourcen über die gleiche API wie andere Ressourcen (auch Remote-Ressourcen) zugegriffen werden kann. http://java.sun.com/developer/onlineTraining/protocolhandlers/

0

Jedes Framework, das konfigurationsbasiert ist (struts, jsf, spring, hibernate usw.), verwendet diesen Mechanismus. Jedes Produkt, das auf der Plugin-Architektur basiert, verwendet diese Funktion ebenfalls.