Dieser Artikel bietet Ihnen eine detaillierte Einführung in das dynamische Laden und Neuladen von Java. Ich hoffe, dass er für Freunde hilfreich ist.
Klassen können in Java zur Laufzeit geladen und neu geladen werden, obwohl dies nicht so einfach ist, wie wir denken. In diesem Artikel wird erläutert, wann und wie Klassen in Java geladen und neu geladen werden.
Man kann darüber streiten, ob das dynamische Laden von Klassen Teil der Java-Reflexion oder Teil des Java-Kerns ist. Wie dem auch sei, ich habe es in Java Reflection abgelegt, da ich keinen besseren Ort dafür habe.
Alle Klassen in einem Java-Programm werden mithilfe einiger Unterklassen von java.lang.ClassLoader geladen. Daher müssen dynamisch geladene Klassen auch eine Unterklasse von java.lang.ClassLoader verwenden.
Wenn eine Klasse geladen wird, werden auch die Klassen geladen, auf die sie verweist. Der Klassenlademodus lädt rekursiv, bis alle erforderlichen Klassen geladen sind. Dies sind möglicherweise nicht alle Klassen für die Anwendung. Nicht referenzierte Klassen werden erst geladen, wenn auf sie verwiesen wird.
Das Laden von Klassen in Java ist in Hierarchien organisiert. Wenn Sie einen eigenständigen ClassLoader erstellen, müssen Sie einen übergeordneten ClassLoader bereitstellen. Wenn ein ClassLoader aufgefordert wird, eine Klasse zu laden, fordert er seinen übergeordneten ClassLoader auf, diese zu laden. Wenn der übergeordnete Klassenlader die Klasse nicht finden kann, versucht der untergeordnete Klassenlader, sie selbst zu laden.
Die Schritte für einen Klassenlader zum Laden einer Klasse sind wie folgt:
Überprüfen Sie, ob die Klasse geladen wurde
Wenn die Klasse nicht geladen ist, fordern Sie den Lader der übergeordneten Klasse auf, sie zu laden.
Wenn der Lader der übergeordneten Klasse die Klasse nicht laden kann, versuchen Sie, sie zu laden aktuellen Klassenlader, um ihn zu laden
Wenn Sie einen Klassenlader implementieren, der Klassen überladen kann, müssen Sie ein wenig von dieser Reihenfolge abweichen. Der übergeordnete Klassenlader sollte nicht aufgefordert werden, die neu zu ladende Klasse zu laden. Mehr dazu später.
Das dynamische Laden von Klassen ist sehr einfach. Sie müssen lediglich einen ClassLoader besorgen und dessen Methode „loadClass()“ aufrufen. Ein Beispiel ist:
public class MainClass { public static void main(String[] args){ ClassLoader classLoader = MainClass.class.getClassLoader(); try { Class aClass = classLoader.loadClass("com.jenkov.MyClass"); System.out.println("aClass.getName() = " + aClass.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
Das dynamische Neuladen von Klassen bringt einige Herausforderungen mit sich. Der in Java integrierte Klassenlader prüft vor dem Laden immer, ob die Klasse geladen wurde. Daher ist es nicht möglich, eine Klasse mit dem in Java integrierten Klassenlader neu zu laden. Um eine Klasse neu zu laden, müssen Sie Ihre eigene ClassLoader-Unterklasse implementieren.
Auch bei benutzerdefinierten Unterklassen von Klassenladern gibt es Herausforderungen. Alle geladenen Klassen müssen verknüpft werden. Diese Methode ist endgültig und kann daher nicht von Ihren ClassLoader-Unterklassen überschrieben werden. Die Methode „resolve()“ lässt nicht zu, dass eine ClassLoader-Instanz zweimal eine Verknüpfung zu einer Klasse herstellt. Daher müssen Sie jedes Mal, wenn Sie eine Klasse neu laden müssen, eine Instanz der ClassLoader-Klasse neu erstellen. Es ist nicht unmöglich, aber man muss wissen, wann die Klasse so gestaltet werden muss, dass sie neu geladen wird.
Wie oben erwähnt, kann der ClassLoader, der die angegebene Klasse lädt, nicht zum Neuladen dieser Klasse verwendet werden. Daher muss zum Laden dieser Klasse ein anderer ClassLoader verwendet werden. Allerdings entstehen dadurch neue Probleme.
Jede in einem Java-Programm geladene Klasse wird durch ihren vollständig qualifizierten Namen (Paketname + Klassenname) identifiziert und von einer ClassLoader-Instanz geladen. Dies bedeutet, dass sich die vom Klassenlader A geladene Klasse MyObject von der gleichen vom Klassenlader B geladenen Klasse MyObject unterscheidet. Der Simulationscode lautet wie folgt:
MyObject object = (MyObject) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
Beachten Sie, wie die Klasse MyObject im Code als Variable vom Typ Objekt referenziert wird. Dadurch wird die MyObject-Klasse vom Klassenlader geladen, der bereits den residenten Code für diese Klasse geladen hat.
Wenn die myClassReloadingFactory-Objektfabrik MyObject mit einem anderen Klassenlader als dem residenten Code lädt, können Sie die neu geladene Objekttypvariable MyObject nicht in den MyObject-Typ umwandeln. Da die beiden MyObjects von unterschiedlichen Klassenladern geladen werden, werden sie als unterschiedliche Klassen betrachtet, auch wenn sie denselben vollqualifizierten Namen haben. Der Versuch, die Klasse eines Objekts als Referenz auf eine andere Klasse umzuwandeln, löst eine ClassCastException aus.
Es ist möglich, diese Einschränkung zu umgehen, aber Sie müssen Ihren Code auf zwei Arten ändern:
Schnittstelle als Variablentyp verwenden und nur die Implementierungsklasse neu laden
Superklasse als Variablentyp verwenden und nur Unterklassen neu laden
Hier ist der Beispielcode:
MyObjectInterface object = (MyObjectInterface) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
MyObjectSuperclass object = (MyObjectSuperclass) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
Wenn der Variablentyp Schnittstelle oder Superklasse ist, Der obige Code wird normal ausgeführt. Die Schnittstelle oder Oberklasse wird nicht neu geladen, wenn die Implementierung oder Unterklasse neu geladen wird.
Damit der obige Code ordnungsgemäß ausgeführt werden kann, müssen Sie natürlich Ihren eigenen Klassenlader implementieren, damit die Schnittstelle oder Superklasse von ihrer übergeordneten Klasse geladen wird. Wenn Ihr Klassenlader aufgefordert wird, MyObject zu laden, wird er auch aufgefordert, die MyObjectInterface-Schnittstelle oder die MyObjectSuperclass-Klasse zu laden, da diese intern von der MyObject-Klasse referenziert werden. Ihr Klassenlader muss das Laden von Klassen an denselben Klassenlader delegieren, der die Schnittstelle oder Superklasse geladen hat.
上文包含了很多内容。让我们看一下简单的示例。下面是一个简单的ClassLoader子类。注意它如何将类加载委托给它的父类,除了它想要重装的一个类之外。如果类加载被委派给了它的父类,它以后将不能被重新加载。记住,一个类只能被同一个ClassLoader实例加载。
如前所述,这只是一个示例,它显示了类加载器的行为的基本知识。这并不是一个你的类加载器的生产就绪的模板。你的类加载器可能并不仅限于一个类,可能是一个你想要重新加载的类的集合。此外,你也不能硬编码class path。
public class MyClassLoader extends ClassLoader{ public MyClassLoader(ClassLoader parent) { super(parent); } public Class loadClass(String name) throws ClassNotFoundException { if(!"reflection.MyObject".equals(name)) return super.loadClass(name); try { String url = "file:C:/data/projects/tutorials/web/WEB-INF/" + "classes/reflection/MyObject.class"; URL myUrl = new URL(url); URLConnection connection = myUrl.openConnection(); InputStream input = connection.getInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int data = input.read(); while(data != -1){ buffer.write(data); data = input.read(); } input.close(); byte[] classData = buffer.toByteArray(); return defineClass("reflection.MyObject", classData, 0, classData.length); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }
下面是使用MyClassLoader的示例:
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader(); MyClassLoader classLoader = new MyClassLoader(parentClassLoader); Class myObjectClass = classLoader.loadClass("reflection.MyObject"); AnInterface2 object1 = (AnInterface2) myObjectClass.newInstance(); MyObjectSuperClass object2 = (MyObjectSuperClass) myObjectClass.newInstance(); //create new class loader so classes can be reloaded. classLoader = new MyClassLoader(parentClassLoader); myObjectClass = classLoader.loadClass("reflection.MyObject"); object1 = (AnInterface2) myObjectClass.newInstance(); object2 = (MyObjectSuperClass) myObjectClass.newInstance(); }
reflection.MyObject类是由自定义类加载器加载的。注意,它是如何继承一个超类、实现一个接口的。这只是为了这个例子。在你的代码中,只需要两个中的一个,继承超类或实现接口。
public class MyObject extends MyObjectSuperClass implements AnInterface2{ //... body of class ... override superclass methods // or implement interface methods }
Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in das dynamische Laden und Neuladen von Java-Klassen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!