Springdependency injection和插件Jar
我有一个使用后端服务的默认impl运行的Web应用程序。 一个人应该能够实现接口并将jar放入plugins文件夹(不在apps类路径中)。 重新启动服务器之后,我们的想法是将新jar加载到类加载器中,并让它参与dependency injection。 我正在使用@Autowired的Spring DI。 新的插件服务impl将具有@Primary注释。 因此,给定接口的两个impl,应该加载primary。
我将jar加载到类加载器中并可以手动调用impl。 但我无法参与dependency injection,并让它替换默认的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(); } } com.plugin.PluginContextLoaderListener
即使我将jar加载到类加载器中,DefaultService仍然作为服务注入。 知道如何让插件jar参与弹簧的DI生命周期吗?
编辑:简单地说,我有一个war文件,在战争中的插件目录中有一些插件jar。 基于应用程序查看的配置文件中的值,当应用程序启动时,我想加载该特定的插件jar并使用它运行应用程序。 这样,我可以将战争分发给任何人,他们可以根据配置值选择运行哪个插件,而无需重新打包所有内容。 这是我试图解决的问题。
您似乎只需要正确创建Spring ApplicationContext
。 我认为没有 classpath mingling是可能的。 最重要的是类路径中Spring配置文件的位置。 因此,将所有插件jar放入WEB-INF/lib
并继续阅读。
让我们从核心模块开始吧。 我们将从位于classpath*:META-INF/spring/*-corecontext.xml
文件创建它的ApplicationContext
classpath*:META-INF/spring/*-corecontext.xml
。
现在我们将使所有插件将其配置文件放在其他位置。 即’myplugin1’将具有如下配置位置: classpath*:META-INF/spring/*-myplugin1context.xml
。 而anotherplugin
将在classpath*:META-INF/spring/*-anotherplugincontext.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)
。
剩下的就是读取您的配置文件(它的位置可以作为servlet init参数传递)。 比你需要构建配置位置列表:
String locationPrefix = "classpath*:META-INF/spring/"; String locationSiffix = "/*.xml"; List configLocations = new ArrayList (); configLocations.add(locationPrefix + "core" + locationSiffix); List 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
这样,每个模块都可以轻松地使用
(在JavaConfig中等效)来为它自己的类添加类路径扫描。 核心模块不应包含任何插件包的任何包扫描。 插件应扩展 ApplicationContext
配置 ,以将自己的包添加到类路径扫描中。
如果重新启动服务器,我认为没有理由不能将JAR添加到WEB-INF / lib并将其保存在CLASSPATH中。 自定义类加载器和上下文监听器的所有复杂性都消失了,因为您可以像在Spring控制下的任何其他类一样对待它。
如果你这样做是因为你不想打开或修改WAR,为什么不把它放在server / lib目录下呢? 让服务器类加载器接收它。 这使得所有已部署的应用程序都可以使用所有插件类。
答案取决于单独/插件目录的重要性。 如果它是解决方案的关键,并且您无法将JAR添加到服务器的/ lib目录,那就是那个。 我什么都没有。 但我认为至少要重新审视你必须确保它是实现你想要的唯一方法的解决方案是值得的。