【发布时间】:2019-06-22 03:45:28
【问题描述】:
所以我有一个项目,我正在使用 Spring Boot,并且想要使用模块系统。我希望模块系统可以动态重新加载。我几乎可以使用它,但 @ComponentScan 在模块中完全不起作用。
有一个module文件夹,里面有jar文件,启动时加载,需要动态卸载,加载,重新加载。
模块是通过 AnnotationConfigApplicationContext 创建的,上下文的类加载器设置为核心的类加载器,模块接口中的方法提供的 @Configuration 类通过 context.register 注册。
@Configuration 中的@Bean 声明适用于自动装配,并且所有类都正确加载。问题是 @ComponentScan 不起作用。我什至尝试用 @Component 注释一个没有字段或任何东西的类,它会抛出一个自动装配错误,就像它不在上下文中一样。如果这不是我的存储库的问题,这将不是什么大问题。
如果有一种方法我可以在没有注释的情况下手动运行组件扫描,我可以做一些额外的工作。
我已经尝试使用带有 @SpringBootApplication(scanPackages = {"package.name"}
的 spring boot 包@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 被自动装配到一个上下文自动装配的类中,没有问题。所以我知道 autowire 正在工作,我知道它是 spring 管理的,但 @ComponentScan 不工作。
我想让 ComponentScan 工作,或者想办法以编程方式手动添加组件。
更多代码:
核心插件: 这是我的模块类:https://hastebin.com/potayuqali.java 这是加载所述模块的控制器:https://hastebin.com/ipegaqojiv.java 示例模块:
这是其中一个模块的示例:https://hastebin.com/umujiqepob.java 这是该模块的@configuration:https://hastebin.com/lakemucifo.css
【问题讨论】:
-
Osgi 更适合动态加载。见:dzone.com/articles/osgi-and-spring-dynamic
标签: java spring module classloader hierarchy