问题
Defining an @Singleton bean in Micronaut does not @Inject the same instance into Picocli commands.
Micronaut offers an integration with Picocli. What is essential done, it seems, is that from the Picocli command, one can start an EmbeddedServer of Micronaut (maybe the problem is already here that Micronaut is started only really started from within Picocli?). When I define a class as singleton via @Singleton, and @Inject it both in a Rest-endpoint of Micronaut and the Picocli command, it notice that these are two different instances and the state is not the same. What I essential want is to transport some state provided via the commandline interface to configure the backend/Rest-service. For now I have just created static instance to share this state but i am wondering if I can get the dependency-injection to work properly between Picocli and 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();
}
}
If I set some state in the SharedState instance in the UICommand's run()-method, I expect to be able to read it from the RestResource. So, i would expect to get back "42" when i call the rest endpoint.
Is there some way to set up Micronaut/Picocli in some way so the dependency-injection container of Micronaut/Picocli is started early and shared? Or is Micronaut really only started with the EmbeddedServer call? In that case, what options to I have to still have some interoperability? Can i explicitly ask Micronaut's DI container for the instance in some way?
回答1:
I believe that the problem is that the code in the question creates two separate ApplicationContext
instances.
The call to PicocliRunner.run(UICommand.class, args)
under the hood creates an ApplicationContext, while the UICommand.run
method calls ApplicationContext.run(EmbeddedServer.class)
, which starts another ApplicationContext
instance.
One way to solve this may be to inject the ApplicationContext
instead of starting a new one:
@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);
}
}
来源:https://stackoverflow.com/questions/56730965/interoperability-between-injections-in-picocli-commands-and-micronaut