是否有可能“重置"状态?一个类加载器? [英] Is it possible to "reset" a class loader?

查看:54
本文介绍了是否有可能“重置"状态?一个类加载器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须多次从某些JAR中动态加载一个具有相同名称的类,但实现方式不同.

我正在创建一个评估器后端,我必须动态加载类并对其进行测试.

I'm creating an evaluator backend and I have to dynamically load classes and test them.

测试是JUnit类,它们实例化了应该测试的类,这是一个简单的示例:

The tests are JUnit classes that instantiate the classes which should be tested, this is a simple example:

package evaluator.tests;

import static org.junit.Assert.*;

import org.junit.*;

import evaluator.tested.*;

public class KTest {
    private K tested;

    @Before
    public void setup() {
        tested = new K();
    }

    @Test
    public void returnsTrueTest() {
        assertTrue(tested.returnsTrue());
    }

}

我的应用程序的其余部分将需要从用户那里接收JAR文件,这些文件将包含上面正在测试的K类的实现.然后,KTest必须在 K类上运行,而不是在应用程序中运行.

The rest of my application would need to receive JAR files from users which would contain implementations of the K class which is being tested above. Then, KTest would have to run on their K classes, not the ones in the application.

我知道如何动态加载一个类,但是我不知道如何使用它进行测试,而不是我自己编写的一个测试.

I know how to dynamically load a class, but I don't know how to make a test work with it, and not the one which I made.

我想到的解决方案之一是将测试隔离在一个全新的类中,例如Evaluation,在该类中创建一个新的类加载器,并使其加载所有引用的类.创建类加载器后,它将从JAR文件中加载K类.

One of the solutions I came up with was to isolate testing in a completely new class, e.g. Evaluation, create a new class loader in that class, and make it load all referenced classes. After creating the class loader, it would load the K class from the JAR file.

这意味着每次用户提交他的JAR时,都会实例化一个单独的Evaluation,它将创建自己的类加载器并启动JUnit测试.发生这种情况时,测试将使用用户的K实现,而不是默认的.

This would mean that each time a user submits his JAR, a separate Evaluation would be instantiated, it would create its own class loader and start the JUnit test. When that happens, the test would use the user's implementation of K, and not the default one.

这有可能吗,怎么办?

我读到类加载器总是问他们的父母是否已经加载了一个类.这意味着我必须以某种方式刷新"从Evaluation中的JAR文件动态加载的所有类,以便将它们卸载,然后再次加载到另一个Evaluation中.

I read that class loaders always ask their parent whether a class is already loaded. This would mean that I would have to somehow "flush" all the classes that I loaded dynamically from the JAR file in Evaluation, so that they would be unloaded and then loaded again in another Evaluation.

加载类K,测试类K,卸载类K,并以不同的K重复.

Load class K, test class K, unload class K, repeat with different K.

推荐答案

我在此主题上发表了一篇博客文章,其中对问题进行了进一步解释:

I've made a blog post on this subject, in which the problematics is explained a bit further: http://yannbane.com/2014/01/manipulating-java-class-loading-mechanisms/.

经过一些研究,我认为我找到了一种完全实现我想要的方法.我尚未实现此系统(一旦完成,将编辑答案),因此,我希望从经验丰富的人那里获得一些反馈.

After some research I think I've found a way to accomplish exactly what I want. I haven't implemented this system yet (will edit the answer once I do), so I'd like some feedback from people who are more experienced in this regard.

这是我理解URLClassLoader(和一般的classloader)工作方式的方法:

URLClassLoader.loadClass()是当前的类加载器(用于执行方法/类),则会自动被调用.该调用首先被委派给其父类加载器,如果未找到任何内容,它将使用其自己的自定义findClass()将其从其中一个URL中加载. loadClass()在这里(以及类加载逻辑)仅从常规ClassLoader继承.

URLClassLoader.loadClass() gets automatically called if it’s the current classloader (for the method/class executing). The call is first delegated to its parent classloader, and if nothing is found it uses its own custom findClass() to load it off one of the URLs it has. loadClass() here (and the classloading logic) is just inherited from the regular ClassLoader.

此方法(loadClass())的默认行为是先将搜索委派给父级,如果父级找不到该类,则只能调用其自己的findClass().默认情况下(在ClassLoader中)该方法(findClass())未实现,应该由您自己实现,方法是从某个地方(例如,文件或网络)获取类的字节码并调用defineClass()在他们身上.

The default behavior of this method (loadClass()) is to first delegate the search to the parent, and if parent cannot find the class, only then to call its own findClass(). This method (findClass()) is, by default (in ClassLoader), left unimplemented, and you are supposed to implement it yourself, by essentially getting the bytecodes of the class from somewhere (e.g. a file or a network) and calling defineClass() on them.

一旦在某个类上调用defineClass(),则只有这样,您才能注册为该类的类加载器.在所有其他情况下,类加载要么委托给您的父级(矛盾的是,您不是正在加载的类的类加载器),要么抛出ClassNotFoundException.您无法在运行时更改某个类的类加载器,该类加载器在加载后即会设置并保持不变.

