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