Java 动态加载的spring上下文

Java 动态加载的spring上下文,java,spring,module,classloader,hierarchy,Java,Spring,Module,Classloader,Hierarchy,所以我有一个项目,在其中我使用Spring boot,并希望利用一个模块系统。我希望模块系统能够动态地重新加载。我让它几乎可以工作,但是@ComponentScan在模块中完全不工作 有一个模块文件夹,其中包含启动时加载的jar文件,需要动态卸载、加载和重新加载 模块通过AnnotationConfigApplicationContext创建,上下文的类加载器设置为核心的类加载器,模块接口中的方法提供的@Configuration类通过context.register注册 @Configurat

所以我有一个项目,在其中我使用Spring boot,并希望利用一个模块系统。我希望模块系统能够动态地重新加载。我让它几乎可以工作,但是@ComponentScan在模块中完全不工作

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

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

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

如果有一种方法可以在没有注释的情况下手动运行componentscan,那么我可以做一些额外的工作

我已经尝试将spring启动包与@SpringBootApplication一起使用(scanPackages={“package.name”}

@使能还原 @实体扫描

都在适当的位置,参数正确

我试过使用.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;
}
公共模块loadModule(路径路径)引发异常{
如果(!path.toFile().exists()){
抛出新异常(String.format(“在%s处找不到模块”,path.toAbsolutionPath());
}
JarFile file=新的JarFile(path.toFile());
ZipEntry moduleJson=file.getEntry(“module.json”);
System.out.println(path.toUri().toul());
if(moduleJson==null){
抛出新异常(String.format(“在%s处找不到模块json”,path.toAbsolutionPath());
}
字符串moduleJsonSTR=CharStreams
.toString(新的InputStreamReader(文件.getInputStream(moduleJson),Charsets.UTF_8));
Gson Gson=new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_,带下划线)
.setPrettyPrinting().create();
PluginConfig=gson.fromJson(moduleJsonSTR,PluginConfig.class);
URLClassLoader classLoader=新URLClassLoader(新URL[]{path.toUri().toURL()},
getClass().getClassLoader());
类mainClass=classLoader.loadClass(config.getMain());
枚举条目=file.entries();
while(entries.hasMoreElements()){
JarEntry clazz=entries.nextElement();
if(clazz.getName().equals(mainClass.getName())){
System.out.println(“找到主文件并跳过”);
继续;
}
if(clazz.isDirectory()| |!clazz.getName().endsWith(“.class”)){
继续;
}
字符串className=clazz.getName().substring(0,clazz.getName().length()-6);
className=className.replace(“/”,“.”);
loadClass(className);
}
模块Module=(模块)mainClass.newInstance();
System.out.println(module.getConfigurationClass().getName());
module.setName(config.getName());
file.close();
classLoader.close();
返回模块;
}
这是我初始化上下文的代码

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;
}
public AnnotationConfigApplicationContext initializeContext(){
SpringConfig cfg=getSpringConfig();
this.context=新的AnnotationConfigApplicationContext();
this.context.setClassLoader(Core.class.getClassLoader());
this.context.refresh();
this.context.register(getConfigurationClass());
Map props=context.getEnvironment().getSystemProperties();
forEach((键,值)->props.put(键,值));
返回上下文;
}
有一个bean是一个空白类,@Component被自动连接到一个类中,在这个类中上下文自动连接没有问题。所以我知道autowire正在工作,我知道它是spring管理的,但是@ComponentScan不工作

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

更多代码:

核心插件: 这是我的模块类: 这是加载所述模块的控制器: 示例模块:

这是其中一个模块的示例:
这是该模块的@configuration:

如果我理解正确,您可以尝试在ApplicationContext加载后加载jar并注册为bean。但是ComponentScan会在应用程序上下文加载后扫描@Component-like注释。因此,您应该以编程方式注册类(正如您所做的
context.register(…)

在注册类之后,您可能有
@Configuration
注释类,它有
@Bean
注释方法。要注册
@Bean
注释方法,您可以运行

@Autowire
ConfigurationClassPostProcessor configurationClassPostProcessor;

你也可以看看我的答案:

另一种解决方案

要扫描基本包(如@ComponentScan),请使用

它扫描基本包并将@Component(etc)注释类注册为@ComponentScan do。但加载jar的类加载器必须与annotationConfigApplicationContext#类加载器相同。因此,必须将类加载器设置为:

annotationConfigApplicationContext.setClassLoader(yourClassLoader)

或者AnnotationConfigApplicationContext无法找到并注册类。

有没有可能@SpringBootApplication类位于@ComponentScan?的某个基本包的根包中?出于某种奇怪的原因,我仍在寻找解释,将我的SpringBootApplication类从com.comp.app移动到com.comp.app.xyx,已从com.comp.app.*加载所有在其他依赖项中定位的组件。

不幸的是,您的代码链接都不起作用,因此很难理解为什么
annotationConfigApplicationContext.scan(basePackageName).
annotationConfigApplicationContext.setClassLoader(yourClassLoader)