Teil eins. Eingabeaufforderung Muss ich diesen Artikel lesen?
Der Java-Klassenlader ist für den Betrieb des Java-Systems von entscheidender Bedeutung, wird von uns jedoch häufig ignoriert. Der Java-Klassenlader lädt Klassen zur Laufzeit, indem er sie findet und lädt. Benutzerdefinierte Klassenlader können die Art und Weise, wie Klassen geladen werden, völlig ändern und Ihre Java Virtual Machine so personalisieren, wie Sie es möchten. In diesem Artikel wird der Java-Klassenlader kurz vorgestellt und anschließend anhand eines Beispiels für die Erstellung eines benutzerdefinierten Klassenladers veranschaulicht. Dieser Klassenlader kompiliert den Code automatisch, bevor er die Klasse lädt. Sie erfahren, was ein Klassenlader eigentlich macht und wie Sie einen eigenen erstellen. Solange Sie über grundlegende Java-Kenntnisse verfügen, wissen, wie man ein Befehlszeilen-Java-Programm erstellt, kompiliert und ausführt und einige grundlegende Konzepte von Java-Klassendateien kennen, können Sie den Inhalt dieses Artikels verstehen. Nachdem Sie diesen Artikel gelesen haben, sollten Sie in der Lage sein:
* Erweitern Sie die Funktionen der Java Virtual Machine
* Erstellen Sie einen benutzerdefinierten Klassenlader
* So integrieren Sie einen benutzerdefinierten Klassenlader in Ihre Anwendung
* Ändern Sie Ihren Klassenlader so, dass er mit Java 2 kompatibel ist
Teil 2. Einführung Was ist ein Klassenlader?
Der Unterschied zwischen Java und anderen Sprachen besteht darin, dass Java auf der Java Virtual Machine (JVM) ausgeführt wird. Dies bedeutet, dass der kompilierte Code in einem plattformunabhängigen Format gespeichert wird und nicht in einem Format, das auf einem bestimmten Computer ausgeführt wird. Dieses Format weist viele wichtige Unterschiede zum herkömmlichen Format für ausführbaren Code auf. Insbesondere ist ein Java-Programm im Gegensatz zu einem C- oder C++-Programm keine unabhängige ausführbare Datei, sondern besteht aus vielen separaten Klassendateien, wobei jede Klassendatei einer Java-Klasse entspricht. Darüber hinaus werden diese Klassendateien nicht sofort in den Speicher geladen, sondern erst dann, wenn das Programm sie benötigt. Ein Klassenlader ist ein Tool, das in der Java Virtual Machine zum Laden von Klassen in den Speicher verwendet wird. Darüber hinaus ist auch der Java-Klassenlader in Java implementiert. Auf diese Weise können Sie ganz einfach Ihren eigenen Klassenlader erstellen, ohne über tiefgreifende Kenntnisse der Java Virtual Machine zu verfügen.
Warum einen Klassenlader erstellen?
Da Java Virtual Gold bereits über einen Klassenlader verfügt, müssen wir dann selbst andere erstellen. Gute Frage. Der Standard-Klassenlader kann nur Klassen vom lokalen System laden. Wenn Ihr Programm vollständig nativ kompiliert ist, funktioniert der Standard-Klassenlader im Allgemeinen gut. Aber eines der aufregendsten Dinge an Java ist, wie einfach es ist, Klassen aus dem Netzwerk statt nur lokal zu laden.
Beispielsweise kann ein Browser Klassen über einen benutzerdefinierten Klassenlader laden. Es gibt auch viele Möglichkeiten, Klassen zu laden. Eines der aufregendsten Dinge an Java ist, dass Sie es nicht nur lokal oder über das Netzwerk anpassen können:
* Überprüfen Sie digitale Signaturen automatisch, bevor Sie nicht vertrauenswürdigen Code ausführen
* Entschlüsseln Sie den Code basierend auf dem vom Benutzer bereitgestellten Passwort
* Erstellen Sie dynamisch Klassen entsprechend den Benutzeranforderungen und integrieren Sie sie problemlos in Form von Bytecode in Ihre Anwendung, wenn Sie den JDK-Appletviewer (Java Software Development Kit) (kleiner Anwendungsbrowser) verwendet haben
Für eingebettete Java-Browser verwenden Sie bereits einen benutzerdefinierten Klassenlader. Als Sun zum ersten Mal die Java-Sprache veröffentlichte, war es eines der aufregendsten Dinge, zu beobachten, wie Java Code ausführte, der von einer Remote-Website heruntergeladen wurde. Von einer Remote-Site über HTTP ausführen
Der von der P-Verbindung übertragene Bytecode sieht etwas seltsam aus. Dies funktioniert, weil Java die Möglichkeit hat, benutzerdefinierte Klassenlader zu installieren. Der Applet-Browser enthält einen Klassenlader. Dieser findet keine Java-Klassen lokal, sondern greift auf den Remote-Server zu, lädt die ursprüngliche Bytecode-Datei über HTTP und konvertiert sie dann in eine Java-Klasse. Natürlich machen Klassenlader noch viele andere Dinge: Sie blockieren unsichere Java-Klassen und verhindern, dass verschiedene Applets auf verschiedenen Seiten einander stören. Echidna, ein von Luke Gorrie geschriebenes Paket, ist ein offenes Java-Softwarepaket, das die sichere Ausführung mehrerer Java-Anwendungen in einer virtuellen Java-Maschine ermöglicht. Es verhindert Interferenzen zwischen Anwendungen, indem ein benutzerdefinierter Klassenlader verwendet wird, um jeder Anwendung eine Kopie der Klassendatei zu geben.
Unser Beispiel für einen Klassenlader Nachdem wir nun wissen, wie ein Klassenlader funktioniert und wie wir unseren eigenen Klassenlader definieren, erstellen wir einen benutzerdefinierten Klassenlader mit dem Namen CompilingClassLoader (CCL). CCL übernimmt die Kompilierungsarbeit für uns, sodass wir sie nicht selbst manuell kompilieren müssen. Dies entspricht im Grunde einem „Make“-Programm, das in unsere Laufzeitumgebung integriert wird.
Hinweis: Bevor wir mit dem nächsten Schritt fortfahren, müssen wir einige verwandte Konzepte verstehen.
Das System wurde in der JDK-Version 1.2 (die wir als Java-2-Plattform bezeichnen) erheblich verbessert. Dieser Artikel wurde unter JDK 1.0 und 1.1 geschrieben, aber in späteren Versionen wird alles funktionieren. ClassLoader wurde auch in Java2 verbessert.
Eine ausführliche Einführung erfolgt im fünften Teil.
Teil 3. Überblick über die Struktur von ClassLoader Der Hauptzweck eines Klassenladers besteht darin, Anforderungen für Java-Klassen zu bedienen. Wenn die Java Virtual Machine eine Klasse benötigt, gibt sie dem Klassenlader einen Klassennamen und dann versucht der Klassenlader, eine entsprechende Klasseninstanz zurückzugeben. Benutzerdefinierte Klassenlader können erstellt werden, indem die entsprechenden Methoden in verschiedenen Phasen überschrieben werden. Als nächstes lernen wir einige der wichtigsten Methoden des Klassenladers kennen. Sie werden verstehen, was diese Methoden bewirken und wie sie beim Laden von Klassendateien funktionieren. Sie wissen auch, welchen Code Sie schreiben müssen, wenn Sie einen benutzerdefinierten Klassenlader erstellen. Im nächsten Teil nutzen Sie dieses Wissen und unser benutzerdefiniertes CompilingCl
assLoader arbeitet zusammen.
Methode LoadClass
ClassLoader.loadClass() ist der Einstiegspunkt von ClassLoader. Die Methodensignatur lautet wie folgt:
Klasse LoadClass(String-Name, boolesche Auflösung);
Der Parametername gibt den vollständigen Namen der Klasse (einschließlich des Paketnamens) an, die von der Java Virtual Machine benötigt wird, z. B. Foo oder java.lang.Object.
Der Auflösungsparameter gibt an, ob die Klasse aufgelöst werden muss. Sie können die Auflösung der Klasse als vollständig laufbereit verstehen. Eine Analyse ist im Allgemeinen nicht erforderlich. Wenn die Java Virtual Machine nur wissen möchte, ob diese Klasse existiert oder ihre übergeordnete Klasse kennen möchte, ist das Parsen völlig unnötig. Wenn Sie in Java 1.1 und seinen Vorgängerversionen den Klassenlader anpassen möchten, ist die Methode „loadClass“ die einzige Methode, die in der Unterklasse überschrieben werden muss.
(ClassLoader wurde in Java1.2 geändert und stellte die Methode findClass() bereit).
methoddefineClass
defineClass ist eine sehr mysteriöse Methode in ClassLoader. Diese Methode erstellt eine Klasseninstanz aus einem Byte-Array. Dieses Rohbyte-Array mit Daten kann aus dem Dateisystem oder dem Netzwerk stammen. defineClass veranschaulicht die Komplexität, das Mysterium und die Plattformabhängigkeit der Java Virtual Machine – es interpretiert Bytecode, um ihn in Laufzeitdatenstrukturen umzuwandeln, prüft auf Gültigkeit und mehr. Aber keine Sorge, Sie müssen nichts davon tun. Eigentlich kann man es überhaupt nicht außer Kraft setzen,
Weil die Methode durch das Schlüsselwort final geändert wird.
MethodfindSystemClass
Die Methode findSystemClass lädt Dateien vom lokalen System. Es sucht nach Klassendateien auf dem lokalen System und ruft diese auf, wenn sie gefunden werden
defineClass konvertiert das ursprüngliche Byte-Array in ein Klassenobjekt. Dies ist der Standardmechanismus für die Java Virtual Machine zum Laden von Klassen beim Ausführen von Java-Anwendungen. Für benutzerdefinierte Klassenlader müssen wir findSystemClass nur verwenden, wenn der Ladevorgang fehlschlägt. Der Grund ist einfach: Unser Klassenlader ist für die Ausführung bestimmter Schritte beim Laden von Klassen verantwortlich, jedoch nicht für alle Klassen. Zum Beispiel,
Auch wenn unser Klassenlader einige Klassen vom Remote-Standort lädt, müssen immer noch viele Basisklassen vom lokalen System geladen werden.
Diese Klassen sind für uns nicht von Belang, daher lassen wir sie von der Java Virtual Machine auf die Standardmethode laden: vom lokalen System. Dies ist, was findSystemClass tut. Der gesamte Prozess läuft ungefähr wie folgt ab:
* Die Java Virtual Machine fordert unseren benutzerdefinierten Klassenlader an, die Klasse zu laden.
* Wir prüfen, ob die Remote-Site über die Klasse verfügt, die geladen werden muss.
* Wenn ja, erhalten wir diese Klasse.
* Wenn nicht, gehen wir davon aus, dass sich diese Klasse in der Basisklassenbibliothek befindet, und rufen findSystemClass auf, um sie aus dem Dateisystem zu laden.
In den meisten benutzerdefinierten Klassenladern sollten Sie zuerst findSystemClass aufrufen, um Zeit beim Suchen von der Fernbedienung aus zu sparen.
Wie wir im nächsten Abschnitt sehen werden, darf die Java Virtual Machine tatsächlich nur dann Klassen aus dem lokalen Dateisystem laden, wenn wir sicher sind, dass wir unseren Code automatisch kompiliert haben.
Methode „resolveClass“.
Wie oben erwähnt, können Klassendatensätze in teilweises Laden (ohne Analyse) und vollständiges Laden (einschließlich Analyse) unterteilt werden. Wenn wir einen benutzerdefinierten Klassenlader erstellen, müssen wir möglicherweise „resolveClass“ aufrufen.
MethodfindLoadedClass
findLoadedClass implementiert einen Cache: Wenn LoadClass zum Laden einer Klasse erforderlich ist, können Sie diese Methode zunächst aufrufen, um festzustellen, ob die Klasse geladen wurde, um das erneute Laden einer bereits geladenen Klasse zu verhindern. Diese Methode muss zuerst aufgerufen werden. Schauen wir uns an, wie diese Methoden zusammen organisiert sind.
Unsere Beispielimplementierung von LoadClass führt die folgenden Schritte aus. (Wir geben keine bestimmte Technologie an, um die Klassendatei zu erhalten – sie kann aus dem Netzwerk, aus einem komprimierten Paket oder dynamisch kompiliert sein. In jedem Fall erhalten wir die ursprüngliche Bytecode-Datei.)
* Rufen Sie findLoadedClass auf, um zu überprüfen, ob diese Klasse geladen wurde.
* Wenn es nicht geladen wird, erhalten wir irgendwie das ursprüngliche Byte-Array.
* Wenn das Array erhalten wurde, rufen Sie defineClass auf, um es in ein Klassenobjekt zu konvertieren.
* Wenn das ursprüngliche Byte-Array nicht abgerufen werden kann, rufen Sie findSystemClass auf, um zu prüfen, ob es aus dem lokalen Dateisystem aufgezeichnet werden kann.
* Wenn der Parameter „resolve“ wahr ist, rufen Sie „resolveClass“ auf, um das Klassenobjekt aufzulösen.
* Wenn die Klasse nicht gefunden wurde, lösen Sie eine ClassNotFoundException aus.
* Andernfalls wird diese Klasse zurückgegeben.
Da wir nun ein umfassenderes Verständnis der Funktionsweise von Klassenladern haben, können wir einen benutzerdefinierten Klassenlader erstellen. Im nächsten Abschnitt werden wir CCL besprechen.
Teil 4. CompilingClassLoader
CCL zeigt uns die Funktion des Klassenladers. Der Zweck von CCL besteht darin, die automatische Kompilierung und Aktualisierung unseres Codes zu ermöglichen. So funktioniert es:
* Wenn eine Anforderung für eine Klasse vorliegt, prüfen Sie zunächst, ob die Klassendatei im aktuellen Verzeichnis und in den Unterverzeichnissen der Festplatte vorhanden ist.
* Wenn keine Klassendatei, aber eine Quellcodedatei vorhanden ist, rufen Sie den Java-Compiler auf, um die Klassendatei zu kompilieren und zu generieren.
* Wenn die Klassendatei bereits vorhanden ist, prüfen Sie, ob die Klassendatei älter als die Quellcodedatei ist. Wenn die Klassendatei älter als die Quellcodedatei ist, rufen Sie den Java-Compiler auf, um die Klassendatei neu zu generieren.
* Wenn die Kompilierung fehlschlägt oder die Klassendatei aus anderen Gründen nicht aus der Quelldatei generiert werden kann, wird die Ausnahme ClassNotFou ausgelöst
ndException.
* Wenn Sie diese Klasse noch nicht erhalten haben, ist sie möglicherweise in anderen Klassenbibliotheken vorhanden. Rufen Sie findSystemClass auf, um zu sehen, ob sie gefunden werden kann.
* Wenn nicht gefunden, ClassNotFoundException auslösen.
* Andernfalls wird diese Klasse zurückgegeben.
Wie wird die Java-Kompilierung implementiert?
Bevor wir fortfahren, müssen wir den Java-Kompilierungsprozess verstehen. Normalerweise kompiliert der Java-Compiler nur die angegebenen Klassen. Es werden auch andere verwandte Klassen kompiliert, wenn dies für die angegebenen Klassen erforderlich ist. CCL kompiliert die Klassen, die wir in der Anwendung kompilieren müssen, einzeln. Im Allgemeinen gilt jedoch: Nachdem der Compiler die erste Klasse kompiliert hat,
CCL stellt fest, dass tatsächlich andere erforderliche verwandte Klassen kompiliert wurden. Warum? Der Java-Compiler verwendet ähnliche Regeln wie wir: Wenn eine Klasse nicht existiert oder die Quelldatei aktualisiert wurde, wird die Klasse kompiliert. Der Java-Compiler ist CCL grundsätzlich einen Schritt voraus und die meiste Arbeit wird vom Java-Compiler erledigt. Es sieht so aus, als würde CCL diese Klassen kompilieren.
In den meisten Fällen werden Sie feststellen, dass der Compiler in der Hauptfunktionsklasse aufgerufen wird, und das war’s – ein einfacher Aufruf reicht aus. Es gibt jedoch einen Sonderfall, bei dem diese Klassen beim ersten Erscheinen nicht kompiliert werden. Wenn Sie eine Klasse anhand ihres Namens mit der Methode Class.forName laden, weiß der Java-Compiler nicht, ob die Klasse benötigt wird. in diesem Fall,
Sie stellen fest, dass CCL den Compiler erneut aufruft, um die Klasse zu kompilieren. Der Code in Abschnitt 6 veranschaulicht diesen Prozess.
Verwenden von CompilationClassLoader
Um CCL verwenden zu können, können wir unser Programm nicht direkt ausführen, es muss auf eine spezielle Weise ausgeführt werden, wie folgt:
% java Foo arg1 arg2
Wir führen es so aus:
% java CCLRun Foo arg1 arg2
CCLRun ist ein spezielles Stub-Programm, das CompilingClassLoader erstellt und zum Laden unserer Hauptfunktionsklasse verwendet. Dadurch wird sichergestellt, dass das gesamte Programm von CompilingClassLoader geladen wird. CCLRun nutzt Ja
Die VA-Reflection-API ruft die Hauptfunktion der Hauptfunktionsklasse auf und übergibt Parameter an diese Funktion. Weitere Informationen finden Sie im Quellcode in Teil 6.
Lassen Sie uns das Beispiel ausführen, um zu demonstrieren, wie der gesamte Prozess funktioniert.
Das Hauptprogramm ist eine Klasse namens Foo, die eine Instanz der Klasse Bar erstellt. Diese Bar-Instanz erstellt wiederum eine Instanz der Klasse Baz, die im Paket baz vorhanden ist. Dies soll zeigen, wie CCL Klassen aus Unterpaketen lädt. Bar lädt auch die Klasse Boo basierend auf dem Klassennamen
, dies wird auch von CCL durchgeführt. Alle Klassen sind geladen und betriebsbereit. Verwenden Sie den Quellcode aus Kapitel 6, um dieses Programm auszuführen. Kompilieren Sie CCLRun und CompilingClassLoader. Stellen Sie sicher, dass Sie keine anderen Klassen kompilieren (Foo, Bar, Baz usw.).
nd Boo), andernfalls funktioniert CCL nicht.
% java CCLRun Foo arg1 arg2
CCL: Foo.java wird kompiliert...
foo! arg1 arg2
bar! arg1 arg2
baz! arg1 arg2
CCL: Boo.java wird kompiliert...
Buuuuh!
Beachten Sie, dass der Compiler zum ersten Mal für Foo.java aufgerufen wird und Bar und baz.Baz ebenfalls zusammen kompiliert werden. Und wie Boo
Wenn der Kanal geladen werden muss, ruft CCL den Compiler erneut auf, um ihn zu kompilieren.
Teil 5. Übersicht über die Verbesserungen des Klassenladers in Java 2 In Java 1.2 und späteren Versionen wurde der Klassenlader erheblich verbessert. Der alte Code funktioniert immer noch, aber das neue System erleichtert unsere Implementierung. Bei diesem neuen Modell handelt es sich um das Proxy-Delegierungsmodell. Das heißt, wenn der Klassenlader eine Klasse nicht finden kann, fordert er seinen übergeordneten Klassenlader auf, sie zu finden. Der Systemklassenlader ist der Vorfahre aller Klassenlader. Der Systemklassenlader lädt Klassen standardmäßig, also aus dem lokalen Dateisystem. Beim Überschreiben der Methode „loadClass“ werden im Allgemeinen mehrere Möglichkeiten zum Laden der Klasse ausprobiert. Wenn Sie viele Klassenlader schreiben, werden Sie feststellen, dass Sie immer wieder einige Änderungen an dieser komplizierten Methode vornehmen. Die Standardimplementierung von „loadClass“ in Java 1.2 umfasst die gebräuchlichste Methode zum Suchen einer Klasse und ermöglicht Ihnen, die Methode „findClass“ und „loadClass“ zu überschreiben, um die Methode „findClass“ entsprechend aufzurufen. Dies hat den Vorteil, dass Sie die LoadClass nicht überschreiben müssen, sondern nur die FindClass überschreiben müssen, was den Arbeitsaufwand verringert.
Neue Methode: findClass
Diese Methode wird von der Standardimplementierung von LoadClass aufgerufen. Das Ziel von findClass besteht darin, den gesamten Klassenlader-spezifischen Code einzuschließen.
Es ist nicht erforderlich, den Code zu wiederholen (z. B. den Systemklassenlader aufzurufen, wenn die angegebene Methode fehlschlägt).
Neue Methode: getSystemClassLoader
Unabhängig davon, ob Sie die Methoden „findClass“ und „loadClass“ überschreiben, kann die Methode „getSystemClassLoader“ direkt auf den Systemklassenlader zugreifen (anstelle des indirekten Zugriffs über „findSystemClass“).
Neue Methode: getParent
Um die Anforderung an den übergeordneten Klassenlader zu delegieren, kann der übergeordnete Klassenlader dieses Klassenladers über diese Methode abgerufen werden. Sie können die Anforderung an den übergeordneten Klassenlader delegieren, wenn eine bestimmte Methode in einem benutzerdefinierten Klassenlader die Klasse nicht finden kann. Der übergeordnete Klassenlader eines Klassenladers enthält den Code, der den Klassenlader erstellt.
Teil 6. Quellcode
CompilingClassLoader.java
Das Folgende ist der Inhalt der Datei CompilingClassLoader.java
java.io.* importieren;
/*
CompilingClassLoader kompiliert dynamisch Java-Quelldateien. Es prüft, ob die .class-Datei existiert und ob die .class-Datei älter als die Quelldatei ist.
*/
Die öffentliche Klasse CompilingClassLoader erweitert ClassLoader
{
//Geben Sie einen Dateinamen an, lesen Sie den gesamten Dateiinhalt von der Festplatte und geben Sie ein Byte-Array zurück.
privates byte[] getBytes( String filename ) löst eine IOException aus {
// Dateigröße abrufen.
Datei file = neue Datei( Dateiname );
long len = file.length();
//Erstelle ein Array, das gerade ausreicht, um den Inhalt der Datei zu speichern.
byte raw[] = neues Byte[(int)len];
//Datei öffnen
FileInputStream fin = new FileInputStream( file );
// Gesamten Inhalt lesen. Wenn er nicht gelesen werden kann, ist ein Fehler aufgetreten.
int r = fin.read(raw);
if (r != len)
throw new IOException( "Can'''t read all, "+r+" != "+len );
// Vergessen Sie nicht, die Datei zu schließen.
fin.close();
// Dieses Array zurückgeben.
roh zurückgeben;
}
// Generieren Sie einen Prozess zum Kompilieren der angegebenen Java-Quelldatei und geben Sie Dateiparameter an. Wenn die Kompilierung erfolgreich ist, geben Sie true zurück, andernfalls.
// Gibt false zurück.
private boolesche Kompilierung (String javaFile) löst eine IOException aus {
//Aktuellen Fortschritt anzeigen
System.out.println( "CCL: Kompilieren von "+javaFile+"..." );
//Starte den Compiler
Prozess p = Runtime.getRuntime().exec( "javac "+javaFile );
// Warten, bis die Kompilierung beendet ist
versuchen {
p.waitFor();
} Catch( InterruptedException ie ) { System.out.println( ie );
// Überprüfen Sie den Rückkehrcode, um festzustellen, ob Kompilierungsfehler vorliegen.
int ret = p.exitValue();
//Gib zurück, ob die Kompilierung erfolgreich war.
return ret==0;
}
// Der Kerncode des Klassenladers – beim Laden von Klassen werden Quelldateien bei Bedarf automatisch kompiliert.
öffentliche Klasse LoadClass (String-Name, boolesche Auflösung)
wirft ClassNotFoundException {
// Unser Ziel ist es, ein Klassenobjekt zu erhalten.
Klasse class = null;
// Überprüfen Sie zunächst, ob diese Klasse verarbeitet wurde.
clas = findLoadedClass( name );
//System.out.println( "findLoadedClass: "+clas );
// Den Pfadnamen über den Klassennamen abrufen, zum Beispiel: java.lang.Object => java/lang/Object
String fileStub = name.replace( ''''.'''', ''''/'''' );
// Objekte erstellen, die auf Quelldateien und Klassendateien verweisen.
String javaFilename = fileStub+".java";
String classFilename = fileStub+".class";
Datei javaFile = new File( javaFilename );
Datei classFile = new File( classFilename );
//System.out.println( "j "+javaFile.lastModified()+" c "
//+classFile.lastModified() );
// Stellen Sie zunächst fest, ob eine Kompilierung erforderlich ist. Wenn die Quelldatei existiert, aber die Klassendatei nicht existiert, oder wenn beide existieren, aber die Quelldatei
// Neuer, was darauf hinweist, dass es kompiliert werden muss.
if (javaFile.exists() &&(!classFile.exists() ||
javaFile.lastModified() > classFile.lastModified())) {
versuchen {
// Kompilieren, wenn die Kompilierung fehlschlägt, müssen wir den Grund für den Fehler angeben (die bloße Verwendung veralteter Klassen reicht nicht aus).
if (!compile( javaFilename ) || !classFile.exists()) {
throw new ClassNotFoundException( "Kompilierung fehlgeschlagen: "+javaFilename );
}
} Catch(IOException ie) {
// Während der Kompilierung kann ein IO-Fehler auftreten.
throw new ClassNotFoundException(ie.toString());
}
}
// Stellen Sie sicher, dass es korrekt kompiliert wurde oder keine Kompilierung erfordert. Wir beginnen mit dem Laden der Rohbytes.
versuchen {
// Bytes lesen.
byte raw[] = getBytes( classFilename );
//In Klassenobjekt konvertieren
clas = defineClass( name, raw, 0, raw.length );
} Catch(IOException ie) {
// Dies bedeutet nicht, dass es fehlschlägt. Möglicherweise befindet sich die Klasse, mit der wir es zu tun haben, in der lokalen Klassenbibliothek, z. B. java.lang.Object.
}
//System.out.println( "defineClass: "+clas );
//Vielleicht in der Klassenbibliothek, standardmäßig geladen.
if (clas==null) {
clas = findSystemClass( name );
}
//System.out.println( "findSystemClass: "+clas );
// Wenn der Parameter „resolve“ wahr ist, interpretieren Sie die Klasse nach Bedarf.
if (&& clas != null auflösen)
auflösenClass(clas);
// Wenn die Klasse nicht abgerufen wurde, ist ein Fehler aufgetreten.
if (Klasse == null)
throw new ClassNotFoundException( name );
// Andernfalls dieses Klassenobjekt zurückgeben.
Rückgabeklasse;
}
}
CCRun.java
Hier ist die Datei CCRun.java
import java.lang.reflect.*;
/*
CCLRun lädt Klassen über CompilingClassLoader, um das Programm auszuführen.
*/
öffentliche Klasse CCLRun
{
static public void main( String args[] ) löst eine Ausnahme aus {
//Der erste Parameter gibt die Hauptfunktionsklasse an, die der Benutzer ausführen möchte.
String progClass = args[0];
//Die nächsten Parameter sind die Parameter, die an diese Hauptfunktionsklasse übergeben werden.
String progArgs[] = new String[args.length-1];
System.arraycopy( args, 1, progArgs, 0, progArgs.length );
// CompilingClassLoader erstellen
CompilingClassLoader ccl = new CompilingClassLoader();
//Laden Sie die Hauptfunktionsklasse über CCL.
Klasse clas = ccl.loadClass( progClass );
// Verwenden Sie Reflection, um seine Hauptfunktion aufzurufen und Parameter zu übergeben.
// Erzeuge ein Klassenobjekt, das den Parametertyp der Hauptfunktion darstellt.
Klasse mainArgType[] = { (new String[0]).getClass() };
// Finden Sie die Standard-Hauptfunktion in der Klasse.
Methode main = clas.getMethod( "main", mainArgType );
// Erstelle eine Parameterliste – in diesem Fall ein Array von Strings.
Object argsArray[] = { progArgs };
// Rufen Sie die Hauptfunktion auf.
main.invoke( null, argsArray );
}
}
Foo.java
Das Folgende ist der Inhalt der Datei Foo.java
öffentliche Klasse Foo
{
static public void main( String args[] ) löst eine Ausnahme aus {
System.out.println( "foo! "+args[0]+" "+args[1] );
new Bar( args[0], args[1] );
}
}
Bar.java
Das Folgende ist der Inhalt der Datei Bar.java
Import Baz.*;
öffentliche Anwaltskammer
{
public Bar( String a, String b ) {
System.out.println( "bar! "+a+" "+b );
neues Baz( a, b );
versuchen {
Klasse booClass = Class.forName( "Boo" );
Object boo = booClass.newInstance();
} Catch(Ausnahme e) {
e.printStackTrace();
}
}
}
baz/Baz.java
Das Folgende ist der Inhalt der Datei baz/Baz.java
Paket BAZ;
öffentliche Klasse Baz
{
public Baz( String a, String b ) {
System.out.println( "baz! "+a+" "+b );
}
}
Boo.java
Das Folgende ist der Inhalt der Datei Boo.java
öffentliche Klasse Boo
{
publicBoo() {
System.out.println( "Boo!" );
}
}
Teil 7. Zusammenfassung Zusammenfassung Nachdem Sie diesen Artikel gelesen haben, ist Ihnen klar geworden, dass Sie durch die Erstellung eines benutzerdefinierten Klassenladers tief in die Interna der Java Virtual Machine eindringen können. Sie können eine Klassendatei von jeder Ressource laden oder dynamisch generieren, sodass Sie durch Erweitern dieser Funktionen viele Dinge tun können, die Sie interessieren, und Sie können auch einige leistungsstarke Funktionen ausführen.
Weitere Themen zu ClassLoader Wie am Anfang dieses Artikels erwähnt, spielen benutzerdefinierte Klassenlader eine wichtige Rolle in eingebetteten Java-Browsern und Applet-Browsern.