Spring依赖注入和插件Jar [英] Spring Dependency Injection and Plugin Jar

查看:140
本文介绍了Spring依赖注入和插件Jar的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have web application running with a default impl of a backend service. One should be able to implement the interface and drop the jar into the plugins folder (which is not in the apps classpath). Once the server is restarted, the idea is to load the new jar into the classloader, and have it take part in dependency injection. I am using Spring DI using @Autowired. The new plugin service impl will have @Primary annotation. So given two impls of the interface, the primary should be loaded.

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

I got the jar loaded into the classloader and can invoke the impl manually. But I haven't been able to get to to participate in the Dependency Injection, and have it replace the default 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

//For lack of better place, I loaded the plugin jar from the ContextListener

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仍然作为服务注入。知道如何让插件罐参与弹簧的DI生命周期吗?

Even after I have loaded the jar into the classloader, DefaultService is still being injected as service. Any idea how I get the plugin jar to participate into the spring's DI lifecycle?

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

Edited: To put it simply, I have a war file that has a few plugin jars in a plugins directory inside the war. Based on a value from a configuration file that the app looks at, when the app is started, I want to load that particular plugin jar and run the application with it. That way, I can distribute the war to anyone, and they can choose which plugin to run based on a config value without having to to repackage everything. This is the problem I am trying to solve.

推荐答案

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

It seems like all You need is to create the Spring ApplicationContext properly. I think it's possible without classpath mingling. What matters most are the locations of the Spring configuration files within the classpath. So put all Your plugin jar's into WEB-INF/lib and read on.

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

Let's start with the core module. We'll make it to create it's ApplicationContext from files located at classpath*:META-INF/spring/*-corecontext.xml.

现在我们将让所有插件将其配置文件放在其他位置。即'myplugin1'的配置位置如下: classpath *:META-INF / spring / * - myplugin1context.xml 。并且 anotherplugin 将在 classpath *:META-INF / spring / * - anotherplugincontext.xml

Now we'll make all plugins to have their config files elsewhere. I.e. 'myplugin1' will have its config location like this: classpath*:META-INF/spring/*-myplugin1context.xml. And anotherplugin will have the configs at classpath*:META-INF/spring/*-anotherplugincontext.xml.

你看到的是一个对流。如果您愿意,也可以使用子目标:

What You see is a convension. You can also use subdirectiries if You like:


  • core: classpath *:META-INF / spring / core / * .xml

  • myplugin1: classpath *:META-INF / spring / myplugin1 / * .xml

  • anotherplugin: classpath *:META-INF / spring / anotherplugin / * .xml

  • core: classpath*:META-INF/spring/core/*.xml
  • myplugin1: classpath*:META-INF/spring/myplugin1/*.xml
  • anotherplugin: classpath*:META-INF/spring/anotherplugin/*.xml

重要的是,地点必须不相交

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

All that remains is to pass the right locations to the ApplicationContext creator. For web applications the right place for this would be to extend the ContextLoaderListener and override the method customizeContext(ServletContext, ConfigurableWebApplicationContext).

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

All that remains is to read Your config file (its location can be passed as servlet init parameter). Than You need to construct the list of config locations:

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

This way You can easily manage what is and what is not loaded into Spring ApplicationContext.

更新:

制作它的工作还有一个我做的隐藏的假设,我现在要解释。核心模块和每个插件的基础包也应该不相交。即ie:

To make it work there's one more hidden assumption I made that I'm about to explain now. The base package of the core module and each plugin should also be disjoint. That is i.e.:


  • com.mycompany.myapp.core

  • com.mycompany.myapp。 myplugin1

  • com.mycompany.myapp.anotherplugin

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

This way each module can use <context:componet-scan /> (on equivalent in JavaConfig) easily to add classpath scanning for it's own classes only. The core module should not contain any package scanning of any plugin packages. The plugins should extend configuration of ApplicationContext to add their own packages to classpath scanning.

这篇关于Spring依赖注入和插件Jar的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