同一提供者的多个实例 [英] Multiple instance of the same provider

查看:73
本文介绍了同一提供者的多个实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个DataSourceProvider来查找容器管理的dataSource:

public class ContainerDataSourceProvider implements Provider<DataSource> {
    private final DataSource ds;

    @Inject
    ContainerDataSourceProvider (String dataSourceName) throws NamingException {
        Context initCtx = new InitialContext();
        Context envCtx = (Context) initCtx.lookup("java:comp/env");
        ds = (DataSource) envCtx.lookup("jdbc/" + dataSourceName);
    }

    @Override
    public DataSource get() {
        return ds;
    }
}

我不能在提供程序上使用@Named注释,因为我希望能够根据dataSourceName提供不同的数据源.

在我的模块中,我想使用提供程序提供多个dataSource绑定. jdni数据存储器的名称来自已经绑定到名称的属性文件.

//How do I configure ContainerDataSourceProvider with jndi name "userDataSource"????
binder.bind(DataSource.class).annotatedWith(UserDS.class)
                .toProvider(ContainerDataSourceProvider.class);

//How do I configure ContainerDataSourceProvider with jndi name "sessionDataSource"????
binder.bind(DataSource.class).annotatedWith(SessionDS.class)
                .toProvider(ContainerDataSourceProvider.class);

然后在我的代码中,我将能够执行类似的操作

public class UserDSClient {
       @Inject UserDSClient (@UserDS DataSource ds) {}
}

public class SessionDSClient {
       @Inject SessionDSClient (@SessionDS DataSource ds) {}
}

我该如何实现?

解决方案

这很简单.手动创建提供程序,将数据源名称传递给其构造函数:

public class ContainerDataSourceProvider implements Provider<DataSource> {
    private final String dataSourceName;

    @Inject
    ContainerDataSourceProvider (String dataSourceName) {
        this.dataSourceName = dataSourceName;
    }

    @Override
    public DataSource get() {
        try {
            Context initCtx = new InitialContext();
            Context envCtx = (Context) initCtx.lookup("java:comp/env");
            return (DataSource) envCtx.lookup("jdbc/" + dataSourceName);
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }
}

我还将上下文查找代码移到了get()方法.如果将绑定放在单例作用域中,那么这在性能上是完全安全的-在这种情况下,Guice足够聪明,可以仅调用一次提供程序方法.这里是绑定:

binder.bind(DataSource.class).annotatedWith(UserDS.class)
    .toProvider(new ContainerDataSourceProvider("userDataSource"))
    .in(Singleton.class);

binder.bind(DataSource.class).annotatedWith(SessionDS.class)
    .toProvider(new ContainerDataSourceProvider("sessionDataSource"))
    .in(Singleton.class);

仅此而已.

BTW,由于某种原因查找可能会失败,因此在依赖项解析期间(在Guice.createInjector()调用中或在injector.getInstance()调用中),您都会在代码中从构造函数中引发异常时得到一个异常,在我的变体中如果您需要处理此错误,请考虑使用投掷提供商扩展名.

I created an DataSourceProvider to look up container managed dataSource:

public class ContainerDataSourceProvider implements Provider<DataSource> {
    private final DataSource ds;

    @Inject
    ContainerDataSourceProvider (String dataSourceName) throws NamingException {
        Context initCtx = new InitialContext();
        Context envCtx = (Context) initCtx.lookup("java:comp/env");
        ds = (DataSource) envCtx.lookup("jdbc/" + dataSourceName);
    }

    @Override
    public DataSource get() {
        return ds;
    }
}

I can't used @Named annotation on the provider since I want to be able to provide different datasource depending on dataSourceName.

In my module, I want to provide multiple dataSource bindings using the provider. The name of the jdni datasorce comes from a property file already binded to Names.

//How do I configure ContainerDataSourceProvider with jndi name "userDataSource"????
binder.bind(DataSource.class).annotatedWith(UserDS.class)
                .toProvider(ContainerDataSourceProvider.class);

//How do I configure ContainerDataSourceProvider with jndi name "sessionDataSource"????
binder.bind(DataSource.class).annotatedWith(SessionDS.class)
                .toProvider(ContainerDataSourceProvider.class);

Then in my code, I will be able to do something like

public class UserDSClient {
       @Inject UserDSClient (@UserDS DataSource ds) {}
}

public class SessionDSClient {
       @Inject SessionDSClient (@SessionDS DataSource ds) {}
}

How can I achieve this?

解决方案

It is pretty simple. Create the provider manually, passing datasource name to its constructor:

public class ContainerDataSourceProvider implements Provider<DataSource> {
    private final String dataSourceName;

    @Inject
    ContainerDataSourceProvider (String dataSourceName) {
        this.dataSourceName = dataSourceName;
    }

    @Override
    public DataSource get() {
        try {
            Context initCtx = new InitialContext();
            Context envCtx = (Context) initCtx.lookup("java:comp/env");
            return (DataSource) envCtx.lookup("jdbc/" + dataSourceName);
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }
}

I've also moved context lookup code to get() method. This is completely safe in terms of performance if you put the bindings in singleton scope - Guice is smart enough to call provider method only once in this case. Here are the bindings:

binder.bind(DataSource.class).annotatedWith(UserDS.class)
    .toProvider(new ContainerDataSourceProvider("userDataSource"))
    .in(Singleton.class);

binder.bind(DataSource.class).annotatedWith(SessionDS.class)
    .toProvider(new ContainerDataSourceProvider("sessionDataSource"))
    .in(Singleton.class);

That is all.

BTW, because for some reason lookup may fail, you will get an exception during dependency resolution time (either in Guice.createInjector() call or in injector.getInstance() call), both in your code, when exception is thrown from the constructor, and in my variant. Consider using throwing providers extension if you need to handle this error.

这篇关于同一提供者的多个实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