簡體   English   中英

模塊化/可插拔 java web 應用

[英]modular/pluggable java web application

我一直在嘗試構建模塊化 web 應用程序。

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

我正在為我的 ui 框架使用 vaadin。 並且希望最終用戶提供包含 ui 的 jar 或 war 文件。 我不想將 jar 捆綁在我的 war 文件中,但是,無論最終用戶提供什么,都應該可以按原樣部署。

我已經研究過使用 osgi,並且已經能夠獲得一個框架,該框架允許使用 vaadin 從捆綁包中實現動態 ui,但是我正在與其他 req 一起經歷依賴地獄。 還有其他我沒有考慮過的選擇嗎?

我使用 osgi 和 vaadin 以我想要的方式工作。 我使用本教程作為參考。 這讓我達到了我需要的一半。

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

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

Java 實現組件的類標有定制注釋:例如

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

然后我使用 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 的完整實現,在 Spring 之上實現:

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 類加載器的工作方式,您必須至少指定一個 package 進行搜索。 在我的示例中,我硬連線了頂部 package “it”(我的東西是“it.tidalwave.*”),很容易將此信息放入可配置的屬性中,最終指定多個 package。


使用NetBeans 平台中的兩個庫即可使用另一種解決方案。 我強調這樣的概念,即這不會將整個平台導入您的項目,包括類加載器設施等,而只是使用兩個 jar 文件。 因此它不是侵入性的。 The libraries are org-openide-util.jar and org-openide-util-lookup.jar (I stress again, you can use the plain.jar files instead of the.nbm files that are specific of the NetBeans Platform).

基本上,您將使用@ServiceProvider 注釋 它在編譯期間被觸發(使用 Java 6)並生成一個 META-INF/services/ 描述文件,該文件將放置在類路徑中。 該文件是 Java 的標准功能(我相信從 1.3 開始),可以使用標准 class ServiceLoader進行查詢。 在這種情況下,您將僅在編譯期間使用 NetBeans 平台庫,因為它們僅用於生成 META-INF/服務。 最終,這些庫還可以通過查找 class用於查詢注冊服務的更好方法。

兩種解決方案之間存在設計差異。 通過我的自定義注釋,我發現了類:然后我將它們與反射一起使用來實例化對象。 使用@ServiceProvider,系統會自動從 class 實例化一個“單例”object。 因此,在前一種情況下,我為要創建的對象注冊了類,在第二種情況下,我注冊了一個工廠來創建它們。 在這種情況下,似乎前一種解決方案需要的通道更少,這就是我使用它的原因(通常,我經常使用@ServiceProvider)。


總結起來,列舉了三種解決方案:

  1. 將我提供的 ClassScanner 與 Spring 一起使用。 運行時需要 Spring。
  2. 在代碼中使用 @ServiceProvider 並使用 ServiceLoader 進行掃描。 在編譯時需要兩個 NetBeans 平台庫,在運行時只需要 Java 運行時。
  3. 在代碼中使用 @ServiceProvider 並使用 Lookup 進行掃描。 運行時需要兩個 NetBeans 平台庫。

你也可以看看這個問題的答案。

好吧,我以前曾將 OSGi 用於大型模塊化 UI。 我們使用了在 shindig 內部運行的 opensocial 小工具。 OSGi 很好,因為您可以將其他小工具作為捆綁包放入框架中,然后讓偵聽器拾取它們並將它們添加到用戶的小工具選擇中。 這個 model 很好地擴展到了其他東西,比如主題。 您對 OSGi 和其他依賴項有什么問題?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM