Вопрос или проблема
Я разрабатываю библиотеку на Java, где хочу, чтобы другой Java-код мог подключаться на различных жизненных циклах.
Подход, который я рассматриваю, это определение различных интерфейсов (для различных точек подключения). В момент выполнения находить всех реализаторов этих классов (которые уже доступны в classpath). Запускать их в соответствующее время. Я, вероятно, смогу использовать Reflections для этого.
Проблема использования Reflections заключается в том, что я диктую подход.
Другой подход может заключаться в том, что у меня есть необязательная зависимость от этих интерфейсов и я использую внедрение зависимостей (например, Dagger), чтобы инжектировать экземпляр. Код, использующий эту библиотеку, затем может инжектировать конкретный класс, который выполняет всю магию Reflections, описанную выше, и предоставляет описанную там функциональность, если это необходимо. Этот подход кажется мне более гибким.
Пожалуйста, предложите, есть ли другие лучшие доступные подходы?
Вам стоит обратить внимание на систему, такую как HK2 (https://hk2.java.net/2.5.0-b30/). Она специально разработана для поиска сервисов и является динамичной для включения и отключения кода во время выполнения. Она используется для ядра WebLogic, ядра GlassFish и ядра Jersey, поэтому поддержка хорошая. Она предоставляет как интерфейс типа реестра (ServiceLocator), так и полностью функционирующую систему DI, включая события, перехват и многие другие функции.
Ответ или решение
При разработке библиотеки на Java, которая поддерживает возможность подключения плагинов для взаимодействия с различными жизненными циклами, важно продумать архитектуру, которая обеспечит гибкость и модульность. В данном контексте можно рассмотреть несколько подходов к созданию универсальной архитектуры плагинов.
1. Интерфейсы и рефлексия
На первом этапе вы могли бы рассмотреть возможность определения нескольких интерфейсов, которые будут представлять различные точки подключения в жизненном цикле библиотеки. Например, вы можете создать интерфейсы для инициализации, обработки запросов, завершения работы и так далее. После этого для нахождения всех имплементаций этих интерфейсов можно использовать библиотеку Reflections. Однако стоит отметить, что использование Reflections предполагает определенный подход к организации кода и может ограничивать пользователей вашей библиотеки.
2. Внедрение зависимостей
Второй подход заключается в использовании внедрения зависимостей (DI). Применение таких библиотек, как Dagger, позволяет пользователям вашей библиотеки самостоятельно управлять экземплярами и их конфигурациями. Вместо того чтобы напрямую зависеть от конкретных реализаций, вы выставляете интерфейсы как опциональные зависимости, которые могут быть реализованы пользователями. Этот метод обеспечивает большую гибкость, так как пользователи могут легко подключать свои реализации и управлять ими через механизм DI.
3. Использование HK2
Рекомендуется также обратить внимание на систему DI, такую как HK2, которая обеспечивает динамическое управление жизнями объектов, включая их включение и отключение во время выполнения. HK2 использует шаблон «Сервисный локатор» и поддерживает множество функций, таких как событийность и перехватчики запросов. Это делает его хорошим выбором для библиотек, использующих системы, такие как WebLogic, GlassFish и Jersey.
4. События и обработчики
Другим полезным подходом является интеграция механизма событий и обработчиков. Вы можете создать общую архитектуру событий, где пользователи вашей библиотеки смогут подписываться на определенные события и обрабатывать их с помощью своих плагинов. Это обеспечивает не только модульность, но и возможность для библиотек расширять функциональность, не меняя основного кода.
5. Конфигурационные файлы
Наконец, использование конфигурационных файлов для регистрации плагинов может значительно упростить процесс интеграции. Вы можете предусмотреть шаблон конфигурационного файла (например, в формате JSON или XML), где пользователи вашей библиотеки смогут описывать свои плагины и их зависимости. При запуске вашей библиотеки можно будет считывать этот конфигурационный файл и автоматически загружать плагины.
Заключение
Таким образом, при разработке библиотеки, поддерживающей архитектуру плагинов, вы можете использовать различные подходы, такие как интерфейсы с рефлексией, внедрение зависимостей, события, обработчики и конфигурационные файлы. Выбор того или иного метода зависит от сложности и целей вашей библиотеки. Учтите, что комбинация этих подходов может предоставить еще больше гибкости и возможностей для пользователей вашей библиотеки.