java - 动态加载的 spring 上下文

标签 java spring module classloader hierarchy

所以我有一个正在使用 Spring boot 的项目,并且想要利用模块系统。我希望模块系统能够动态重新加载。我几乎可以正常工作,但 @ComponentScan 在模块中完全不起作用。

有一个模块文件夹,其中包含启动时加载的 jar 文件,需要动态卸载、加载和重新加载。

模块是通过 AnnotationConfigApplicationContext 创建的,上下文的类加载器设置为核心的类加载器,模块接口(interface)中的方法提供的 @Configuration 类通过 context.register 注册。

@Configuration 中的 @Bean 声明适用于 Autowiring ,并且所有类都正确加载。问题是 @ComponentScan 不起作用。我什至尝试使用 @Component 注释一个没有字段或任何内容的类,它会抛出一个 Autowiring 错误,就像它不在上下文中一样。如果这不是我的存储库的问题,这也不是什么大问题。

如果有一种方法可以在没有注释的情况下手动运行组件扫描,我可以做一些额外的工作。

我尝试将 spring boot 包与 @SpringBootApplication(scanPackages = {"package.name"} 一起使用

@EnableJpaRepository @EntityScan

都位于适当的位置,并且具有正确的参数。

我尝试将 .register 与核心上下文一起使用,但不起作用。无论如何,我不能真正使用它,因为据我所知,您可以取消注册配置。

这是我实际加载 jar 文件的代码

    public Module loadModule(Path path) throws Exception {
    if (!path.toFile().exists()) {
        throw new Exception(String.format("Could not find module at %s", path.toAbsolutePath()));
    }

    JarFile file = new JarFile(path.toFile());
    ZipEntry moduleJson = file.getEntry("module.json");
    System.out.println(path.toUri().toURL());

    if (moduleJson == null) {
        throw new Exception(String.format("Could not find module json at %s", path.toAbsolutePath()));
    }

    String moduleJsonSTR = CharStreams
            .toString(new InputStreamReader(file.getInputStream(moduleJson), Charsets.UTF_8));
    Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            .setPrettyPrinting().create();

    PluginConfig config = gson.fromJson(moduleJsonSTR,   PluginConfig.class);


    URLClassLoader classLoader = new URLClassLoader(new URL[] { path.toUri().toURL() },
            getClass().getClassLoader());
    Class mainClass = classLoader.loadClass(config.getMain());

    Enumeration<JarEntry> entries = file.entries();

    while (entries.hasMoreElements()) {
        JarEntry clazz = entries.nextElement();

        if (clazz.getName().equals(mainClass.getName())) {
            System.out.println("FOUND MAIN AND SKIPPING");
            continue;
        }

        if (clazz.isDirectory() || !clazz.getName().endsWith(".class")) {
            continue;
        }
        String className = clazz.getName().substring(0, clazz.getName().length() - 6);
        className = className.replace('/', '.');
        classLoader.loadClass(className);
    }
    Module module = (Module) mainClass.newInstance();
    System.out.println(module.getConfigurationClass().getName());
    module.setName(config.getName());
    file.close();
    classLoader.close();
    return module;
}

这是我初始化上下文的代码

public AnnotationConfigApplicationContext initializeContext() {
    SpringConfig cfg = getSpringConfig();
    this.context = new AnnotationConfigApplicationContext();
    this.context.setClassLoader(Core.class.getClassLoader());
    this.context.refresh();
    this.context.register(getConfigurationClass());
    Map<String, Object> props = context.getEnvironment().getSystemProperties();
    cfg.getProperties().forEach((key, value) -> props.put(key, value));
    return context;
}

有一个 bean,它是一个空类,@Component 被 Autowiring 到一个类中,其中上下文 Autowiring 没有问题。所以我知道 Autowiring 正在工作,我知道它是由 spring 管理的,但是 @ComponentScan 不起作用。

我想让 ComponentScan 正常工作,或者找到一种以编程方式手动添加组件的方法。

更多代码:

核心插件: 这是我的模块类:https://hastebin.com/potayuqali.java 这是加载所述模块的 Controller :https://hastebin.com/ipegaqojiv.java 示例模块:

这是模块之一的示例:https://hastebin.com/umujiqepob.java 这是该模块的@configuration:https://hastebin.com/lakemucifo.css

最佳答案

不幸的是,您的代码链接都不起作用,因此有点难以理解为什么模块的组件未注册。

但我确实知道,当您不让 Spring 管理 bean 生命周期时,Spring 会变得很困难。

这可能对您的应用程序进行太大的更改,但我不会尝试编写自己的插件系统,而是尝试 Plugin Framework for Java 。这是一个相当典型的插件框架,它有一个 spring plugin以及。

基本上,您编写插件,将它们构建为 zip/jar 并将它们放入 plugins 文件夹中。

关于java - 动态加载的 spring 上下文,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56712407/

相关文章:

module - Node.JS错误: Cannot find module 'posix' (using Cygwin)

java - 生成属于两个不同范围的 rand 值

spring - 如何在连接事件(SockJS、STOMP、Spring)上发送消息?

java - java/spring 和 c++/qt 应用程序之间使用 websockets 进行通信

java - Pivotal Cloud Foundry - 安全证书

javascript - 在较低级别冗余加载 Angular 模块?

python - 使模块在全局范围内可用

java - 如何将其嵌入到我的网站中?

java - Proguard 或编译器会预先计算 TimeUnit.Minutes.toMillis(120)

java.lang.IllegalAccessException : is related to public/private attribute for classes? 异常