Определение bean-компонента @Singleton в Micronaut не приводит к @Inject того же экземпляра в команды Picocli.
Micronaut предлагает интеграцию с Пикокли. Что важно сделать, кажется, так это то, что из команды Picocli можно запустить EmbeddedServer Micronaut (может быть, проблема уже здесь, что Micronaut запускается только из Picocli?). Когда я определяю класс как синглтон через @Singleton и @Inject его как в конечной точке Rest в Micronaut, так и в команде Picocli, он замечает, что это два разных экземпляра и состояние не одно и то же. Что мне действительно нужно, так это передать некоторое состояние, предоставленное через интерфейс командной строки, для настройки бэкэнда/Rest-сервиса. На данный момент я только что создал статический экземпляр, чтобы поделиться этим состоянием, но мне интересно, смогу ли я заставить инъекцию зависимостей работать правильно между Picocli и Micronaut.
@Singleton
public class SharedState {
private int num;
public void setNum(int num) { this.num = num };
public int getNum() { return this.num; };
}
@Command(name = "ui", description = "...", mixinStandardHelpOptions = true)
public class UICommand implements Runnable {
@Inject
SharedState state;
public static void main(String[] args) throws Exception {
PicocliRunner.run(UICommand.class, args);
}
public void run() {
EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class);
state.setNum(42);
}
}
@Controller("/rest")
public class RestResource{
@Inject
SharedState state;
@Get
public String get() {
return state.getNum();
}
}
Если я устанавливаю некоторое состояние в экземпляре SharedState в методе run() UICommand, я ожидаю, что смогу прочитать его из RestResource. Итак, я ожидаю получить обратно «42», когда я вызову остальную конечную точку.
Есть ли способ каким-то образом настроить Micronaut/Picocli, чтобы контейнер для внедрения зависимостей Micronaut/Picocli запускался раньше и использовался совместно? Или Micronaut действительно запускается только с вызова EmbeddedServer? В таком случае, какие у меня есть варианты, чтобы по-прежнему иметь некоторую совместимость? Могу ли я каким-то образом явно запросить контейнер DI Micronaut для экземпляра?
Добавлен основной метод.
Я считаю, что проблема в том, что код в вопросе создает два отдельных экземпляра ApplicationContext
.
Вызов PicocliRunner.run(UICommand.class, args)
под капотом создает ApplicationContext, в то время как метод UICommand.run
вызывает ApplicationContext.run(EmbeddedServer.class)
, который запускает другой экземпляр ApplicationContext
.
Одним из способов решения этой проблемы может быть внедрение ApplicationContext
вместо запуска нового:
@Command(name = "ui", description = "...", mixinStandardHelpOptions = true)
public class UICommand implements Runnable {
@Inject
SharedState state;
@Inject
ApplicationContext appContext;
public static void main(String[] args) throws Exception {
PicocliRunner.run(UICommand.class, args);
}
public void run() {
// start the injected, shared, application context (not a new instance)
if (!appContext.isRunning()) { // future versions of PicocliRunner may start the context
appContext.start();
}
// start the embedded server
EmbeddedServer server = appContext.getBean(EmbeddedServer.class);
if (!server.isRunning()) {
server.start();
}
state.setNum(42);
}
}
Команда Micronaut подтвердила возможность внедрения контекста приложения. Вышеупомянутое решило проблему?
Хорошо, я начинаю понимать, в чем проблема. Но я не могу последовать вашему совету, так как этот код не компилируется. Метод запуска — это статический метод ApplicationContext; не метод экземпляра.
Ты прав. Я обновил свой ответ. Я не уверен, что все bean-компоненты, которые реализуют LifeCycle
(например, EmbeddedServer
), автоматически запускаются при запуске контекста приложения, поэтому я явно запускаю EmbeddedServer
в приведенном выше примере, но вы можете опустить это.
Спасибо, это сработало. Пришлось запустить сервер, но не контекст.
Можете ли вы показать метод
main
, в котором вызывается ваша команда CLI 'ui'? Вы используете один из методовPicocliRunner
?