Spring依赖注入和插件Jar

Lan*_*ali 13 java plugins spring java-ee

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

我将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()");
   } 
}
Run Code Online (Sandbox Code Playgroud)

//由于缺少更好的地方,我从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>
Run Code Online (Sandbox Code Playgroud)

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

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

Roa*_*ner 8

看起来你需要的就是ApplicationContext正确创建Spring .我认为没有 classpath mingling 是可能的.最重要的事情是Spring配置文件的位置的类路径.所以把你的所有插件jar放入WEB-INF/lib并继续阅读.

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

现在我们将使所有插件将其配置文件放在其他位置.即'myplugin1'将具有如下配置位置:classpath*:META-INF/spring/*-myplugin1context.xml.而anotherplugin将在CONFIGS classpath*:META-INF/spring/*-anotherplugincontext.xml.

你看到的是一种对流.如果您愿意,也可以使用子目录:

  • 核心: 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).

剩下的就是读取您的配置文件(它的位置可以作为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()]));
Run Code Online (Sandbox Code Playgroud)

这样您就可以轻松管理Spring中加载的内容和内容ApplicationContext.

更新:

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

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

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