问题
I'm trying to initialize some components in my Jersey application in the Application constructor (the thing that inherits from ResourceConfig) . It looks like this
public Application(@Context ServletContext context,
@Context ServiceLocator locator)...
When I try to use the locator at any point, I still can't create instances of things that I have registered in an AbstractBinder using the locator.create(MyThing.class) method.
I'm certain that they are bound correctly because they are injected properly into my resource classes via the @inject field annotation.
The difference is that the Jersey/HK2 framework is instantiating my resource classes (as expected, since they're in my package scan path), but I can not seem to leverage the ServiceLocator through code.
My ultimate goal is to have other non-jersey classes injected when they have the @Inject attribute, eg. I have a worker class that needs to be injected with the configured database access layer. I want to say
locator.Create(AWorker.class)
and have it injected.
How do I get the real ServiceLocator that will inject everything I've already registered/bound with my Binder? (Or should I be using something other than ServiceLocator?)
回答1:
How are you starting up your container? If you are using ApplicationHandler, you can just call:handler.getServiceLocator()
. The ServiceLocator is, indeed, what you want to be using to access your dependencies.
If you are starting up a servlet, I found that the best way to get access to the service locator was to have a Jersey feature set it on my startup class:
private static final class LocatorSetFeature implements Feature {
private final ServiceLocator scopedLocator;
@Inject
private LocatorSetFeature(ServiceLocator scopedLocator) {
this.scopedLocator = scopedLocator;
}
@Override
public boolean configure(FeatureContext context) {
locator = this.scopedLocator; // this would set our member locator variable
return true;
}
}
The feature would just be registered with our resource config with config.register(new LocatorSetFeature()).
It would be important to tie in startup of other components based on the lifecycle of your container, so this still feels a bit hacky. You might consider adding those classes as first class dependencies in the HK2 container and simply injecting the appropriate dependencies into your third party classes (using a Binder, for example).
回答2:
I am going to assume you are starting up a servlet and have a class extending org.glassfish.jersey.server.ResourceConfig
and your bindings are correctly registered (e.g. using a Binder and registerInstances). If you then want to access the ServiceLocator in order to perform additional initialization, you have two choices:
One approach is to register a ContainerLifecycleListener (as seen here in this post):
// In Application extends ResourceConfig constructor
register(new ContainerLifecycleListener() {
@Override
public void onStartup(final Container container) {
// access the ServiceLocator here
final ServiceLocator serviceLocator = container.getApplicationHandler().getInjectionManager().getInstance(ServiceLocator.class);
// Perform whatever with serviceLocator
}
@Override
public void onReload(final Container container) {
/* ... */}
@Override
public void onShutdown(final Container container) {
/* ... */}
});
The second approach is to use a Feature
, which can also be auto-discovered using @Provider
:
@Provider
public final class StartupListener implements Feature {
private final ServiceLocator sl;
@Inject
public ProvisionStartupListener(final ServiceLocator sl) {
this.sl = sl;
}
@Override
public boolean configure(final FeatureContext context) {
// Perform whatever action with serviceLocator
return true;
}
来源:https://stackoverflow.com/questions/21149161/jersey-and-hk2-servicelocator