How does clojure class reloading work?
我一直在阅读代码和文档,以尝试了解Clojure中类重载的工作方式。根据许多网站的介绍,例如http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html,每当您加载类时,基本上就可以获取字节码(通过任何数据机制),将字节码转换为字节码插入类Class的实例(通过defineClass),然后通过resolveClass解析(链接)该类。 (defineClass是否隐式调用resolveClass?)。任何给定的类加载器仅允许一次链接一个类。如果尝试链接现有的类,则不执行任何操作。由于无法链接新实例化的类,因此会产生问题,因此,每次重新加载类时,都必须创建一个新的类加载器实例。
回到clojure,我尝试检查加载类的路径。
在clojure中,您可以根据需要以多种方式定义新类:
匿名类:
整顿
代理
命名类:
deftype
defrecord(在后台使用deftype)
gen-class
最终,这些代码指向clojure / src / jvm / clojure / lang / DynamicClassLoader.java
其中DynamicClassLoader / defineClass使用super的defineClass创建一个实例,然后缓存该实例。当您要检索类时,请通过调用forName调用clojure加载,该调用将调用classloader和DynamicClassLoader / findClass,后者先在高速缓存中查找,然后委派给超类(这与大多数普通类加载器的工作方式相反)混淆的重要点在于:记录了forName在返回类之前链接该类,但这意味着您无法从现有DynamicClassLoader重新加载类,而需要创建一个新的DynamicClassLoader。 ,但是我在代码中看不到这一点。我知道代理和验证定义了匿名类,因此它们的名称不同,因此可以将其视为不同的类。但是,对于命名的类,这会崩溃。在实际的Clojure代码中,您可以同时具有对类的旧版本的引用和对类的新版本的引用,但是尝试创建新的类实例将是新版本。
请解释一下clojure如何能够在不创建DynamicClassLoader新实例的情况下重新加载类,如果我能理解重新加载类的机制,我想将此重新加载功能扩展到我可能使用javac创建的Java .class文件中。
笔记:
这个问题涉及到类RELOADING,而不仅仅是动态加载。重新加载意味着我已经实习了一个类,但想实习该实例的新更新版本。
我想重申一下,尚不清楚clojure如何能够重新加载deftype定义的类。调用deftype最终会导致调用clojure.lang.DynamicClassLoader / defineClass。再次执行此操作将导致另一个对defineClass的调用,但是手动执行此操作会导致链接错误。这下面发生了什么事情,使clojure可以使用deftypes做到这一点?
并非所有这些语言功能都使用相同的技术。
代理
整顿
对于
gen-class
使用
deftype和defrecord
这是真正的动态类重新加载的地方。我对JVM的内部知识并不十分熟悉,但是调试器和REPL的一些工作可以清楚地说明一点:每次需要解析类名时,例如在编译使用该类的代码时或调用类class的
请注意,您在本教程中提到的关于重载类是另一个类的警告,尽管名称相同,但仍适用于Clojure类:
1 2 3 4 5 | (deftype T [a b]) ; define an original class named T (def x (T. 1 2)) ; create an instance of the original class (deftype T [a b]) ; load a new class by the same name (cast T x) ; cast the old instance to the new class -- fails ; ClassCastException java.lang.Class.cast (Class.java:2990) |
Clojure程序中的每个顶级窗体都会获得一个新的DynamicClassLoader,该动态ClassLoader可用于该窗体中定义的任何新类。这不仅包括通过
1 2 3 4 5 | (.getClassLoader (class x)) ;=> #<DynamicClassLoader clojure.lang.DynamicClassLoader@337b4703> (.getClassLoader (class (T. 3 4))) ;=> #<DynamicClassLoader clojure.lang.DynamicClassLoader@451c0d60> |
但是,只要不定义新的
1 2 | (.getClassLoader (class (T. 4 5))) ;=> #<DynamicClassLoader clojure.lang.DynamicClassLoader@451c0d60> |