модульное/подключаемое веб-приложение Java

Я пытался создать модульное веб-приложение.

Мои требования состоят в том, чтобы динамически генерировать пользовательский интерфейс, но компоненты пользовательского интерфейса должны быть подключаемыми. Например, у меня может быть базовый набор виджетов пользовательского интерфейса, которые поставляются из коробки, но если клиент хочет создать свой собственный, у него будет определенный интерфейс для реализации своего собственного компонента.

Я использую vaadin для своего пользовательского интерфейса. И просто хотел бы, чтобы конечные пользователи предоставили файл jar или war, содержащий там ui. Однако я не хочу связывать банку с моим военным файлом, все, что предоставляет конечный пользователь, должно быть развертываемым как есть.

Я рассмотрел использование osgi и смог получить фреймворк, который позволяет использовать динамический пользовательский интерфейс из пакетов с использованием vaadin, однако я прохожу через ад зависимостей с другими req. Есть ли другие альтернативы, которые я не рассматривал?


person broschb    schedule 22.07.2011    source источник


Ответы (3)


Я получил это так, как хотел, используя osgi и vaadin. Я использовал этот учебник как ссылка. Это дало мне половину пути к тому, что мне было нужно.

person broschb    schedule 05.08.2011

Кажется, я делаю очень похожую вещь. Хотя в конечном итоге фреймворки компонентов, такие как 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 вместо файлов .nbm, специфичных для платформы NetBeans).

По сути, вы должны использовать аннотация @ServiceProvider. Он запускается во время компиляции (с Java 6) и генерирует файл описания META-INF/services/, который будет помещен в путь к классам. Этот файл является стандартной функцией Java (начиная с 1.3, я полагаю), и его можно запрашивать с помощью стандартного класса ServiceLoader. В этом случае вы должны использовать библиотеки платформы NetBeans только во время компиляции, поскольку они используются только для создания META-INF/служб. В конце концов, библиотеки можно было бы использовать и для более эффективных способов запроса зарегистрированных служб с помощью класса Lookup.

Между этими двумя решениями есть разница в дизайне. С помощью моей пользовательской аннотации я обнаруживаю классы: затем я использую их с отражением для создания экземпляров объектов. С помощью @ServiceProvider система автоматически создает объект-одиночку из класса. Таким образом, в первом случае я регистрирую классы для объектов, которые хочу создать, во втором случае я регистрирую фабрику для их создания. В этом случае кажется, что предыдущее решение требует на один проход меньше, и поэтому я использую его (обычно я часто использую @ServiceProvider).


Подводя итоги, были перечислены три решения:

  1. Используйте мой предоставленный ClassScanner с Spring. Требуется Spring во время выполнения.
  2. Используйте @ServiceProvider в коде и сканируйте с помощью ServiceLoader. Требуются две библиотеки платформы NetBeans во время компиляции и только среда выполнения Java во время выполнения.
  3. Используйте @ServiceProvider в коде и сканируйте с помощью Lookup. Требуются две библиотеки платформы NetBeans во время выполнения.

Вы также можете просмотреть ответы на этот вопрос.

person Fabrizio Giudici    schedule 25.07.2011
comment
На самом деле я попробовал несколько из этих подходов, и в моем точном сценарии они так или иначе потерпели неудачу. Я действительно смог выполнить то, что мне было нужно, используя OSGI. - person broschb; 05.08.2011

Что ж, раньше я использовал OSGi для большого модульного пользовательского интерфейса. Мы использовали открытые социальные гаджеты, работающие внутри вечеринки. OSGi удобен, потому что вы можете просто поместить дополнительные гаджеты в структуру в виде пакетов, а слушатель подберет их и добавит к выбору гаджетов пользователя. Эта модель хорошо распространяется на другие вещи, такие как темы. Какие проблемы у вас возникают с OSGi и другими зависимостями?

person Jon7    schedule 23.07.2011