如何让Jersey与Dagger依赖注入一起工作?

40

Jersey通常使用HK2依赖注入,但我想使用Dagger 2与Jersey一起使用。Dagger和HK2都实现了JSR 330,这使我相信可以在不费太多力气的情况下实现。我找到了让Jersey与CDI(如Weld)、Spring DI和Guice配合使用的方法,但在Dagger上却找不到任何信息。

为提供一些上下文:我在SE环境中运行Grizzly-Jersey服务器,而非在EE容器中。我的Maven项目有com.google.dagger:daggerorg.glassfish.jersey.containers:jersey-container-grizzly2-http作为依赖项,但没有org.glassfish.jersey.inject:jersey-hk2,因为我想用Dagger替换HK2。

资源类的代码如下:

@Path("/example")
public final class ExampleResource {

    private final Dependency dependency;

    @Inject
    public ExampleResource(final Dependency dependency) {
        this.dependency = Objects.requireNonNull(dependency);
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Example getExample() {
        return this.dependency.giveExample();
    }

}

而Dagger组件可以定义如下:

@Component
public interface Application {

    public ExampleResource exampleEndpoint();
    public XyzResource xyzEndpoint();
    // etc.

}

这样主方法看起来会与以下类似:

public final class Main {

    public static void main(final String[] args) {
        final Application application = DaggerApplication.create();
        final URI baseUri = UriBuilder.fromUri("http://0.0.0.0/").port(80).build();
        final ResourceConfig resourceConfig = new ResourceConfig();
        // how to initialize `resourceConfig` using `application`?
        final HttpServer httpServer = GrizzlyHttpServerFactory
                .createHttpServer(baseUri, resourceConfig, false);
        try {
            httpServer.start();
        } catch (final IOException ex) {
            ...
        }
    }

}

运行应用程序会立即导致异常:IllegalStateException:InjectionManagerFactory not found。似乎需要Dagger实现此工厂。

我的问题是:如何将Dagger与Jersey集成?


1
你试过 https://github.com/johnlcox/dagger-servlet 吗? - Sasha Shpota
@OleksandrShpota 不,但我没有使用servlets。我从一个主方法中运行Jersey。没有EE。 - Rinke
1
@Rinke 没问题。上面链接的 dagger-servlet 项目应该是一个很好的起点。在这个项目中,有一个名为 dagger-jersey 的模块,其中包含将 dagger 钩入 Jersey IOC 接口的类。特别是 DaggerComponentProviderFactory 类具有大部分必要的逻辑。 - Dogs
根据此评论dagger-servlet仅适用于Dagger 1,与Dagger 2不兼容...很遗憾。@OleksandrShpota和@Dogs - Rinke
附带说明:如果您想将其作为Docker服务使用,则可以考虑使用http://0.0.0.0而不是http://localhost作为baseUri(以便服务在该端口上处理所有请求)。 - Rob Bygrave
显示剩余2条评论
2个回答

1
你不应该把它看作是“如何将dagger与jersey集成”。先弄清楚如何设置jersey,一旦你搞清楚了这个问题,那么你就可以开始担心如何使用dagger。
以下是我会做的(非常粗略):
创建自己的ResourceConfig类实现。
@ApplicationPath("/service")
public class MyResourceConfig extends ResourceConfig {

    @Inject
    public MyResourceConfig(
            @Nonnull final ExampleResource exampleResource) {
        this.register(exampleResource);
    }

}

创建一个模块,设置一切所需以创建HttpServer。
@Module
public class MyServiceModule {

    @Provides
    @Singleton
    @Named("applicationPort")
    public Integer applicationPort() {
        return 80;
    }

    @Provides
    @Singleton
    @Named("applicationBaseUri")
    public URI baseUri(
            @Named("applicationPort") @Nonnull final Integer applicationPort) {
        return UriBuilder.fromUri("http://0.0.0.0/").port(applicationPort).build();
    };

