Dropwizard和Guice:注入环境

时间:2014-08-30 10:55:43

标签: dependency-injection guice dropwizard jdbi

我目前正在构建一个基于Dropwizard + Guice + Jersey的应用程序,其中数据库访问暂时由JDBI处理。

我想要实现的是拥有典型的企业架构,其中Resources访问服务类访问DAO类,而DAO类又访问数据库。如果所有其他方法都失败了,我想我可以在应用程序的run()方法中构建我的对象图,这将很高兴以正确的DI方式连接所有这些。

所以,我遇到了这里提到的问题before:获取DBIFactory需要环境和配置,在Guice注入时需要以某种方式提供魔术,而不是在跑步() - 时间。

作为一个Dropwizard和Guice noob,到目前为止我设法组建的是我需要一个Provider来为我的DAO对象提供一些东西。

public class UserDAOProvider implements Provider<UserDAO> {

    @Inject
    Environment environment;
    @Inject
    Configuration configuration;

    @Override
    public UserDAO get() {
        final DBIFactory factory = new DBIFactory();
        final (MyConfiguration) config = (MyConfiguration) configuration;
        DBI jdbi = null;
        try {
            jdbi = factory.build(environment, config.getDataSourceFactory(),
                    "mysql");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return jdbi.onDemand(UserDAO.class);
    }

}

将此注册为单身提供者应该让我将UserDAO注入我的服务。

现在,我们如何实际将环境注入提供者?目前我被困在Guice抱怨没有为环境找到合适的构造函数,所以它试图实例化它而不是从Dropwizard本身获取它。

看起来这是可行的;有dropwizard-guice包我的DropWizardEnvironmentModule是我需要的。但我觉得我只是在这里错过了一些难题,以便理解如何把事情放在一起。到目前为止,我还没有找到一个完整的工作示例......

3 个答案:

答案 0 :(得分:4)

我遇到的问题与OP相同,但使用Hibernate而不是JDBI。我的简单解决方案适用于JDBI - 只需为DBIFactory切换SessionFactory

首先在Guice模块中为单个SessionFactory添加一个注射提供程序:

public class MyModule extends AbstractModule {

    private SessionFactory sessionFactory;

    @Override
    protected void configure() {
    }

    @Provides
    SessionFactory providesSessionFactory() {

        if (sessionFactory == null) {
             throw new ProvisionException("The Hibernate session factory has not yet been set. This is likely caused by forgetting to call setSessionFactory during Application.run()");
        }

       return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
}

您需要从应用程序的run()方法设置单例SessionFactory。在您的情况下,使用JDBI,您可以在将其交给Guice模块之前创建和配置DBIFactory:

public void run(MyConfiguration configuration, Environment environment) {

    myModule.setSessionFactory(hibernateBundle.getSessionFactory());
    ...
}

现在可以在任何需要的地方注入SessionFactory。我现在通过使用@Inject注释构造函数并注入SessionFactory单例来为我的DAO类使用隐式绑定。我没有为DAO类显式创建提供程序:

@Singleton
public class WidgetDAO extends AbstractDAO<App> {

    @Inject
    public WidgetDAO(SessionFactory factory) {
        super(factory);
    }

    public Optional<Widget> findById(Long id) {
        return Optional.fromNullable(get(id));
    }
    ...
}

现在我可以将DAO单例实例注入资源:

@Path("/widgets")
@Produces(MediaType.APPLICATION_JSON)
public class WidgetsResource {

    private final WidgetDAO widgetDAO;

    @Inject
    public WidgetsResource(WidgetDAO widgetDAO) {
        this.widgetDAO = widgetDAO;
    }
    ...
}

请注意,此方法遵循Guice建议仅注入直接依赖项。不要尝试注入Envrionment和Configuration,以便创建一个DBI工厂 - 注入预构建的DBI工厂。

答案 1 :(得分:3)

这就是我如何使用Guice和Dropwizard。在run()方法内添加行

Guice.createInjector(new ConsoleModule()); 

你无法注入Environ

创建类ConsoleModule

public class ConsoleModule extends AbstractModule {

    //configuration and env variable declaration

    public  ConsoleModule(ConsoleConfiguration consoleConfig, Environment env)
    {
        this.consoleConfig = consoleConfig;
        this.env= env;
    }

    protected void configure()
    {
        //You should not inject Configuration and Environment in your provider since you are mixing     
        //dropwizard framework stuff with Guice.Neverthless you will have to bind them in the below order

        bind(Configuration.class).toInstance(consoleConfig.class);
        bind(Environment.class).toInstance(env.class);
        bind(UserDAO.class).toProvider(UserDAOProvider.class).in(Singleton.class);
    }
}

答案 2 :(得分:0)

我们有相同的配置(dw-jdbi-guice)以及一个抽象的“基础”Application类,这使得事情变得更复杂。

由于在run方法期间发生了很多事情,并且许多事情依赖于配置对象,我们最终在run方法中创建了注入器。但由于我们还需要来自bootsrap的对象(例如ObjectMapper),我们最终在Application类中有一个List<Module>字段。不是最漂亮的解决方案,但可以处理各种场景。