Статьи

Как работают точки расширения NetBeans?

Одним из основных преимуществ платформы NetBeans является ее модульная система. Независимо от того, какая модульная система лучше (я думаю, скоро будет версия NetBeans, которая также может работать как пакеты OSGi), важно иметь систему, которая позволит вам создать модульную архитектуру для вашего приложения. Модульная система — это приглашение создать чистую и поддерживаемую архитектуру с определенными зависимостями и красивыми API с четко определенными и простыми в использовании точками расширения. Если вы будете следовать этим принципам, другие могут легко расширить ваше приложение. Возможно, самый простой способ предоставить точки расширения в NetBeans — это файл layer.xml (также известный как «файл слоя»).

В модуле NetBeans (также известном как «плагин») файл слоя является его центральным файлом конфигурации. Среда IDE NetBeans использует файл слоя для предоставления точек расширения для API. Объекты могут быть созданы декларативно в файле слоя, и вы можете использовать API поиска NetBeans для прослушивания изменений. Вы будете использовать этот подход всякий раз, когда создаете, например, новые действия (которые вызываются с помощью пунктов меню, кнопок панели инструментов и / или сочетаний клавиш) или TopComponents (которые предоставляют «представления» или «окна»).

Этот краткий учебник покажет, как вы можете указать свои собственные точки расширения через файл слоя. Исходный код примера находится здесь на портале плагинов NetBeans .

Предпосылки

  1. NetBeans (я использую 6.1, но это также будет работать со старыми / более новыми версиями).
  2. Создайте новый набор модулей:

    1. Выберите «Файл»> «Новый проект» (Ctrl-Shift-N). В разделе «Категории» выберите «Модули NetBeans». В проектах выберите «Приложение на платформе NetBeans» или («Проект Module Suite» в более старых версиях) и нажмите «Далее».
    2. На панели «Имя и местоположение» введите «Layerextensionpoints» в поле «Имя проекта». Измените местоположение проекта на любой каталог на вашем компьютере, чтобы сохранить приложение. Нажмите Готово.
  3. Теперь создайте четыре пакета внутри пакета, которые называются «extensionpointinterface», «messagereader», «messageprovider1» и «messageprovider2»:

    1. Снова выберите «Файл»> «Новый проект» (Ctrl-Shift-N). В разделе «Категории» выберите «Модули NetBeans». В разделе «Проекты» выберите «Модуль» и нажмите «Далее».
    2. На панели «Имя и местоположение» введите имя в поле «Имя проекта» (т. Е. Одно из четырех имен, перечисленных в начале этого шага). По умолчанию в мастере следует создать модуль под каталогом, в котором вы только что создали пакет, и это нормально. Нажмите «Далее.
    3. На панели «Конфигурация базового модуля» замените базу кодовых имен на de.eppleton. <Modulename>. Убедитесь, что в среде IDE создан файл слоя, заполнив поле XML Layer файлом de / eppleton / <modulename> /layer.xml. Нажмите Готово.

Вот что вы должны сейчас увидеть:

Создайте интерфейс поставщика услуг

Мы будем использовать модуль «extensionpointinterface» для определения интерфейса, который будет использоваться двумя поставщиками точек расширения, а также модулем «messagereader», который использует две точки расширения. Внутри этого модуля создайте интерфейс «MessageProviderInterface» с единственным методом getMessage (), который возвращает String:

public interface MessageProviderInterface {

public String getMessage();

}

Чтобы сделать эту часть общедоступного API правой кнопкой мыши, щелкните узел проекта, выберите Версии API и установите флажок de.eppleton.extensionpointinterface. Теперь этот пакет доступен для других модулей.

Создание реализаций поставщика услуг

Теперь нам нужны некоторые модули, которые реализуют интерфейс MessageProviderInterface. Для этого мы будем использовать модули «messageprovider1» и «messageprovider2». Для реализации интерфейса им обоим нужна зависимость от модуля «extensionpointinterface». Для каждого из них сделайте следующее:

  1. Щелкните правой кнопкой мыши узел проекта, выберите «Свойства» и в диалоговом окне «Свойства проекта» выберите категорию «Библиотеки».
  2. Нажмите «Добавить зависимость».
  3. Выберите «extensionpointinterface» и дважды нажмите «OK».

