В зависимости от ваших конкретных требований, в некоторых случаях механизм загрузчика сервисов Java может достичь того, что вам нужно.
Короче говоря, это позволяет разработчикам явно объявить, что класс подклассирует некоторый другой класс (или реализует некоторый интерфейс), перечислив его в файл в каталоге файла JAR / WAR META-INF/services
. Затем его можно обнаружить с помощью java.util.ServiceLoader
класса, который приClass
объекта, будет генерировать экземпляры всех объявленных подклассов этого класса (или, еслиClass
представляет интерфейс, все классы, реализующие этот интерфейс).
Основное преимущество этого подхода состоит в том, что нет необходимости вручную сканировать весь путь к классам для поиска подклассов - вся логика обнаружения содержится внутри ServiceLoader
класса, и он загружает только классы, явно объявленные вMETA-INF/services
каталоге (не каждый класс в пути к классам) ,
Есть, однако, некоторые недостатки:
- Он не найдет все подклассы, только те, которые явно объявлены. Таким образом, если вам нужно действительно найти все подклассы, такой подход может оказаться недостаточным.
- Это требует от разработчика явного объявления класса в
META-INF/services
каталоге. Это дополнительная нагрузка на разработчика и может быть подвержена ошибкам.
ServiceLoader.iterator()
Создает экземпляры подкласса, а не ихClass
объекты. Это вызывает две проблемы:
- Вы не можете сказать, как создаются подклассы - конструктор без аргументов используется для создания экземпляров.
- Таким образом, у подклассов должен быть конструктор по умолчанию, или он должен объявлять конструктор без аргументов.
Очевидно, в Java 9 будут устранены некоторые из этих недостатков (в частности, те, которые касаются создания экземпляров подклассов).
Пример
Предположим, вы заинтересованы в поиске классов, которые реализуют интерфейс com.example.Example
:
package com.example;
public interface Example {
public String getStr();
}
Класс com.example.ExampleImpl
реализует этот интерфейс:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Вы бы объявили, что класс ExampleImpl
является реализацией Example
, создав файл, META-INF/services/com.example.Example
содержащий текст com.example.ExampleImpl
.
Затем вы можете получить экземпляр каждой реализации Example
(включая экземпляр ExampleImpl
) следующим образом:
ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
System.out.println(example.getStr());
}
// Prints "ExampleImpl's string.", plus whatever is returned
// by other declared implementations of com.example.Example.