【问题标题】:modular/pluggable java web application模块化/可插拔 Java Web 应用程序
【发布时间】:2011-07-22 20:43:55
【问题描述】:

我一直在尝试构建一个模块化的 Web 应用程序。

我的要求是动态生成 UI,但 ui 的组件是可插拔的。例如,我可能有一组开箱即用的核心 UI 小部件,但如果客户想要创建自己的 UI 小部件,则会有一个定义的接口供他们实现自己的组件。

我正在为我的 ui 框架使用 vaadin。并且只想让最终用户提供一个包含 ui 的 jar 或 war 文件。但是,我不想将 jar 捆绑到我的 war 文件中,无论最终用户提供什么都应该可以按原样部署。

我已经研究过使用 osgi,并且已经能够获得一个框架,该框架允许使用 vaadin 从捆绑包中实现动态 ui,但是我正在与其他 req 一起经历依赖地狱。还有其他我没有考虑过的替代方案吗?

【问题讨论】:

    标签: java web-applications osgi vaadin


    【解决方案1】:

    我使用 osgi 和 vaadin 以我想要的方式工作。我用这个tutorial 作为参考。这让我达到了我需要的一半。

    【讨论】:

      【解决方案2】:

      看来我正在做一件非常相似的事情。虽然最终组件框架,如 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...
            }
      

      这是我在 Spring 之上实现的 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 类加载器的工作方式,您必须指定至少一个要搜索的包。在我的示例中,我硬连线了顶级包“it”(我的东西是“it.tidalwave.*”),很容易将此信息放入可配置的属性中,最终指定多个包。


      另一种解决方案可以通过使用来自 NetBeans 平台的两个库来使用。我强调这样一个概念,即这不会将整个平台导入您的项目,包括类加载器设施等,而只是使用两个 jar 文件。因此它不是侵入性的。这些库是 org-openide-util.jar 和 org-openide-util-lookup.jar(我再次强调,您可以使用普通的 .jar 文件而不是特定于 NetBeans 平台的 .nbm 文件)。

      基本上,您会使用@ServiceProvider annotation。它在编译期间被触发(使用 Java 6)并生成一个 META-INF/services/ 描述文件,该文件将放置在类路径中。该文件是 Java 的标准特性(我相信是从 1.3 开始),可以使用标准类 ServiceLoader 进行查询。在这种情况下,您将仅在编译期间使用 NetBeans 平台库,因为它们仅用于生成 META-INF/服务。最终,这些库还可以用于更好的方式来查询已注册的服务,通过Lookup class

      这两种解决方案之间存在设计差异。通过我的自定义注释,我发现了类:然后我将它们与反射一起使用来实例化对象。使用@ServiceProvider,系统会自动从类中实例化一个“单例”对象。因此,在前一种情况下,我为要创建的对象注册了类,在第二种情况下,我注册了一个工厂来创建它们。在这种情况下,似乎前一种解决方案需要的通道更少,这就是我使用它的原因(通常我使用@ServiceProvider很多)。


      总结一下,列举了三种解决方案:

      1. 将我提供的 ClassScanner 与 Spring 结合使用。运行时需要 Spring。
      2. 在代码中使用@ServiceProvider 并使用ServiceLoader 进行扫描。在编译时需要两个 NetBeans 平台库,在运行时只需要 Java 运行时。
      3. 在代码中使用 @ServiceProvider 并使用 Lookup 进行扫描。运行时需要两个 NetBeans 平台库。

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

      【讨论】:

      • 我实际上尝试了其中的几种方法,在我的确切情况下,它们以一种或另一种方式失败了。实际上,我最终使用 OSGI 完成了我需要的工作。
      【解决方案3】:

      嗯,我以前曾将 OSGi 用于大型模块化 UI。我们使用了在 shindig 内部运行的 opensocial 小工具。 OSGi 很好,因为您可以将其他小工具作为捆绑包放入框架中,然后让侦听器拾取它们并将它们添加到用户的小工具选择中。该模型很好地扩展到了主题等其他事物。您对 OSGi 和其他依赖项有什么问题?

      【讨论】:

        猜你喜欢
        • 2014-04-17
        • 1970-01-01
        • 2013-08-15
        • 2012-10-12
        • 1970-01-01
        • 2011-03-27
        • 1970-01-01
        • 2012-03-23
        • 1970-01-01
        相关资源
        最近更新 更多