Once you call defineClass() on some class, and only then, are you registered as the classloader of this class. In all other cases classloading is either delegated to your parent (and you are not the classloader of the class you are loading, paradoxically), or you throw a ClassNotFoundException. You cannot change the classloader of some class at runtime, it is set once it is loaded, and constant.

我所有的测试类都会尝试getClass().getClassLoader().loadClass()他们引用的所有类-包括我的自定义测试类(这是 all 类的常规行为,不仅仅是我的测试,清楚).只要他们使用标准类以及我应用程序中其他未经测试的类,则应将此类加载方法进一步委托给应用程序类加载器.但是,一旦他们尝试加载要测试的类,就需要获得自己的,专门加载的自定义版本.

All of my test classes will try to getClass().getClassLoader().loadClass() all of the classes that they reference - including my custom test classes (this is the regular behavior of all classes, not just my tests, to be clear). As long as they’re using standard classes, and other classes from my application that are not to-be-tested classes, this classloading method should be delegated further on to the application classloader. However, as soon as they try to load a to-be-tested class, they need to get their own, specifically loaded, custom version of it.

我的应用程序的用例是,用户使用某个类提交了一个JAR,然后在该JAR的类上运行了一个期望该类具有某些方法的测试(使用JUnit) ,然后将结果发送回用户.

The use case of my application is that a user submits a JAR with some class, then a test that expects this class to have certain methods is ran on the class from that JAR (using JUnit), and then the results are sent back to the user.

解决方案如下:

  1. 实现要测试的类的基本版本,以便测试可以在没有任何提交的JAR的情况下编译和运行.这可以(更有可能,应该)通过利用多态性来完成,但是目前还没有计划(这意味着用户最有可能在发送要测试的类之前,在本地扩展该类的基本"版本) ).
  2. 扩展URLClassLoader并重新处理类加载逻辑.
    • 进行默认实现中存在的必要检查(是否已加载该类).
    • 尝试自己查找类,如果您的URL中没有该类,则抛出一个ClassNotFoundException.
    • 如果已经加载了该类,或者刚从某个URL加载了该类,请返回该类.
    • 如果该类以前从未(未通过此类加载器)加载过,也未在其中一个URL中加载,则将搜索委派给您的父级.
    • 如果父级返回该类,则返回该类,否则返回ClassNotFoundException(它实际上将由父级抛出).
  1. Implement basic versions of the to-be-tested classes so that the tests would compile and run without any submitted JARs. This could (more likely, should) be done by leveraging polymorphism, but is not planned at the moment (this means that the user should most likely extend the "basic" version of the class himself, locally, before sending the class to be tested).
  2. Extend URLClassLoader and re-work the classloading logic.
    • Do the necessary checks (is the class already loaded) that exist in the default implementation.
    • Try to findClass yourself, if it’s not in your URLs, throw a ClassNotFoundException.
    • If the class has already been loaded, or if has been loaded just now from some URL, return the class.
    • If the class has neither been loaded before (by this classloader), nor is it in one of the URLs, delegate the search to your parent.
    • If the parent returns the class, return the class, if not, throw ClassNotFoundException (it will actually be thrown by the parent).
  • 实例化自定义URLClassLoader
  • 向其中添加JAR以及特定的测试类
  • 询问它以加载测试类.此时,我的自定义类加载逻辑开始运行,它直接从磁盘加载了我的测试-重新进行测试,而没有委托给其父类加载器.为什么?它在测试类上调用defineClass(),该类将此自定义URLClassLoader设置为测试类的父类
  • 将测试类提供给JUnit,然后将其实例化并开始测试
  • Instantiate the custom URLClassLoader
  • Add the JAR to it and the specific test class as well
  • Ask it to load the test class. At this point my custom classloading logic kicks in, and it loads my test straight from the disk - anew, without delegating to its parent classloader. Why? It calls defineClass() on my test class, which sets this custom URLClassLoader as the parent of the test class
  • Give the test class to JUnit, which then instantiates it and begins testing

我已经说过-我还没有实现这一点,如果您在评论中指出我的错误,我真的很想.

As I’ve said - I haven’t implemented this yet, and I would really like if you’d point out my mistakes in the comments.

我从中收集此信息的资源:

Resources from which I've gathered this information:

  1. http://www.onjava.com/pub/a/onjava/2003/11/12/classloader.html
  2. http://www.devx.com/Java/Article/31614
  3. http://www.onjava.com/pub/a/onjava/2005/01/26/classloading.html
  1. http://www.onjava.com/pub/a/onjava/2003/11/12/classloader.html
  2. http://www.devx.com/Java/Article/31614
  3. http://www.onjava.com/pub/a/onjava/2005/01/26/classloading.html
  4. http://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#loadClass%28java.lang.String,%20boolean%29

这篇关于是否有可能“重置"状态?一个类加载器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