HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Теги: java, java se, service provider interface (spi), jar-архив, \meta-inf\services, интерфейс для группы модулей, стратегия упаковки классов-реализаций в jar-архивы, classloader, загрузка классов-реализаций из jar-архива</p>
1 <p>Теги: java, java se, service provider interface (spi), jar-архив, \meta-inf\services, интерфейс для группы модулей, стратегия упаковки классов-реализаций в jar-архивы, classloader, загрузка классов-реализаций из jar-архива</p>
2 <p>Вот представьте: повысили вас до ведущего разработчика! Не номинально, а по-настоящему - будете отвечать за техническое развитие продукта. И вот заходите вы в почту, естественно, чтобы должность в подписи поменять, а там...</p>
2 <p>Вот представьте: повысили вас до ведущего разработчика! Не номинально, а по-настоящему - будете отвечать за техническое развитие продукта. И вот заходите вы в почту, естественно, чтобы должность в подписи поменять, а там...</p>
3 <p>Вместе с полномочиями пришла и ответственность: - пользователи не хотят больше устанавливать дистрибутив приложения целиком, они хотят устанавливать только то, что планируют использовать; - техническая поддержка просит реализовать наконец возможность автоматического обновления приложения; - разработчики тоже ругают долгий цикл доставки обновлений пользователям; - отдел продаж требует технически поддержать новую модель гибкого ценообразования - теперь пользователь покупает только нужный ему функционал; - партнёры хотят создавать собственные расширения к продукту; - юристы перестраховываются и требуют убрать из состава дистрибутива коммерческого продукта ту часть функционала, которая использует некоторые open source библиотеки, чтобы избежать необходимости публикации исходников...</p>
3 <p>Вместе с полномочиями пришла и ответственность: - пользователи не хотят больше устанавливать дистрибутив приложения целиком, они хотят устанавливать только то, что планируют использовать; - техническая поддержка просит реализовать наконец возможность автоматического обновления приложения; - разработчики тоже ругают долгий цикл доставки обновлений пользователям; - отдел продаж требует технически поддержать новую модель гибкого ценообразования - теперь пользователь покупает только нужный ему функционал; - партнёры хотят создавать собственные расширения к продукту; - юристы перестраховываются и требуют убрать из состава дистрибутива коммерческого продукта ту часть функционала, которая использует некоторые open source библиотеки, чтобы избежать необходимости публикации исходников...</p>
4 <h2>Как разрубить этот "гордиев узел"?</h2>
4 <h2>Как разрубить этот "гордиев узел"?</h2>
5 <p>Разделить монолитное приложение на модули! Модули могут загружаться по требованию и обновляться при необходимости в процессе работы основного приложения.</p>
5 <p>Разделить монолитное приложение на модули! Модули могут загружаться по требованию и обновляться при необходимости в процессе работы основного приложения.</p>
6 <p><strong>А как это сделать?</strong>Выбор велик. Можно просидеть все выходные, изобретая собственный "велосипед". Можно вооружиться кофе-машиной и погрузиться в изучение вселенной<strong>OSGi</strong>...</p>
6 <p><strong>А как это сделать?</strong>Выбор велик. Можно просидеть все выходные, изобретая собственный "велосипед". Можно вооружиться кофе-машиной и погрузиться в изучение вселенной<strong>OSGi</strong>...</p>
7 <p>А можно вспомнить, чему учат на<a>курсе OTUS</a>: в состав<strong>Java SE</strong>входит технология<strong>Service Provider Interface (SPI)</strong>.</p>
7 <p>А можно вспомнить, чему учат на<a>курсе OTUS</a>: в состав<strong>Java SE</strong>входит технология<strong>Service Provider Interface (SPI)</strong>.</p>
8 <h2>О ней и пойдёт речь далее</h2>
8 <h2>О ней и пойдёт речь далее</h2>
9 <p>Технология SPI позволяет разделить сервисы (бизнес-логику) приложения на интерфейс и его реализации. Таким образом, реализация сервиса может распространяться в виде отдельного jar-архива и включаться в работу уже в процессе работы основного приложения.</p>
9 <p>Технология SPI позволяет разделить сервисы (бизнес-логику) приложения на интерфейс и его реализации. Таким образом, реализация сервиса может распространяться в виде отдельного jar-архива и включаться в работу уже в процессе работы основного приложения.</p>
10 <p>Эта технология, например, применяется в хорошо известной вам JDBC для загрузки драйвера конкретной СУБД.</p>
10 <p>Эта технология, например, применяется в хорошо известной вам JDBC для загрузки драйвера конкретной СУБД.</p>
11 <h2>На практике это выглядит так:</h2>
11 <h2>На практике это выглядит так:</h2>
12 <p><strong>1) Создаёте общий интерфейс для группы ваших модулей</strong>Сигнатуры методов интерфейса могут быть абсолютно любыми. Например:</p>
12 <p><strong>1) Создаёте общий интерфейс для группы ваших модулей</strong>Сигнатуры методов интерфейса могут быть абсолютно любыми. Например:</p>
13 public interface Plugin { Object execute(Object input); }<p><strong>2) Создаёте необходимое количество классов-реализаций ваших модулей</strong>Например:</p>
13 public interface Plugin { Object execute(Object input); }<p><strong>2) Создаёте необходимое количество классов-реализаций ваших модулей</strong>Например:</p>
14 public class EchoPlugin implements Plugin { public Object execute(Object input) { // Делаем что-то полезное return input; } }<p><strong>3) Определяете стратегию упаковки классов-реализаций в jar-архивы</strong>В один jar-архив может быть упакован один или несколько классов-реализаций. Как правило, один архив содержит реализацию одного логически целостного модуля приложения. Внутри каждого jar-архива в каталоге<strong>\META-INF\services</strong>должен находится текстовый файл в кодировке UTF-8, название которого совпадает с полным именем интерфейса, созданного в п.1, например,<strong>ru.otus.Plugin</strong>. В тексте файла должны быть указаны полные имена классов-реализаций, содержащихся в jar-архиве, например:</p>
14 public class EchoPlugin implements Plugin { public Object execute(Object input) { // Делаем что-то полезное return input; } }<p><strong>3) Определяете стратегию упаковки классов-реализаций в jar-архивы</strong>В один jar-архив может быть упакован один или несколько классов-реализаций. Как правило, один архив содержит реализацию одного логически целостного модуля приложения. Внутри каждого jar-архива в каталоге<strong>\META-INF\services</strong>должен находится текстовый файл в кодировке UTF-8, название которого совпадает с полным именем интерфейса, созданного в п.1, например,<strong>ru.otus.Plugin</strong>. В тексте файла должны быть указаны полные имена классов-реализаций, содержащихся в jar-архиве, например:</p>
15 ru.otus.EchoPlugin ru.otus.CopyPlugin<p><strong>4) В программном коде основного приложения создаёте ClassLoader для загрузки классов-реализаций из jar-архива</strong></p>
15 ru.otus.EchoPlugin ru.otus.CopyPlugin<p><strong>4) В программном коде основного приложения создаёте ClassLoader для загрузки классов-реализаций из jar-архива</strong></p>
16 URL[] moduleUrls = new URL[]{new URL("http://example.com/module-1.0.jar")}; URLClassLoader urlClassLoader = new URLClassLoader(moduleUrls);<p><strong>5) А затем, получаете и используете реализации необходимых вам модулей</strong></p>
16 URL[] moduleUrls = new URL[]{new URL("http://example.com/module-1.0.jar")}; URLClassLoader urlClassLoader = new URLClassLoader(moduleUrls);<p><strong>5) А затем, получаете и используете реализации необходимых вам модулей</strong></p>
17 for (final Plugin plugin : ServiceLoader.load(Plugin.class, urlClassLoader)) { Object result = plugin.execute(new Object()); }<h2>Всё, пользуйтесь!</h2>
17 for (final Plugin plugin : ServiceLoader.load(Plugin.class, urlClassLoader)) { Object result = plugin.execute(new Object()); }<h2>Всё, пользуйтесь!</h2>
18 <p><em>Есть вопрос? Напишите в комментариях!</em></p>
18 <p><em>Есть вопрос? Напишите в комментариях!</em></p>
19  
19