我一直在尝试构建模块化Web应用程序。
我的要求是动态生成UI,但是ui的组件是可插拔的。例如,我可能有一组开箱即用的核心UI小部件,但如果客户想要创建自己的小部件,则会有一个定义的接口供他们实现自己的组件。
我正在使用vaadin作为我的ui框架。并且只想让最终用户提供包含ui的jar或war文件。我不想在我的war文件中捆绑jar,但是,无论最终用户提供什么,都应该按原样部署。
我已经看过使用osgi,并且已经能够获得一个框架,允许使用vaadin从bundle中获取动态ui,但是我正在通过其他req进行依赖地狱。还有其他我未考虑的替代方案吗?
答案 0 :(得分:4)
我按照我想要的方式使用osgi和vaadin。我使用此tutorial作为参考。这让我得到了我需要的一半。
答案 1 :(得分:3)
我似乎在做一件非常相似的事情。虽然最终组件框架(如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 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,系统会自动从类中实例化“单例”对象。因此,在前一种情况下,我注册了我想要创建的对象的类,在第二种情况下,我注册了一个工厂来创建它们。在这种情况下,似乎前一个解决方案需要少一个通道,这就是我使用它的原因(通常,我经常使用@ServiceProvider)。
总结一下,列举了三个解决方案:
您还可以查看this question的答案。
答案 2 :(得分:1)
好吧,之前我已经将OSGi用于大型模块化UI。我们使用shindig内部运行的opensocial小工具。 OSGi很不错,因为您可以将其他小工具作为捆绑包放入框架中,并让监听器选择它们并将它们添加到用户的小工具选项中。这个模型很好地延伸到主题等其他东西。您对OSGi和其他依赖项有什么问题?