所以我有一个正在使用 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/