Теперь, когда у нас есть доступ к интерфейсу в наших двух провайдерах сообщений, мы можем реализовать его. Опять же, для обоих модулей сделайте следующее:

  1. Выберите «Новый»> «Класс Java».
  2. В мастере введите «MessageProvider1» или «MessageProvider2» в поле «Имя класса», соответственно, и выберите основной пакет в каждом случае для создания нового класса.
  3. Реализуйте интерфейс. Каждый из них должен предоставить свою строку. Другими словами, мы предоставим «Hello» в MessageProvider1, а затем мы предоставим «World!» в MessageProvider2:
import de.eppleton.extensionpointinterface.MessageProviderInterface;
public class MessageProvider1 implements MessageProviderInterface {
public String getMessage() {
return "Hello ";
}
}
import de.eppleton.extensionpointinterface.MessageProviderInterface;
public class MessageProvider2 implements MessageProviderInterface {
public String getMessage() {
return "World!";
}
}
}

Чтобы сделать наши MessageProviders доступными в качестве сервисов, добавьте эти записи в слой двух модулей. Между тегами <filesystem> файла слоя добавьте следующее, т. Е. В первый модуль добавьте первый набор тегов и добавьте второй набор тегов во второй модуль:

<folder name ="MessageProviders">
<file name="de-eppleton-messageprovider1-MessageProvider1.instance" />
</folder>

и

<folder name ="MessageProviders">
<file name="de-eppleton-messageprovider2-MessageProvider2.instance"/>
</folder>

 

Это создаст экземпляр наших MessageProviders с использованием стандартного конструктора. Хитрость заключается в том, что эти экземпляры будут доступны извне модулей через файловую систему системы NetBeans.

Теперь, когда вы создали свои сервисы, на следующем шаге показано, как вы можете получить к ним доступ, даже не устанавливая для них зависимость модуля.

Найдите и используйте поставщиков услуг

Мы будем использовать модуль «messagereader» для отображения сообщений, предоставляемых нашими двумя MessageProviders Для этого мы создадим TopComponent в модуле «messagereader»:

  1. В диалоговом окне «Свойства проекта» для «messagereader» установите зависимость от «extensionpointinterface», как показано ранее.
  2. Щелкните правой кнопкой мыши узел проекта «messagereader» и выберите «Создать»> «Компонент окна». Выберите «Выход», который будет определять, где будет отображаться новое окно. Сделайте так, чтобы он показывался при запуске, поставив галочку. Нажмите «Далее.
  3. Введите «MessageReader» для префикса имени класса и нажмите «Готово».
  4. Класс TopComponent откроется в представлении «Дизайн». Удалите JScrollPane из палитры в окне и сделайте так, чтобы он занимал всю область. Добавьте к нему JTextArea, чтобы он подходил и для всей области.
  5. В представлении исходного кода (т. Е. Нажмите «Источник» в верхней части представления «Дизайн») добавьте его в конец конструктора:
Lookup lkp = Lookups.forPath("MessageProviders");

for (MessageProviderInterface mi : lkp.lookupAll(MessageProviderInterface.class)) {
jTextArea1.append(mi.getMessage());
}

Приведенный выше код будет искать папку, созданную вами через файл слоя (Lookups.forPath («MessageProviders»)), искать классы, реализующие интерфейс (lookupAll (MessageProviderInterface.class)), и вызывать метод интерфейса во всех экземплярах.

Давайте попробуем это!

Запустите пакет модулей. Вы увидите окно с сообщением «Hello World!» или «Мир! Привет». Как мы видим из этого очень простого примера, порядок, в котором вызываются ServiceProviders, может быть важен для результата. Поэтому на следующем шаге я покажу вам трюк, чтобы гарантировать правильный порядок.

Сортировка поставщиков услуг

Платформа NetBeans предоставляет способ сортировки записей слоев. Этот механизм используется, например, для обеспечения порядка действий в меню и панелях инструментов. Начиная с 6.0, это делается с помощью атрибута position в файле слоя. Так что это не будет работать в старых версиях:

В файле слоя двух модулей вставьте атрибуты «position», которые вы видите ниже, т.е. первый набор тегов ниже принадлежит «messageprovider1», а второй — «messageprovider2»:

