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

时间:2018-09-28 00:21:18

标签: java dependency-injection jersey jax-rs guice

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

  

org.glassfish.hk2.api.UnsatisfiedDependencyException:SystemInjecteeImpl(requiredType = 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();
  }

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

public class AccountService {

    public AccountService() {}

    public String test() {
        return "test";
    }
}

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

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();
    }
}

}

呼叫服务器时,出现上述错误。似乎依赖项注入无效。请帮助

1 个答案:

答案 0 :(得分:5)

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

与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)。您要确保使用的是完全相同的版本。

下一步,您需要以编程方式链接两个系统。一种实现方法是在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将创建您的资源类(带有注释的类@Path)的实例。我们需要桥接的原因是Jersey与HK2紧密耦合,因此,当我们注入资源类时,在创建实例时,Jersey将调用HK2尝试查找该资源的所有依赖关系。

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

首先绑定资源

public class AccountResourceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(AccountService.class);
        bind(AccountResource.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带注释的类,然后将它们绑定到Guice并将其注册到Jersey。

this SO post中,我们可以使用Reflections库轻松获取所有类。只需添加以下依赖项

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.11</version>
</dependency>

然后开设一些辅助课程

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)));