当前位置: 首页 > 知识库问答 >
问题:

在Java中动态加载模块(类)的最佳方法

惠凯歌
2023-03-14

我目前正在编写一个需要在不同类型的设备上运行的应用程序。我的方法是制作一个“模块化”应用程序,可以根据需要操作的设备动态加载不同的类。

为了使应用程序易于扩展,我的目标是为其他模块(jar或.class文件)分配一个特定路径,使核心程序保持原样。当不同的客户需要不同的模块时(无需为每个客户编译不同的应用程序),这一点至关重要。

这些模块将实现一个公共接口,“核心”应用程序可以使用接口上定义的这些方法,并让单个实现完成工作。按需加载的最佳方式是什么?我曾考虑使用URLClassLoader,但我不知道根据新模式和Java趋势,这种方法是否是最新的,因为我希望避免设计糟糕的应用程序和不推荐的技术。使用JDK 9(只需将模块文件添加到文件夹即可扩展)创建模块化且易于扩展的应用程序的最佳替代方法是什么?

共有3个答案

艾敏学
2023-03-14

有两种情况。

  1. 实现jar位于类路径
    在这种情况下,您可以简单地使用ServiceLoader API(请参阅@pdem答案)
  2. 实现jar不在类路径上让我们假设BankController是您的接口,CoreController是您的实现。
    如果您想从动态路径动态加载其实现,请创建一个新的模块层和加载类。

请参阅以下代码:

        private final BankController loadController(final BankConfig config) {
            System.out.println("Loading bank with config : " + JSON.toJson(config));
            try {
                //Curent ModuleLayer is usually boot layer. but it can be different if you are using multiple layers
                ModuleLayer currentModuleLayer       = this.getClass().getModule().getLayer(); //ModuleLayer.boot();
                final Set<Path> modulePathSet        = Set.of(new File("path of implementation").toPath());
                //ModuleFinder to find modules 
                final ModuleFinder moduleFinder      = ModuleFinder.of(modulePathSet.toArray(new Path[0]));
                //I really dont know why does it requires empty finder.
                final ModuleFinder emptyFinder       = ModuleFinder.of(new Path[0]);
                //ModuleNames to be loaded
                final Set<String>  moduleNames       = moduleFinder.findAll().stream().map(moduleRef -> moduleRef.descriptor().name()).collect(Collectors.toSet());
                // Unless you want to use URLClassloader for tomcat like situation, use Current Class Loader 
                final ClassLoader loader             = this.getClass().getClassLoader();
                //Derive new configuration from current module layer configuration
                final Configuration  configuration   = currentModuleLayer.configuration().resolveAndBind(moduleFinder, emptyFinder, moduleNames);
                //New Module layer derived from current modulee layer 
                final ModuleLayer    moduleLayer     = currentModuleLayer.defineModulesWithOneLoader(configuration, loader);
                //find module and load class Load class 
                final Class<?>       controllerClass = moduleLayer.findModule("org.util.npci.coreconnect").get().getClassLoader().loadClass("org.util.npci.coreconnect.CoreController");
                //create new instance of Implementation, in this case org.util.npci.coreconnect.CoreController implements org.util.npci.api.BankController
                final BankController bankController  = (BankController) controllerClass.getConstructors()[0].newInstance(config);
                return bankController;
            } catch (Exception e) {BootLogger.info(e);}
            return null;
        }

参考号:https://docs.oracle.com/javase/9/docs/api/java/lang/module/Configuration.html

鞠修雅
2023-03-14

听起来您可能想使用ServicerLoader接口,该接口自Java 6以来就已可用。然而,请记住,如果您想使用Spring依赖项注入,这可能不是您想要的。

闾丘选
2023-03-14

除了@SeverityOne提供的ServicerLoader用法之外,您还可以使用模块信息。java来声明接口的不同实例,使用“uses”/“provides”关键字。

然后使用模块路径而不是类路径,它加载包含模块的所有目录,无需创建特定的类加载器

serviceLoader用法:

public static void main(String[] args) {
    ServiceLoader<IGreeting> sl = ServiceLoader.load(IGreeting.class);
    IGreeting greeting = sl.findFirst().orElseThrow(NullPointerException::new);
    System.out.println( greeting.regular("world"));
}

在用户项目中:

module pl.tfij.java9modules.app {
    exports pl.tfij.java9modules.app;
    uses pl.tfij.java9modules.app.IGreeting;
}

在提供者项目中:

module pl.tfij.java9modules.greetings {
    requires pl.tfij.java9modules.app;
    provides pl.tfij.java9modules.app.IGreeting
            with pl.tfij.java9modules.greetings.Greeting;
}

最后是CLI的使用

java --module-path mods --module pl.tfij.java9modules.app

这里有一个例子;Github示例(感谢“tfij/”存储库初始示例)

编辑时,我意识到存储库已经提供了解耦示例:https://github.com/tfij/Java-9-modules---reducing-coupling-of-modules

 类似资料:
  • 问题内容: 在Java中,我可以向类路径中动态添加内容并加载类(“动态”的意思是无需重新启动应用程序)。是否有一个已知的框架/库可以处理模块的动态加载/卸载而无需重新启动? 通常的设置是负载平衡器,应用程序的多个实例以及逐步部署和重新启动新版本(尤其是对于Web应用程序)。我正在寻找其他东西- 具有多个服务/插件的应用程序,可能是单实例桌面应用程序,在其中禁用单个服务很便宜,但是关闭或重新启动完整

  • 本文向大家介绍Python动态加载模块的3种方法,包括了Python动态加载模块的3种方法的使用技巧和注意事项,需要的朋友参考一下 1、使用系统函数__import_() 2、使用imp 模块 3、使用exec

  • 问题内容: 我正在尝试动态加载我创建的模块。 现在这可以正常工作: 但是,如果我通过动态导入尝试相同的操作,它将失败。 提供的错误是: 有什么想法吗? 编辑:使用完整范围时(它的工作原理?): 这不会引发任何错误,但是,它不会加载索引模块,而是会加载“ neoform”模块。 “ struct”的结果是: 另外,作为附带的问题,我该如何在动态加载的模块中实例化一个类?(假设所有模块都包含一个通用的

  • 该应用程序是模块化插件架构。即,在完成工作的同时动态加载类。为此,自定义类加载器扩展了类加载器。 问题的本质是在Eclipse中运行应用程序,而在终端(ubuntu)中使用以下代码行(其中目录m/表示模块的位置*. class): 我得到以下信息: 10:07:24085调试主CModuleLoader:findClass:39-运行系统类加载器。线程“main”java中出现异常。朗。反思。在太

  • 问题内容: 我查找了语法并搜索了api,但仍然对该过程感到困惑。我还搜索了Stackoverflow。加载类并从中动态创建对象的正确方法是什么?换句话说,我希望用户指定要创建的对象类型,然后创建该类型的对象。我不需要菜单,因为我希望他们能够选择当前目录中的任何类。 问题答案: 假设该类具有无参数构造函数,则最简单的方法是- 参考-java.lang.Class

  • 问题内容: 好的,我已经搜索了高低位,但是无法可靠地确定Webpack是否可行。 https://github.com/webpack/webpack/tree/master/examples/require.context 似乎表明可以将字符串传递给函数并加载模块… 但是我的尝试没有用:webpack.config.js server.js modules /模块中名为test.js的模块 但是