2011-07-22 60 views
4

我一直在試圖構建一個模塊化的Web應用程序。模塊化/可插拔的Java Web應用程序

我的要求是動態生成UI,但UI的組件是可插拔的。例如,我可能有一組核心的UI小部件,但如果客戶想自己創建,那麼會有一個定義的接口來實現他們自己的組件。

我使用vaadin我的UI框架。並希望讓最終用戶提供包含用戶界面的jar或war文件。不過,我不想將jar包壓入我的war文件中,無論最終用戶提供的是否應該按原樣部署。

我已經看了使用OSGi,並且已經能夠得到一個框架,允許使用vaadin束動態UI,不過我會通過與其他REQ依賴地獄。我還沒有考慮過其他的選擇嗎?

回答

4

我按照我想用osgi和vaadin的方式工作。我用這個tutorial作爲參考。這讓我得到了我所需要的一半。

1

嗯,我以前用的OSGi的大模塊化的UI。我們使用shindig內部運行的opensocial gadgets。 OSGi非常好,因爲您可以將其他小工具以捆綁包的形式放入框架中,並讓偵聽器選取並將其添加到用戶的小工具選項中。這個模型可以很好地擴展到其他的主題。你對OSGi和其他依賴有什麼問題?

3

看來我在做一件非常相似的事情。雖然最終組件框架(如OSGi和NetBeans平臺(也可以用於服務器端))是我已經使用並用於其他項目的可行解決方案,但它們在使用更多功能時付出了複雜性他們提供,除了搜索註冊組件(例如強制執行依賴性檢查,版本檢查,模塊隔離等)。

但對於掃描捆綁類有一個簡單的解決方案,基於註釋掃描。在我與Vaadin的項目中,我創建了一個引用「抽象」組件名稱的用戶界面,這些用戶界面必須與用戶可能提供的實際Java類匹配。

實現組件的Java類標有一個特製的註解:例如

@ViewMetadata(typeUri="component/HtmlTextWithTitle", controlledBy=DefaultHtmlTextWithTitleViewController.class) 
public class VaadinHtmlTextWithTitleView extends Label implements HtmlTextWithTitleView 

然後我在classpath中搜索註釋類與ClassScanner:

final ClassScanner classScanner = new ClassScanner(); 
    classScanner.addIncludeFilter(new AnnotationTypeFilter(ViewMetadata.class)); 

    for (final Class<?> viewClass : classScanner.findClasses()) 
     { 
     final ViewMetadata viewMetadata = viewClass.getAnnotation(ViewMetadata.class); 
     final String typeUri = viewMetadata.typeUri(); 
     // etc... 
     } 

這是我對ClassScanner全面執行,在上面實施春:

import javax.annotation.Nonnull; 
import java.util.ArrayList; 
import java.util.Collection; 
import java.util.List; 
import org.springframework.beans.factory.config.BeanDefinition; 
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; 
import org.springframework.core.type.filter.TypeFilter; 
import org.springframework.util.ClassUtils; 

public class ClassScanner 
    { 
    private final String basePackage = "it"; // FIXME 

    private final ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); 

    @Nonnull 
    public final Collection<Class<?>> findClasses() 
     { 
     final List<Class<?>> classes = new ArrayList<Class<?>>(); 

     for (final BeanDefinition candidate : scanner.findCandidateComponents(basePackage)) 
      { 
      classes.add(ClassUtils.resolveClassName(candidate.getBeanClassName(), ClassUtils.getDefaultClassLoader())); 
      } 

     return classes; 
     } 

    public void addIncludeFilter (final @Nonnull TypeFilter filter) 
     { 
     scanner.addIncludeFilter(filter); 
     } 
    } 

這是非常簡單,但有效。請注意,由於Java ClassLoaders的工作原理,您必須指定至少一個包來搜索。在我的示例中,我硬連接頂層包「it」(我的東西是「it.tidalwave。*」),很容易將這些信息放入可配置的屬性中,最終指定多個包。


另一種解決方案可以通過只是使用從NetBeans平臺兩個庫使用。我強調這個概念,它不會將整個平臺導入到您的項目中,包括類加載器設施等,但僅使用兩個jar文件。因此它不是侵入性的。這些庫是org-openide-util.jar和org-openide-util-lookup.jar(我再次強調,您可以使用簡單的.jar文件,而不是特定於NetBeans平臺的.nbm文件)。基本上,你會使用@ServiceProvider annotation。它在編譯過程中被觸發(使用Java 6)並生成一個META-INF/services/description文件,該文件將放置在類路徑中。這個文件是Java的一個標準功能(我相信1.3),可以用標準類ServiceLoader查詢。在這種情況下,僅在編譯期間使用NetBeans平臺庫,因爲它們僅用於生成META-INF /服務。最終,通過Lookup class,這些庫也可用於更好地查詢註冊服務。

這兩種解決方案之間存在設計差異。用我的自定義註釋,我發現類:然後我用它們與反射來實例化對象。通過@ServiceProvider,系統會自動從類中實例化一個'singleton'對象。因此,在前一種情況下,我爲我想創建的對象註冊類,在第二種情況下,我註冊了一個工廠來創建它們。在這種情況下,前面的解決方案似乎需要少一段時間,這就是爲什麼我使用它(通常,我使用@ServiceProvider很多)。


綜上,三個解決方案已經列舉:

  1. 使用我提供ClassScanner春暖花開。在運行時需要Spring。
  2. 在代碼中使用@ServiceProvider並使用ServiceLoader進行掃描。在編譯時需要兩個NetBeans平臺庫,而運行時需要Java運行時。
  3. 在代碼中使用@ServiceProvider並使用Lookup進行掃描。運行時需要兩個NetBeans Platform庫。

您也可以查看this question的答案。

+0

我嘗試了其中幾種方法,在我的確切場景中,他們以某種方式失敗。我實際上能夠完成我最終需要使用OSGI。 – broschb

相關問題