    @Provides
    @Singleton
    public HttpServer httpServer(
            @Named("applicationBaseUri") @Nonnull final URI applicationBaseUri,
            @Nonnull final MyResourceConfig myResourceConfig) {
        return GrizzlyHttpServerFactory
                .createHttpServer(applicationBaseUri, myResourceConfig, false);
    }

}

然后创建一个组件,公开HttpServer。我通常喜欢制作尽可能少暴露的组件。在这种情况下,您只需要公开HttpServer。

@Singleton
@Component(modules = { MyServiceModule.class })
protected interface ServiceComponent {

    HttpServer httpServer();

    @Component.Builder
    interface Builder {

        // Bind any parameters here...

        ServiceComponent build();

    }

}

然后,只需开始构建您的组件并启动HttpServer即可。

public static void main(String[] args) {
    final ServiceComponent component = DaggerServiceComponent.builder().build()
    try {
        component.httpServer().start();
    } catch (Exception ex) {
        // handle exception...
    }
}

还有一件事需要注意。我个人从不使用@Named("")注释,而是更喜欢使用限定符。因此,您可以创建一个具有唯一值的限定符注释。然后,您可以注入类似以下内容的内容

@Provides
@Singleton
@MyUniqueQualifier
public String myUniqueQualifierProviderValue() {
    return "something";
}

然后在注入它时。
@Inject
public SomeClass(@MyUniqueQualifier @Nonnull final String myUniqueQualifiedValue) 

如果您使用@Named注释,则不会在编译时检查冲突或缺失值。您会在运行时发现未注入值,或者名称与其他内容冲突。这很快变得混乱。

1
谢谢你的回答,Callan!然而,问题的重点是如何使用Dagger而不是HK2,这样就不必手动配置“ResourceConfig”类。 - Rinke
@Rinke dagger只是依赖注入。它对ResourceConfig一无所知。您需要提供要注入的对象,并且还需要一个地方将它们注入。根据Jersey在这种情况下的设置方式,将它们注入到您自己的ResourceConfig类中是最合理的选择。您试图解决什么问题?是否有技术原因导致您无法创建自己的ResourceConfig类? - Callan
你可能没有理解我的问题重点。你有使用过Jersey-HK2组合吗?它非常方便。我的问题是如何使用Dagger来替代HK2,以获得相同的体验。我希望Jersey使用Dagger来实例化资源类。 - Rinke
Dagger和HK2是两个不同的框架。我回答的问题是如何使用Dagger依赖注入设置Jersey。但是听起来你想问的是,如何让Dagger像HK2一样工作?这个我无法告诉你,因为它是一个不同的框架,使用方式也不同,所以它可能不能完全为你做相同的事情。 - Callan
Jersey可以使用各种DI框架来实例化资源类,例如HK2、Spring DI、Weld等。问题是如何配置Jersey以便使用Dagger。感谢您的努力尝试回答我的问题。 - Rinke

0

很遗憾,我并不真正理解你的回答。我已经明白我需要一个DaggerInjectionManagerFactory。然而,如何实现它应该生成的DaggerInjectionManager并不是一件简单的事情。 - Rinke
你能详细说明一下 META-INF 条目吗?我不知道该如何把它翻译成我的情况。 - Rinke
关于 META-INF,我不确定你在问什么 - 查看我的回答中的链接,你的会完全一样,只是指向你的 InjectionManagerFactory。实现实际的 InjectionManager 的一个简单方法(至少对于 getInstance 方法)是在其构造函数中创建 MyComponent 的一个实例,在其中遍历它的方法,将结果放入每个方法返回类到方法调用返回的对象的映射表中,然后在 getInstance 方法中(至少是那个接受一个类的方法)从映射表中返回该实例。 - user7505251
这个答案是更聪明的方法,我建议进一步阐述:他基本上建议如何在Jersey中配置您的DI机制,通过遵循这种方法,您可以消除对org.glassfish.jersey.inject:jersey-hk2的依赖,因为您将其替换为自定义实现,其中您可以委托给Dagger组件、Spring应用程序或Guice注入器。您必须实现InjectionManagerFactory的版本并返回您的InjectionManager版本:这种方法似乎需要大量的样板代码,但我认为值得尝试。 - Cristiano Costantini

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接