且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

同一提供者的多个实例

更新时间:2023-11-22 17:01:28

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

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.