В зависимости от ваших конкретных требований, в некоторых случаях механизм загрузки сервиса Java может достичь того, что вам нужно.
Короче говоря, он позволяет разработчикам явно объявить, что класс подклассов какого-то другого класса (или реализует некоторый интерфейс), перечислив его в файл в каталоге META-INF/services
файла JAR / WAR. Затем его можно обнаружить с помощью класса java.util.ServiceLoader
, который при задании объекта Class
будет генерировать экземпляры всех объявленных подклассов этого класса (или, если Class
представляет интерфейс, все классы, реализующие этот интерфейс).
Основным преимуществом этого подхода является то, что нет необходимости вручную сканировать весь путь к классам для подклассов - вся логика обнаружения содержится в классе ServiceLoader
, и он загружает только классы, явно объявленные в META-INF/services
каталог (не каждый класс на пути к классам).
Есть, однако, некоторые недостатки:
- Он не найдет всех подклассов, только те, которые явно объявлены. Таким образом, если вам нужно действительно найти все подклассы, такой подход может оказаться недостаточным.
- Требуется, чтобы разработчик явно объявил класс в каталоге
META-INF/services
. Это является дополнительным бременем для разработчика и может быть подвержено ошибкам.
-
ServiceLoader.iterator()
генерирует экземпляры подкласса, а не их Class
объекты. Это вызывает две проблемы:
- Вы не можете сказать, как создаются подклассы - конструктор no-arg используется для создания экземпляров.
- Как таковые, подклассы должны иметь конструктор по умолчанию или должны явно указывать конструктор без аргументов.
Очевидно, в 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.