Java Guice DI error: UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl
Java Guice DI error: UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl
我有一个使用 Jersey 2.x 的简单 REST API 项目。我尝试使用 Google Guice 来注入我的依赖项,但它似乎不起作用。我收到此错误:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=AccountService,parent=AccountsResource,qualifiers={},position=0,optional=false,self=false,unqualified=null,1658198405)
我有这个简单的资源class
@Path("/accounts")
@Produces(MediaType.APPLICATION_JSON)
public class AccountsResource {
private final AccountService accountService;
@Inject
public AccountsResource(AccountService accountService) {
this.accountService = accountService;
}
@GET
@Path("test")
public String test() {
return this.accountService.test();
}
我想将此服务注入我的资源class
public class AccountService {
public AccountService() {}
public String test() {
return "test";
}
}
所以,按照 Guice 的指导,我创建了这个模块 class
import com.google.inject.*;
public class AccountsResourceModule extends AbstractModule {
@Override
protected void configure() {
bind(AccountService.class);
}
}
最后,我在我的主要方法中添加了注入器
public class TradingServer implements Runnable {
private static final int PORT = 8181;
public static void main(String[] args) {
Injector injector = Guice.createInjector(new AccountsResourceModule());
AccountsResource accountsResource = injector.getInstance(AccountsResource.class);
new TradingServer().run();
}
public void run() {
Server server = new Server(PORT);
ServletContextHandler contextHandler = new ServletContextHandler(server, "/");
ResourceConfig packageConfig = new ResourceConfig().packages("ca.ulaval.glo4002.trading");
ServletContainer container = new ServletContainer(packageConfig);
ServletHolder servletHolder = new ServletHolder(container);
contextHandler.addServlet(servletHolder, "/*");
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
} finally {
server.destroy();
}
}
}
当我调用服务器时,出现上述错误。似乎依赖注入不起作用。请帮忙
所以 Jersey 对 Guice 一无所知。它已经使用了自己的 DI 框架 HK2。您可以做几件事。您可以将 Guice 与 HK2 绑定在一起,以便 HK2 可以找到绑定在 Guice 内部的服务,或者另一种方法是将您的资源 classes 绑定在 Guice 中,并向 Jersey 注册这些资源的实例。
将 Guice 与 HK2 绑定
要将 Guice 与 HK2 绑定,您需要使用 Guice HK2 Bridge。首先需要添加如下依赖
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>${hk2.version}</version>
</dependency>
要获得 hk2.version
,请查看您的 Jersey 依赖项(您可以 运行 mvn dependency:tree
并查看引入的 HK2 Jersey 版本)。您要确保使用的是完全相同的版本。
接下来您需要做的是以编程方式 link 这两个系统。一种方法是在 Feature
.
中
public class GuiceFeature implements Feature {
@Override
public boolean configure(FeatureContext context) {
// This is the way in Jersey 2.26+ to get the ServiceLocator.
// In earlier versions, use
// ServiceLocatorProvider.getServiceLocator(context);
ServiceLocator locator = InjectionManagerProvider.getInjectionManager(context)
.getInstance(ServiceLocator.class);
Injector injector = Guice.createInjector(new AccountResourceModule());
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
return true;
}
}
然后只需使用 Jersey 注册该功能。
ResourceConfig packageConfig = new ResourceConfig()
.packages("ca.ulaval.glo4002.trading")
.register(GuiceFeature.class);
就是这样。它 应该 工作,正如我测试过的那样。
使用 Guice 绑定资源
通过上述配置,Jersey 将创建您的资源实例 classes(@Path
注释为 classes)。我们需要桥接的原因是 Jersey 与 HK2 紧密耦合,因此当我们注入资源 classes 时,在创建实例时,Jersey 将调用 HK2 以尝试找到该资源的所有依赖项。
虽然在这种情况下,我们不会依赖 Jersey 来创建资源实例。我们会将资源绑定到 Guice,并让 Guice 在我们请求时创建实例。我们将使用该实例向 Jersey 注册。
先绑定资源
public class AccountResourceModule extends AbstractModule {
@Override
protected void configure() {
bind(AccountService.class);
bind(AccountResource.class);
}
}
还要确保资源 class 中的 @Inject
注释是 com.google.inject.Inject
。
获取资源实例并注册
Injector injector = Guice.createInjector(new AccountResourceModule());
AccountResource accountResource = injector.getInstance(AccountResource.class);
ResourceConfig config = new ResourceConfig()
.register(accountResource);
您可能需要想出一种更简洁的方法来执行此操作,因为您不想对您拥有的每个资源都执行此操作。但如果你需要做什么,这就是要点。
更新
所以这是清理第二个解决方案的快速实施。我们可以做的是递归地扫描一个包,得到所有 @Path
注释的 classes 然后在 Guice 中绑定它们并在 Jersey 中注册它们。
从this SO post开始,我们可以使用反射库轻松获取所有classes。只需添加以下依赖项
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
那就做个小帮手吧classes
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Path;
import org.reflections.Reflections;
public class ResourceClassHelper {
private static Set<Class<?>> resourceClasses;
public static Set<Class<?>> getResourceClasses() {
if (resourceClasses != null) {
return resourceClasses;
}
// the package to scan for @Path classes "com.example"
Reflections reflections = new Reflections("com.example");
resourceClasses = reflections.getTypesAnnotatedWith(Path.class);
resourceClasses = Collections.unmodifiableSet(resourceClasses);
return resourceClasses;
}
}
然后在你的 Guice 模块中
public class AccountResourceModule extends AbstractModule {
@Override
protected void configure() {
bind(AccountService.class);
ResourceClassHelper.getResourceClasses().forEach(this::bind);
}
}
以及您的资源注册
Injector injector = Guice.createInjector(new AccountResourceModule());
ResourceConfig config = new ResourceConfig();
ResourceClassHelper.getResourceClasses()
.forEach(cls -> config.register(injector.getInstance(cls)));
我有一个使用 Jersey 2.x 的简单 REST API 项目。我尝试使用 Google Guice 来注入我的依赖项,但它似乎不起作用。我收到此错误:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=AccountService,parent=AccountsResource,qualifiers={},position=0,optional=false,self=false,unqualified=null,1658198405)
我有这个简单的资源class
@Path("/accounts")
@Produces(MediaType.APPLICATION_JSON)
public class AccountsResource {
private final AccountService accountService;
@Inject
public AccountsResource(AccountService accountService) {
this.accountService = accountService;
}
@GET
@Path("test")
public String test() {
return this.accountService.test();
}
我想将此服务注入我的资源class
public class AccountService {
public AccountService() {}
public String test() {
return "test";
}
}
所以,按照 Guice 的指导,我创建了这个模块 class
import com.google.inject.*;
public class AccountsResourceModule extends AbstractModule {
@Override
protected void configure() {
bind(AccountService.class);
}
}
最后,我在我的主要方法中添加了注入器
public class TradingServer implements Runnable {
private static final int PORT = 8181;
public static void main(String[] args) {
Injector injector = Guice.createInjector(new AccountsResourceModule());
AccountsResource accountsResource = injector.getInstance(AccountsResource.class);
new TradingServer().run();
}
public void run() {
Server server = new Server(PORT);
ServletContextHandler contextHandler = new ServletContextHandler(server, "/");
ResourceConfig packageConfig = new ResourceConfig().packages("ca.ulaval.glo4002.trading");
ServletContainer container = new ServletContainer(packageConfig);
ServletHolder servletHolder = new ServletHolder(container);
contextHandler.addServlet(servletHolder, "/*");
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
} finally {
server.destroy();
}
}
}
当我调用服务器时,出现上述错误。似乎依赖注入不起作用。请帮忙
所以 Jersey 对 Guice 一无所知。它已经使用了自己的 DI 框架 HK2。您可以做几件事。您可以将 Guice 与 HK2 绑定在一起,以便 HK2 可以找到绑定在 Guice 内部的服务,或者另一种方法是将您的资源 classes 绑定在 Guice 中,并向 Jersey 注册这些资源的实例。
将 Guice 与 HK2 绑定
要将 Guice 与 HK2 绑定,您需要使用 Guice HK2 Bridge。首先需要添加如下依赖
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>${hk2.version}</version>
</dependency>
要获得 hk2.version
,请查看您的 Jersey 依赖项(您可以 运行 mvn dependency:tree
并查看引入的 HK2 Jersey 版本)。您要确保使用的是完全相同的版本。
接下来您需要做的是以编程方式 link 这两个系统。一种方法是在 Feature
.
public class GuiceFeature implements Feature {
@Override
public boolean configure(FeatureContext context) {
// This is the way in Jersey 2.26+ to get the ServiceLocator.
// In earlier versions, use
// ServiceLocatorProvider.getServiceLocator(context);
ServiceLocator locator = InjectionManagerProvider.getInjectionManager(context)
.getInstance(ServiceLocator.class);
Injector injector = Guice.createInjector(new AccountResourceModule());
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
return true;
}
}
然后只需使用 Jersey 注册该功能。
ResourceConfig packageConfig = new ResourceConfig()
.packages("ca.ulaval.glo4002.trading")
.register(GuiceFeature.class);
就是这样。它 应该 工作,正如我测试过的那样。
使用 Guice 绑定资源
通过上述配置,Jersey 将创建您的资源实例 classes(@Path
注释为 classes)。我们需要桥接的原因是 Jersey 与 HK2 紧密耦合,因此当我们注入资源 classes 时,在创建实例时,Jersey 将调用 HK2 以尝试找到该资源的所有依赖项。
虽然在这种情况下,我们不会依赖 Jersey 来创建资源实例。我们会将资源绑定到 Guice,并让 Guice 在我们请求时创建实例。我们将使用该实例向 Jersey 注册。
先绑定资源
public class AccountResourceModule extends AbstractModule {
@Override
protected void configure() {
bind(AccountService.class);
bind(AccountResource.class);
}
}
还要确保资源 class 中的 @Inject
注释是 com.google.inject.Inject
。
获取资源实例并注册
Injector injector = Guice.createInjector(new AccountResourceModule());
AccountResource accountResource = injector.getInstance(AccountResource.class);
ResourceConfig config = new ResourceConfig()
.register(accountResource);
您可能需要想出一种更简洁的方法来执行此操作,因为您不想对您拥有的每个资源都执行此操作。但如果你需要做什么,这就是要点。
更新
所以这是清理第二个解决方案的快速实施。我们可以做的是递归地扫描一个包,得到所有 @Path
注释的 classes 然后在 Guice 中绑定它们并在 Jersey 中注册它们。
从this SO post开始,我们可以使用反射库轻松获取所有classes。只需添加以下依赖项
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
那就做个小帮手吧classes
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Path;
import org.reflections.Reflections;
public class ResourceClassHelper {
private static Set<Class<?>> resourceClasses;
public static Set<Class<?>> getResourceClasses() {
if (resourceClasses != null) {
return resourceClasses;
}
// the package to scan for @Path classes "com.example"
Reflections reflections = new Reflections("com.example");
resourceClasses = reflections.getTypesAnnotatedWith(Path.class);
resourceClasses = Collections.unmodifiableSet(resourceClasses);
return resourceClasses;
}
}
然后在你的 Guice 模块中
public class AccountResourceModule extends AbstractModule {
@Override
protected void configure() {
bind(AccountService.class);
ResourceClassHelper.getResourceClasses().forEach(this::bind);
}
}
以及您的资源注册
Injector injector = Guice.createInjector(new AccountResourceModule());
ResourceConfig config = new ResourceConfig();
ResourceClassHelper.getResourceClasses()
.forEach(cls -> config.register(injector.getInstance(cls)));