我目前正在构建一个基于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是我需要的。但我觉得我只是在这里错过了一些难题,以便理解如何把事情放在一起。到目前为止,我还没有找到一个完整的工作示例......
答案 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>
字段。不是最漂亮的解决方案,但可以处理各种场景。