Java Guice DI错误:UnsatisfiedDependencyException:SystemInjecteeImpl中没有可用于注入的对象

Wis*_*rod 1 java dependency-injection jax-rs jersey guice

我有一个使用Jersey 2.x的简单REST API项目.我尝试使用Google Guice来注入我的依赖项,但它似乎不起作用.我收到此错误:

org.glassfish.hk2.api.UnsatisfiedDependencyException:SystemInjecteeImpl没有可用于注入的对象(r​​equiredType = AccountService,parent = AccountsResource,qualifiers = {},position = 0,optional = false,self = false,unqualified = null,1658198405 )

我有这个简单的资源类

@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();
  }
Run Code Online (Sandbox Code Playgroud)

我想将此服务注入我的资源类

public class AccountService {

    public AccountService() {}

    public String test() {
        return "test";
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,按照Guice的指南,我创建了这个模块类

import com.google.inject.*;

public class AccountsResourceModule extends AbstractModule  {

@Override
protected void configure() {
    bind(AccountService.class);
}
}
Run Code Online (Sandbox Code Playgroud)

最后,我在主方法中添加了注入器

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

}

当我打电话给我的服务器时,我得到上面提到的错误.似乎依赖注入不起作用.请帮忙

Pau*_*tha 5

所以泽西对Guice一无所知.它已经使用了它自己的DI框架,HK2.你可以做几件事.您可以将Guice与HK2绑定在一起,以便HK2可以找到绑定在Guice中的服务,或者另一种方法是将您的资源类绑定在Guice中,并使用Jersey注册这些资源的实例.

与HK2联系Guice

要将Guice与HK2联系起来,您需要使用Guice HK2 Bridge.首先,您需要添加以下依赖项

<dependency>
    <groupId>org.glassfish.hk2</groupId>
    <artifactId>guice-bridge</artifactId>
    <version>${hk2.version}</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

要了解hk2.version您的Jersey依赖项(您可以运行mvn dependency:tree并查看HK2 Jersey的版本).您想确保使用完全相同的版本.

接下来您需要做的是以编程方式链接这两个系统.一种方法是在里面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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后只需在Jersey上注册该功能.

ResourceConfig packageConfig = new ResourceConfig()
        .packages("ca.ulaval.glo4002.trading")
        .register(GuiceFeature.class);
Run Code Online (Sandbox Code Playgroud)

就是这样.它应该工作,正如我测试的那样.

用Guice绑定资源

使用上面的配置,Jersey将创建资源类的实例(带@Path注释的类).我们需要桥接的原因是Jersey与HK2紧密耦合,因此当我们注入资源类时,在创建实例时,Jersey会调用HK2来尝试查找资源的所有依赖项.

但在这种情况下,我们不会依赖Jersey来创建资源的实例.我们将资源绑定到Guice,让Guice在我们请求时创建实例.我们将使用该实例向Jersey注册.

首先绑定资源

public class AccountResourceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(AccountService.class);
        bind(AccountResource.class);
    }
}
Run Code Online (Sandbox Code Playgroud)

还要确保@Inject资源类中的注释是com.google.inject.Inject.

获取资源实例并注册它

Injector injector = Guice.createInjector(new AccountResourceModule());
AccountResource accountResource = injector.getInstance(AccountResource.class);

ResourceConfig config = new ResourceConfig()
        .register(accountResource);
Run Code Online (Sandbox Code Playgroud)

您可能需要找到一种更简洁的方法来执行此操作,因为您不希望必须为您拥有的每个资源执行此操作.但如果您需要做什么,这就是要点.

更新

所以这是一个快速实现清理第二个解决方案.我们可以做的是递归扫描包以获取所有带@Path注释的类,然后在Guice中绑定它们并将它们注册到Jersey.

这篇SO帖子中,我们可以使用Reflections库轻松获取所有类.只需添加以下依赖项

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.11</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

然后做一个小帮助班

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的Guice模块中

public class AccountResourceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(AccountService.class);

        ResourceClassHelper.getResourceClasses().forEach(this::bind);
    }
}
Run Code Online (Sandbox Code Playgroud)

和你的资源注册

Injector injector = Guice.createInjector(new AccountResourceModule());

ResourceConfig config = new ResourceConfig();
ResourceClassHelper.getResourceClasses()
            .forEach(cls -> config.register(injector.getInstance(cls)));
Run Code Online (Sandbox Code Playgroud)