Launcher
Обзор
Launcher - это, по сути, высоко абстрагированная и обобщенная реализация метода main()
. Он объединяет модули и сервисы вместе и управляет жизненным циклом приложения.
Характеристики
- Идеальная совместимость с ActiveJ Inject
- Может быть объединен с Service Graph для поддержки запуска/остановки независимых услуг.
- Предоставляет возможность плавного отключения служб
- Обеспечивает полный жизненный цикл приложения и ведение журнала
Launcher в двух словах
Launcher
- это очень явный инструмент. Вы можете понять, как он работает, просто посмотрев на реализацию его основного метода launch()
в двух словах:
public void launch(String[] args) throws Exception { logger.info("=== INJECTING DEPENDENCIES"); Injector injector = createInjector(args); logger.info("Eager instances: " + injector.createEagerInstances()); Set<LauncherService> services = injector.getInstance(new Key<Set<LauncherService>>() {}); Set<LauncherService> startedServices = new HashSet<>(); logger.info("Post-inject instances: " + injector.postInjectInstances());
logger.info("=== STARTING APPLICATION"); logger.info("Starting LauncherServices: " + services); startServices(services, startedServices); onStart(); onStartFuture.complete(null);
logger.info("=== RUNNING APPLICATION"); run(); onRunFuture.complete(null);
logger.info("=== STOPPING APPLICATION"); onStop(); stopServices(startedServices); onCompleteFuture.complete(null);
Пошаговый экзамен:
- Создать
Injector
сМодули
предоставлен вLauncher
- Инжектируйте зависимости верхнего уровня в сам экземпляр
Launcher
. - Установите все ключи, отмеченные как
@Eager
многосвязующая группа клавиш, экспортируемая модулямиLauncher.
- Запустите все службы из набора
Set<LauncherService>
multibinder set, экспортированного модулями Launcher's Modules, и выполните методonStart()
. - Выполнить
run()
метод - После завершения работы
run()
(либо в результате завершения всех специфических для приложения вычислений, либо в ответ на запрос о завершении работы, например SIGKILL), выполните методonStop()
и остановите все службы
Вот пример жизненного цикла Launcher
, представленного в виде журналов (в частности, HttpServerLauncher
подкласс, который предоставляет AsyncHttpServer
, an Eventloop
и Config
). Запустите этот пример:
public final class HttpHelloWorldExample extends HttpServerLauncher { @Provides AsyncServlet servlet() { return request -> HttpResponse.ok200() .withPlainText("Hello World") .toPromise(); }
public static void main(String[] args) throws Exception { Launcher launcher = new HttpHelloWorldExample(); launcher.launch(args); }}
для просмотра похожих журналов:
=== INJECTING DEPENDENCIESCreated eager singletonsPost-injected instances: [HttpHelloWorldExample]
=== STARTING APPLICATIONStarting LauncherServices: [io.active.net.jmx.JmxModule, io.active.net.service.ServiceGraphModule]Starting LauncherService: io.active.net.jmx.JmxModuleStarting LauncherService: io.active.net.service.ServiceGraphModuleCreating ServiceGraph...Запуск службПрослушивание на [/0.0.0.0:8080]: AsyncHttpServer{listenAddresses=[/0.0.0.0:8080]}Started io.active.net.http.AsyncHttpServer
=== RUNNING APPLICATIONHTTP Server is listening on http://localhost:8080/
=== STOPPING APPLICATIONStopping LauncherService: io.active.net.jmx.JmxModuleStopping LauncherService: io.active.net.service.ServiceGraphModuleStopping ServiceGraph...Остановка услуг
Launcher
опционально требует следующих зависимостей от своих Модулей:@Eager
многосвязующая ключевая группаНабор<LauncherService>
многопереплетный набор
Launcher
экспортирует следующие зависимости в свои Модули:
class Launcher{ @NotNull public final Injector createInjector() { return Injector.of(getInternalModule() .combineWith(getModule()) .overrideWith(getOverrideModule())); }
private Module getInternalModule() { Class<Launcher> launcherClass = (Class<Launcher>) getClass(); Key<CompletionStage<Void>> completionStageKey = new Key<CompletionStage<Void>>() {};
return Module.create() .bind(String[].class).annotatedWith(Args.class).toInstance(args) .bind(Launcher.class).to(launcherClass) .bind(launcherClass).toInstance(this)
.postInjectInto(launcherClass) .bind(completionStageKey.named(OnStart.class)).toInstance(onStartFuture) .bind(completionStageKey.named(OnRun.class)).toInstance(onRunFuture) .bind(completionStageKey.named(OnComplete.class)).toInstance(onCompleteFuture)
.scan(Launcher.this); }
// этот метод может быть переопределен подклассами, которые расширяют Launcher, // предоставляют модули бизнес-логики (например, ConfigModule) protected Module getModule() { return Module.empty(); }
// этот метод может быть переопределен подклассами, расширяющими Launcher, // переопределяет определения во внутреннем модуле protected Module getOverrideModule() { return Module.empty(); }}
@Args String[]
аргументы своего методаLauncher.launch(String[] args)
, соответствующего методуmain(String[] args)
.- Сам экземпляр
Launcher
@OnStart
CompletionStage<Void>
будущее, которое завершается, когда приложение подключено и запущено.@OnRun
CompletionStage<Void>
будущее, которое завершается, когдаLauncher.run()
завершается.@OnComplete
CompletionStage<Void>
будущее, которое завершается, когда приложение остановлено.
Launcher
также имеет удобные диагностические и JMX свойства:
- Мгновенный запуск, запуск, запуск, остановка и завершение
- Продолжительность старта, пробега, остановки и общая продолжительность
- Throwable applicationError свойство
Больше примеров
note
Чтобы запустить примеры, необходимо клонировать ActiveJ с GitHub
git clone https://github.com/activej/activej
И импортируйте его как проект Maven. Посмотрите тег v6.0-beta2. Перед запуском примеров выполните сборку проекта. Эти примеры расположены по адресу activej/examples/core/datastream.
Пример Hello World
Вот простое приложение "Hello World" Launcher с использованием ActiveJ Inject:
public final class HelloWorldExample extends Launcher { @Inject String message;
@Provides String message() { return "Hello, world!"; }
@Override protected void run() { System.out.println(message); }
public static void main(String[] args) throws Exception { Launcher launcher = new HelloWorldExample(); launcher.launch(args); }}
Полный текст примера смотрите на GitHub.
HTTP-сервер с нуля с помощью Launcher
При создании HTTP-серверов или любых других приложений вы можете использовать либо некоторые предопределенные решения (см. примеры), либо простой Launcher. В этом случае вам нужно будет позаботиться обо всех необходимых зависимостях и переопределить некоторые основные методы:
public final class CustomHttpServerExample extends Launcher { private static final int PORT = 8080;
@Provides NioReactor reactor() { return Eventloop.create(); }
@Provides AsyncServlet servlet() { return request -> HttpResponse.ok200() .withPlainText("Hello from HTTP server") .toPromise(); }
@Provides @Eager HttpServer server(NioReactor reactor, AsyncServlet servlet) { return HttpServer.builder(reactor, servlet) .withListenPort(PORT) .build(); }
@Override protected Module getModule() { return ServiceGraphModule.create(); }
@Override protected void run() throws Exception { logger.info("HTTP Server is now available at http://localhost:" + PORT); awaitShutdown(); }
public static void main(String[] args) throws Exception { Launcher launcher = new CustomHttpServerExample(); launcher.launch(args); }}
Полный текст примера смотрите на GitHub.
Работа с аргументами программы
Чтобы получить доступ к аргументам программы внутри Launcher
, необходимо передать эти аргументы в метод Launcher#launch
. Тогда существует два способа доступа к аргументам: - Использование Launcher
's String[] args
field - Запрос аргументов с помощью Dependency Injection. Ключ - это String[].class
, аннотированный аннотацией @Args
. Следующий пример иллюстрирует оба случая:
public final class LauncherArgsExample extends Launcher {
@Inject Injector injector;
@Override protected void run() { System.out.println("Received args: " + Arrays.toString(args));
String[] injectedArgs = injector.getInstance(Key.of(String[].class, Args.class)); System.out.println("Args retrieved from DI: " + Arrays.toString(injectedArgs)); }
public static void main(String[] args) throws Exception { new LauncherArgsExample().launch(args); }}