[英]Spring Dependency Injection and Plugin Jar
我的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()");
}
}
//由於缺少更好的地方,我從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仍然作為服務注入。 知道如何讓插件罐參與彈簧的DI生命周期嗎?
編輯:簡單地說,我有一個戰爭文件,在戰爭中的插件目錄中有一些插件罐。 基於應用程序查看的配置文件中的值,當應用程序啟動時,我想加載該特定的插件jar並使用它運行應用程序。 這樣,我可以將戰爭分發給任何人,他們可以根據配置值選擇運行哪個插件,而無需重新打包所有內容。 這是我試圖解決的問題。
您似乎只需要正確創建Spring ApplicationContext
。 我認為沒有 classpath mingling是可能的。 最重要的事情是在classpath 中的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
。
你看到的是一種對流 。 如果您願意,也可以使用子目錄:
classpath*:META-INF/spring/core/*.xml
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
加載的內容和內容。
更新:
為了讓它發揮作用,我做了一個更隱藏的假設,我現在要解釋。 核心模塊和每個插件的基礎包也應該是不相交的 。 那就是:
這樣,每個模塊都可以輕松地使用<context:componet-scan />
(在JavaConfig中等效)來為它自己的類添加類路徑掃描。 核心模塊不應包含任何插件包的任何包掃描。 插件應擴展 ApplicationContext
配置 ,以將自己的包添加到類路徑掃描中。
如果重新啟動服務器,我認為沒有理由不能將JAR添加到WEB-INF / lib並將其保存在CLASSPATH中。 自定義類加載器和上下文監聽器的所有復雜性都消失了,因為您可以像在Spring控制下的任何其他類一樣對待它。
如果你這樣做是因為你不想打開或修改WAR,為什么不把它放在server / lib目錄下呢? 讓服務器類加載器接收它。 這使得所有已部署的應用程序都可以使用所有插件類。
答案取決於單獨/插件目錄的重要性。 如果它是解決方案的關鍵,並且您無法將JAR添加到服務器的/ lib目錄,那就是那個。 我什么都沒有。 但我認為至少要重新審視你必須確保它是實現你想要的唯一方法的解決方案是值得的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.