Jersey HK2 注入手动创建的对象

Jersey HK2 injection in manually created objects

有什么方法可以将依赖项注入到手动创建的对象中吗?

public class MyCommand {
    @Inject Repository repository;
}

public Repository {
    @Inject EntityManager em;
}

MyCommand command = new MyCommand();

存储库已正确注册 jersey ResourceConfig,并且可以注入通过 CDI 容器创建的对象,例如资源 class。

但是由于我自己创建了命令,@Inject 注释被忽略了。

有没有办法在@Inject 和@Context 旁边注册一个class? 像 Application.get(Repository.class)

public class MyCommand {
    Repository repository;

    public MyCommand() {
        repository = Application.get(Repository.class);
    }
}

----- 编辑 -----

感谢您的帮助和一些反思,我找到了解决问题的方法。

首先,无需任何准备就可以将 ServiceLocator 注入到您的对象中。

第二件事是我从使用执行方法的普通命令转移到命令总线系统。 原因是我无法控制命令的创建,所以有干净的方法来注入依赖项。

新方法如下所示:

class CommandBus {
    private final ServiceLocator serviceLocator;

    @Inject
    public CommandBus(ServiceLocator serviceLocator) {
        this.serviceLocator = serviceLocator;
    }

    public void dispatch(Command command) {
        Class handlerClass = findHandlerClassForCommand(command);
        CommandHandler handler = (CommandHandler) serviceLocator.getService(handlerClass);
        handler.handle(command);
    }
}

interface CommandHandler {
    void handle(Command command);
}

interface Command {
}

class ConcreteCommand implements Command {
    // I'm just a dto with getters and setters
}

class ConcreteHandler implements CommandHandler {
    private final SomeDependency dependency;

    @Inject
    public ConcreteHandler(SomeDependency dependency) {
        this.dependency = dependency;
    }
    @Override
    public void handle(ConcreteCommand command) {
        // do some things
    }
}

在我的资源中,我有这样的东西:

@Path("/some-resource")
class Resource {

    @Context
    private CommandBus bus;

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public void runCommand(ConcreteCommand command) {
        bus.dispatch(command);
    }
}

您可以使用 ServiceLocator 的 inject 方法来注入已创建的对象。 ServiceLocator 是 HK2 的基本注册表,应该在您的资源中可用。

正如 jwells 指出的那样 - HK2 是一个注入框架 :)

我花了一些时间研究它 - 我不得不说,我发现它比说 guice 或 spring 复杂得多。也许这是因为我使用 Dropwizard,这使得访问服务定位器变得不那么容易。

但是,您可以按照以下方法进行操作。

首先,您必须获得对 ServiceLocator 的引用。它也必须与 jersey 正在使用的 ServiceLocator 相同。您可以访问它,例如:

How to get HK2 ServiceLocator in Jersey 2.12?

在我的示例代码中,我将使用一个事件侦听器,这是由于我的 Dropwizard 设置。

您现在有 2 个选择:使用您的服务定位器注册您的命令并让注入框架处理创建,或者将 ServiceLocator 传递给您的命令以便使用它。

我使用 Dropwizard 和 jersey 写了一个简单的例子:

public class ViewApplication extends io.dropwizard.Application<Configuration> {

    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {

        environment.jersey().register(new ApplicationEventListener() {
            @Override
            public void onEvent(ApplicationEvent event) {
                if (event.getType() == ApplicationEvent.Type.INITIALIZATION_FINISHED) {
                    ServiceLocator serviceLocator = ((ServletContainer) environment.getJerseyServletContainer())
                            .getApplicationHandler().getServiceLocator();

                    ServiceLocatorUtilities.bind(serviceLocator, new AbstractBinder() {

                        @Override
                        protected void configure() {
                            bind(new Repository("test")).to(Repository.class);
                            bind(MyCommandInjected.class).to(MyCommandInjected.class);
                        }
                    });

                    MyCommandInjected service = serviceLocator.getService(MyCommandInjected.class);
                    MyCommandManual tmp = new MyCommandManual(serviceLocator);
                }
            }
            @Override
            public RequestEventListener onRequest(RequestEvent requestEvent) {
                return null;
            }
        });


    }

    @Override
    public void initialize(Bootstrap<Configuration> bootstrap) {
        super.initialize(bootstrap);
    }

    public static void main(String[] args) throws Exception {
        new ViewApplication().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
    }

    @Path("test")
    @Produces(MediaType.APPLICATION_JSON)
    public static class HelloResource {

        @GET
        @Path("asd")
        public String test(String x) {
            return "Hello";
        }

    }

    public static class Repository {

        @Inject
        public Repository(String something) {
        }
    }

    public static class MyCommandInjected {

        @Inject
        public MyCommandInjected(final Repository repo) {
            System.out.println("Repo injected " + repo);
        }
    }

    public static class MyCommandManual {

        public MyCommandManual(final ServiceLocator sl) {
            Repository service = sl.getService(Repository.class);
            System.out.println("Repo found: " + service);
        }
    }

}

在 运行 方法中,我可以访问我的 ServiceLocator。我在那里绑定了我的 类 (所以有一个如何做到这一点的例子)。您也可以直接向 jersey 注册 Binders——它们将使用正确的 ServiceLocator。

2 类 MyCommandInjectedMyCommandManual 是如何创建此命令的示例。

与您相关的行可能是:

Repository service = sl.getService(Repository.class);

这向服务定位器请求存储库的新实例。

现在,这只是一个简单的例子。与直接使用 HK2 相比,我更喜欢 guice 桥 :) 我发现它更易于使用且更清晰。使用 guice-jersey-bridge 你可以通过 guice 做任何事情,它会自动做正确的事情。

希望能带来一些内在,

阿图尔