<folder name = "MessageProviders">
<file name="de-eppleton-messageprovider1-MessageProvider1.instance" >
<attr name="position" intvalue="30"/>
</file>
</folder>

и

<folder name = "MessageProviders">
<file name="de-eppleton-messageprovider2-MessageProvider2.instance" >
<attr name="position" intvalue="40"/>
</file>
</folder>

Теперь сообщения всегда будут отображаться в правильном порядке: «Привет, мир!». Просто поменяйте местами эти значения, чтобы изменить порядок сообщений. Когда вы делаете что-то подобное, убедитесь, что вы выбираете достаточно большие значения, чтобы добавить службы между начальными, чтобы ваше приложение могло расти! Теперь попробуйте и посмотрите, что произойдет, если вы установите одинаковое значение для обоих атрибутов!

Резюме

Цель этой статьи — показать, как просто реализовать слабую связь в приложении на платформе NetBeans. Обратите внимание, что модуль, который использует службы (т. Е. «Средство чтения сообщений»), не зависит от модулей, которые предоставляют службы («messageprovider1» и «messageprovider2»). Платформа NetBeans не только предоставляет очень простой механизм предоставления и поиска сервисов, но также предоставляет простой способ их декларативного заказа.

Приложение: Альтернативный механизм регистрации, Использование «META-INF / services»

В предыдущих разделах я показал, как зарегистрировать поставщиков услуг через файл слоя. Как правильно указано в комментарии к этой статье, Casper Bang предлагает альтернативный способ регистрации поставщиков услуг. Использование папки META-INF / services является стандартным подходом, который поддерживается Java начиная с версии 1.3. Эти дополнительные разделы ниже показывают, как это работает.

Зарегистрируйте Услуги

Есть только несколько небольших изменений, необходимых для изменения механизма регистрации:

  1. В модуле messageprovider1 создайте папку через контекстное меню «Исходные пакеты»> «Создать»> «Прочее»> «Папка». Введите «META_INF / services» в качестве имени папки; папка будет отображаться как обычный пакет.
  2. Создайте файл через контекстное меню этого нового пакета «META-INF.services»> «Новый»> «Другой»> «Пустой файл», назовите файл «de.eppleton.extensionpointinterface.MessageProviderInterface».
  3. Отредактируйте файл и введите «de.eppleton.messageprovider1.MessageProvider1»
  4. Скопируйте и вставьте папку «META_INF» в папку «Исходные пакеты» модуля messageprovider2 и измените содержимое файла «de.eppleton.extensionpointinterface.MessageProviderInterface» на «de.eppleton.messageprovider2.MessageProvider2»

Существуют разные способы поиска услуг. Вы можете использовать поиск по умолчанию для этого или использовать механизм ServiceLoader, представленный в Java 1.6. Давайте начнем с метода, использующего поиск по умолчанию.

Использование Lookup для получения ServiceProviders 

Глобальный поиск автоматически предоставит вам эти услуги, поэтому требуется лишь небольшое изменение. В модуле «messagereader» отредактируйте «MessagereaderTopComponent» и замените эту строку:

        Lookup lkp = Lookups.forPath("MessageProviders");

с этой строкой:

        Lookup lkp = Lookup.getDefault();

После этого вы можете собрать и запустить приложение, как и раньше. Вы можете заметить, что порядок услуг изменился. Как и раньше, вы можете исправить это, добавив дополнительный атрибут позиции при определении ServiceProvider. Отредактируйте оба файла «de.eppleton.extensionpointinterface.MessageProviderInterface» в модулях messageprovider1 и messageprovider2. Добавьте строки «# position = 10» и «# position = 20» соответственно. Запустите приложение и поиграйте со значениями, чтобы изменить порядок, как раньше.

Использование ServiceLoader для поиска ServiceProviders

Если вы используете JDK 1.6 или более поздней версии, вы можете отредактировать «MessagereaderTopComponent» и заменить код, который мы добавили ранее, следующим:

ServiceLoader<MessageProviderInterface> serviceLoader = ServiceLoader.load(MessageProviderInterface.class); 
for (MessageProviderInterface mi : serviceLoader) {
     jTextArea1.append(mi.getMessage());
}

Обратите внимание, что атрибут position теперь игнорируется, поскольку он не является частью стандартного JDK, а является расширением, добавленным платформой NetBeans.