Java:动态加载同一类的多个版本

Posted

技术标签:

【中文标题】Java:动态加载同一类的多个版本【英文标题】:Java: Dynamically Load Multiple Versions of Same Class 【发布时间】:2010-12-14 21:53:00 【问题描述】:

我想做的是加载一组类,可能都在同一个文件夹中。所有这些都实现相同的接口并且是相同的类,然后在我的代码中我希望能够在这些类上调用函数。

【问题讨论】:

如果不使用自定义类加载器玩游戏,这是不可能的(那时甚至可能不可能)。也许如果您解释了您要完成的工作,您会得到更多帮助。 OSGI 看起来很有趣,但也许我错误地处理了这个问题。在不太一般的意义上,这就是我想要完成的事情。我有一个运行游戏机制的游戏 Oware 的外壳。它通过向其他类发送游戏状态并返回一个动作来要求其他类做出动作。我想要一个包含不同代码的 AI 文件夹,其中可能有很多。然后,shell 将对它找到的所有 AI 进行循环赛,并输出哪些 AI 战胜了哪些。 【参考方案1】:

根据您对我的问题的回答,您似乎想要定义一个游戏界面,然后插入任意数量的 AI 实现,可能是从 .properties 文件配置的。这是对 API 接口的相当标准的使用。

您定义一个 EngineInterface 提供一个接受游戏状态并返回移动的方法。然后定义多个类,它们都实现了 EngineInterface。您的驱动程序读取属性文件以获取实现类的名称,使用 Class.forName() 实例化它们并将它们存储在列表和/或映射中。然后,当驱动程序收到请求时,它会依次调用每个实现并跟踪结果。

【讨论】:

【参考方案2】:

您是否尝试过类似的方法:

class Move;   // some data type that is able to represent the AI's move.

interface AI 

    Move getMove( GameState state);
;

AIOne implements AI;
AITwo implements AI;

每个类都会实现自己的算法来生成移动,但会被调用但被通用方法调用

【讨论】:

【参考方案3】:

你可以用 OSGI 做你想做的事,但你也可以使用自定义的类加载器。这个想法是您必须为要加载的类的每个版本实例化一个类加载器。 Here你可以找到一个很好的解释。

但我认为您真正需要解决问题的是基于 Jim Garrison 或 Dave L Delaney 所描述的接口...

【讨论】:

【参考方案4】:

    如果你可以使用 OSGI,它就像 弹响指!在 oSGI 中,您可以 有多个相同的版本 班级。你所做的就是拥有相同的 不同版本的捆绑包。

    否则,您仍然可以编写自定义类加载器来读取这两个类。一种方法是这样的。您编写了两个 ClassLoader,其中一个加载该类的一个版本,另一个加载该类的另一个版本。现在根据需要选择classloader1 或classloader2 来加载类。所以现在你也可以在内存中同时加载同一个类的多个版本。

注意:确保这确实是您想要做的,可能有其他方法可以解决您的问题。

【讨论】:

@jason...我没听懂你:) “就像打个响指一样简单”...这是关于 OSGi 的大胆声明。 @thilo...是的,我想是的,也许我已经习惯了osgi...似乎我的潜意识在这里玩游戏:)【参考方案5】:

我知道的唯一支持你所追求的框架是OSGI

本文“Exposing the boot classpath in OSGi”中描述的网络模型确实允许这样做

网络模型的副作用(或目标)之一是类型隔离或类版本控制:同一类的多个版本可以在同一 VM 内很好地共存,因为每个版本都加载到自己的网络、自己的空间中。

请参阅此tutorial 开始并选择 OSGI 框架的 eof(如 Equinox、Knoplerfish 或 Apache Felix)

【讨论】:

【参考方案6】:

可以使用动态类加载来完成。它不是加载不同版本的类,而是加载超类或接口的不同子类。

重要的步骤是:

(1) 使用 Class.forName(...) 按名称加载类。类必须在类路径中。

(2) 使用 aClass.newInstance() 实例化对象。如果构造函数不需要参数,这很容易。

以下代码应该为您提供一些想法。它不处理你必须做的异常。

class Context 
    void moveUp();
    void moveDown();
    ...


interface AI 
    void action(Context con);


public class Game 
    public Game() 
        Context  aContext    = new Context();
        String[] aAIClsNames = this.getAIClassNames("ai.list");
        AI[]     aAIs        = this.loadAI(aAIClsNames);
        this.run(aAIs);
    
    String[] getAIClassNames(String pAIClassListFile) 
        // .. Load the file containning the AI-class file names
    
    AI[] loadAI(String[] pAIClsNames) 
        AI[] AIs = new AI[pAIClsNames.length];
        for(int i = 0; i < pAIClsNames.length; i++) 
            String    aAIClsName       = pAIClsNames[i];

            // (1) Get the class by name
            Class<? extends AI> aAICls = Class.forName(aAIClsName);

            // (2) Notice the cast as all of class in the list must implements AI
            AIs[i] = (AI)aAICls.newInstance();
        
        return AIs;
    
    void run(AI[] pAIs) 
        // ...
    

希望这会有所帮助。

【讨论】:

【参考方案7】:

Jim 的反应很好——您为要使用的类命名,它们都符合通用 API。但是,给出的解决方案假定这些类都已经在应用程序的类路径上可用。您可能希望以后能够添加更多实现,例如安装应用程序后。

如果是这样,那么您可能需要使用自定义类加载器。例如,您可以允许人们将 jar 文件放在某个特定文件夹中的某个位置,并将实现的类名添加到属性文件中。然后,您需要一个自定义类加载器,才能从该文件夹中的 jar 加载类,并且您将使用该类加载器来加载类(例如,使用 Class.forName(className, classLoader))。

事实上,如果您对每个 jar 文件都有一个类加载器,那么您将能够在 jar 文件中拥有多个具有相同名称的类,因为类加载器定义了类名边界。这几乎就是 OSGI 正在做的事情。

以下是一些与从 jar 中加载类相关的代码:

http://sourceforge.net/projects/jcloader/ http://www.javaworld.com/javatips/jw-javatip70.html

【讨论】:

以上是关于Java:动态加载同一类的多个版本的主要内容,如果未能解决你的问题,请参考以下文章

(反射)类的加载(33)

Java 反射理解-- 动态加载类

类的加载机制和反射——使用反射生成JDK动态代理

Java-反射之动态加载类

java反射--动态加载

java类的动态类加载