Архитектура большинства Java(и не только) приложений сегодня предусматривает возможность расширения функционала посредством различного рода
магических воздействий на код. В последнее время это также стало возможно, если использовать какой-нибудь модный
фреймворк или IoC-контейнер. Но что делать, если приложение долгоживущее и слишком сложное для того, чтобы переводить его на использование какого либо фреймворка?
В последнем приложении, с которым я работал, был реализован на тот момент неизвестный мне
велосипед SPI механизм, который искал в джарках текстовые файлы вида
META-INF/services/<qualified interface name> и брал оттуда название нужного класса, реализующего этот интерфейс, далее этот класс использовался как расширение. Поискав в интернете, узнал, что
Service Provider Interface(SPI) представляет собой программный механизм для поддержки сменных компонентов и что этот механизм уже довольно давно используется в
Java Runtime Environment(JRE), например в
Java Database Connectivity(JDBC):
ps = Service.providers(java.sql.Driver.class);
try {
while (ps.hasNext()) {
ps.next();
}
} catch (Throwable t) {
// Do nothing
}
Благодаря этому коду приложения больше не нуждаются в конструкции
Class.forName(<driver class>) (хотя и с ней будут работать), JDBC драйверы будут подгружены автоматически при первом обращении к методам класса
DriverManager.
SPI механизм также используется в
Java Cryptography Extension(JCE),
Java Naming and Directory Service(JNDI),
Java API for XML Processing(JAXP),
Java Business Integration(JBI), Java Sound, Java Image I/O.
Как это работает?
Весь смысл в разделении логики на сервис(Service) и провайдеры(Service Providers). Ссылки на провайдеры сохраняются в джарках расширений в текстовом файле(UTF-8)
META-INF/services/<qualified service class>, в каждой строке полное имя класса провайдера. Пустые строки и комментарии(начинающиеся с символа #) игнорируются. Ограничения на провайдеры: они должны реализовывать интерфейс либо наследоваться от класса сервиса и иметь конструктор по умолчанию(zero-argument public constructor).