Multiple instance of the same provider

夙愿已清 提交于 2019-12-11 14:15:06

问题


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?


回答1:


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.




回答2:


I strongly recommend you to use Private Modules. Private module will hide all binding information from environment. So, you will be able to inject different implementation to the same interface without the use of an Annotations.

Well, I made a presentation. Maybe it will help you get the idea around Private modules. http://slid.es/milanbaran/dec



来源:https://stackoverflow.com/questions/20041776/multiple-instance-of-the-same-provider

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!