【问题标题】:Spring Dependency Injection and Plugin JarSpring 依赖注入和插件 Jar
【发布时间】:2011-12-08 02:48:47
【问题描述】:

我的 Web 应用程序使用后端服务的默认 impl 运行。应该能够实现接口并将 jar 放入插件文件夹(不在应用程序类路径中)。一旦服务器重新启动,想法是将新的 jar 加载到类加载器中,并让它参与依赖注入。我正在使用 @Autowired 使用 Spring DI。新的插件服务 impl 将具有 @Primary 注释。因此,给定接口的两个 impl,应该加载主接口。

我将 jar 加载到类加载器中,并且可以手动调用 impl。但是我无法参与依赖注入,并让它替换默认的 impl。

这是一个简化的例子:

@Controller
public class MyController {
   @Autowired
   Service service;
}

//default.jar
@Service
DefaultService implements Service {
   public void print() {
       System.out.println("printing DefaultService.print()");
   } 
}

//plugin.jar not in classpath yet
@Service
@Primary
MyNewService implements Service {
   public void print() {
      System.out.println("printing MyNewService.print()");
   } 
}

//由于没有更好的地方,我从 ContextListener 加载了插件 jar

public class PluginContextLoaderListener extends org.springframework.web.context.ContextLoaderListener {

        @Override
        protected void customizeContext(ServletContext servletContext,
                                        ConfigurableWebApplicationContext wac) {
                System.out.println("Init Plugin");
                PluginManager pluginManager = PluginManagerFactory.createPluginManager("plugins");
                pluginManager.init();

                    //Prints the MyNewService.print() method  
                    Service service = (Service) pluginManager.getService("service");
                    service.print();                  
            }
    }

     <listener>
            <listener-class>com.plugin.PluginContextLoaderListener</listener-class>
     </listener>

即使我已经将 jar 加载到类加载器中,DefaultService 仍然作为服务注入。知道如何让插件 jar 参与 Spring 的 DI 生命周期吗?

编辑: 简单来说,我有一个war 文件,它在war 的plugins 目录中有几个插件jar。根据应用程序查看的配置文件中的值,当应用程序启动时,我想加载该特定插件 jar 并使用它运行应用程序。这样,我可以将战争分发给任何人,他们可以根据配置值选择要运行的插件,而无需重新打包所有内容。这就是我要解决的问题。

【问题讨论】:

    标签: java spring jakarta-ee plugins


    【解决方案1】:

    如果您重新启动服务器,我看不出为什么您不能将 JAR 添加到 WEB-INF/lib 并将其放在 CLASSPATH 中。自定义类加载器和上下文监听器的所有复杂性都消失了,因为您将其视为 Spring 控制下的任何其他类。

    如果你这样做是因为你不想打开或修改一个WAR,为什么不把它放在服务器/lib目录中呢?让服务器类加载器来获取它。这使得所有插件类可用于所有已部署的应用程序。

    答案取决于单独的 /plugin 目录的重要性。如果它是解决方案的关键,并且您不能将 JAR 添加到服务器的 /lib 目录,那么就是这样。我什么都没有。但我认为至少重新审视你必须确保它是实现你想要的唯一方法的解决方案是值得的。

    【讨论】:

    • 这总是一个选项。但想法是在战争中(但不在类路径中)有一些经过认证的插件,用户可以根据应用程序外部的配置值加载一个或另一个插件。这样,普通用户只需要关心配置属性,而不是插件系统的内部。
    【解决方案2】:

    您似乎只需要正确创建 Spring ApplicationContext。我认为没有类路径混合是可能的。最重要的是 Spring 配置文件 类路径中的位置。所以将你所有的插件 jar 放入 WEB-INF/lib 并继续阅读。

    让我们从核心模块开始。我们将使用位于classpath*:META-INF/spring/*-corecontext.xml 的文件创建它的ApplicationContext

    现在我们将让所有插件的配置文件放在别处。 IE。 'myplugin1' 的配置位置如下:classpath*:META-INF/spring/*-myplugin1context.xml。而anotherplugin 的配置将位于classpath*:META-INF/spring/*-anotherplugincontext.xml

    你所看到的是约定。如果您愿意,也可以使用子目录:

    • 核心:classpath*:META-INF/spring/core/*.xml
    • myplugin1:classpath*:META-INF/spring/myplugin1/*.xml
    • 另一个插件:classpath*:META-INF/spring/anotherplugin/*.xml

    重要的是位置必须不相交

    剩下的就是将正确的位置传递给ApplicationContext 创建者。对于 Web 应用程序,正确的地方是扩展 ContextLoaderListener 并覆盖方法 customizeContext(ServletContext, ConfigurableWebApplicationContext)

    剩下的就是读取你的配置文件(它的位置可以作为 servlet init 参数传递)。比你需要构建配置位置列表:

    String locationPrefix = "classpath*:META-INF/spring/";
    String locationSiffix = "/*.xml";
    
    List<String> configLocations = new ArrayList<String>();
    configLocations.add(locationPrefix + "core" + locationSiffix);
    
    List<String> pluginsTurnedOn = getPluginsTurnedOnFromConfiguration();
    for (String pluginName : pluginsTurnedOn) {
        configLocations.add(locationPrefix + pluginName + locationSiffix);
    }
    
    applicationContext.setConfigLocations(configLocations.toArray(new String[configLocations.size()]));
    

    通过这种方式,您可以轻松管理 Spring ApplicationContext 中加载的内容和未加载的内容。

    更新:

    为了让它发挥作用,我还做了一个隐藏的假设,我现在要解释一下。核心模块的基础包和每个插件也应该是不相交的。即:

    • com.mycompany.myapp.core
    • com.mycompany.myapp.myplugin1
    • com.mycompany.myapp.anotherplugin

    这样每个模块都可以轻松地使用&lt;context:componet-scan /&gt;(JavaConfig 中的等效项)为它自己的类添加类路径扫描。核心模块不应包含对任何插件包的任何包扫描。 插件应该扩展ApplicationContext 的配置,以将自己的包添加到类路径扫描中。

    【讨论】:

    • 谢谢。看起来这会奏效。但是我大量使用spring Annotations,并且插件jar 中没有任何spring xml。我想知道对注解驱动的依赖注入执行与上述相同的操作会有多容易。
    • 您的每个插件都应该有某种配置(@see my extended answer)。即使它只是一个小 xml,只有一个 &lt;context:conponent-scan /&gt; 的插件包,它也应该在 插件中 而没有其他任何地方。它是最了解自己并知道如何配置自己的插件。核心模块不应该知道插件的存在 - 它应该只为他们提供将自己的配置加入到ApplicationContext 的可能性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-19
    • 2018-11-21
    • 2012-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多