Перейти к основному содержанию

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);  }}

Полный текст примера смотрите на GitHub.