所以我有一个正在使用Spring boot的项目,并且想要利用模块系统。我希望模块系统可以动态重载。我几乎可以正常使用了,但是@ComponentScan在模块中完全不起作用。
有一个模块文件夹,其中包含jar文件,这些jar文件在启动时会加载,并且需要动态卸载,加载和重新加载。
通过AnnotationConfigApplicationContext创建模块,将上下文的类加载器设置为核心的类加载器,并通过context.register注册模块接口中的方法提供的@Configuration类。
@Configuration中的@Bean声明适用于自动装配,并且所有类均正确加载。问题是@ComponentScan不起作用。我什至尝试使用@Component注释类,该类没有字段或任何内容,并且会引发自动装配错误,就像不在上下文中一样。如果这不是我的存储库的问题,那也没什么大不了的。
如果有一种方法,我可以手动运行没有注释的componentcan,我可以做一些额外的工作。
我尝试将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被自动装配到一个可以自动装配上下文的类中。所以我知道autowire在起作用,我知道它是在春季管理的,但是@ComponentScan不起作用。
我想使ComponentScan工作,或者想办法以编程方式手动添加组件。
更多代码:
核心插件: 这是我的模块类:https://hastebin.com/potayuqali.java 这是加载所述模块的控制器:https://hastebin.com/ipegaqojiv.java 示例模块:
这是以下模块之一的示例:https://hastebin.com/umujiqepob.java 这是该模块的@configuration:https://hastebin.com/lakemucifo.css
答案 0 :(得分:1)
如果我理解正确,则尝试在ApplicationContext加载后加载jar并注册为bean。但是,当应用程序上下文加载之后,ComponentScan会扫描@Component之类的注释。因此,您应该以编程方式注册类(与context.register(..)
一样)。
在注册类之后,您可能拥有@Configuration
注释类,并且它具有@Bean
注释方法。要注册带有@Bean
注释的方法,您可以运行
@Autowire
ConfigurationClassPostProcessor configurationClassPostProcessor;
和
configurationClassPostProcessor.processConfigBeanDefinitions(applicationContext.getDefaultListableBeanFactory());
您也可以看一下我的答案: Is There Any Way To Scan All @Component like Annotated Class After Spring ApplicationContext Load
另一种解决方案:
要扫描基本软件包(例如@ComponentScan),请使用
annotationConfigApplicationContext.scan(basePackageName).
它扫描基本包,并像@ComponentScan一样注册@Component(etc)注释的类。但是加载jar的classLoader必须与注解ConfigApplicationContext#classLoader相同。因此,您必须使用以下方式设置classLoader:
annotationConfigApplicationContext.setClassLoader(yourClassLoader)
否则AnnotationConfigApplicationContext无法找到和注册类。
答案 1 :(得分:0)
@SpringBootApplication类是否有可能位于@ComponentScan的basePackages之一的根包中?出于某些奇怪的原因,我仍在寻找解释,将SpringBootApplication类从com.comp.app移至com.comp.app.xyx,并从com.comp.app。*中加载了位于其他依赖项中的所有组件。
答案 2 :(得分:0)
很遗憾,您的代码链接均无效,因此很难理解为什么模块的Components
未注册。
但是我确实知道,如果不让Spring管理bean的生命周期,Spring就会变得困难。
对您的应用进行的更改可能太大了,但是与其尝试编写自己的插件系统,还不如尝试Plugin Framework for Java。这是一个非常典型的插件框架,并且还有一个spring plugin。
基本上,您编写插件,将其构建为zip / jar并将其放入plugins
文件夹中。