【问题标题】:Jersey HK2 injection in manually created objects手动创建的对象中的 Jersey HK2 注入
【发布时间】:2016-09-20 12:30:14
【问题描述】:

有没有办法将依赖项注入到手动创建的对象中?

public class MyCommand {
    @Inject Repository repository;
}

public Repository {
    @Inject EntityManager em;
}

MyCommand command = new MyCommand();

Repository 已在 jersey ResourceConfig 中正确注册,并且可以注入通过 CDI 容器创建的对象中,例如资源类。

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

有没有办法在@Inject 和@Context 之外获取注册类? 类似 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);
    }
}

【问题讨论】:

  • 这是可能的,但是我建议使用通向通用注入框架的桥接器(例如 google guice)。或者,我在此处的回答中描述了球衣注射和绑定:stackoverflow.com/questions/38976724/…
  • 我会声称 hk2 是一个通用注入框架 ;-)
  • @jwells131313 你是对的 :)

标签: java dependency-injection jersey hk2


【解决方案1】:

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

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

但是,您可以这样做。

首先,您必须获得对您​​的 ServiceLocator 的引用。它必须与球衣使用的 ServiceLocator 相同。您可以访问它,例如:

How to get HK2 ServiceLocator in Jersey 2.12?

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

您现在有 2 个选择:使用 Service Locator 注册您的命令并让注入框架处理创建,或者将 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);
        }
    }

}

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

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

你的相关行可能是:

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

这会向服务定位器询问存储库的新实例。

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

希望能带来一些内在的东西,

阿图尔

【讨论】:

  • 显然您可以立即将 ServiceLocator 注入您的资源中。
  • @Migaoli 这很有意义,因为资源是由同一个服务定位器创建的。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-13
    • 1970-01-01
    相关资源
    最近更新 更多