For those stumbling on this question in the future, I have found a solution
that I am most pleased with:
Isolate the bindings of the Red and Blue Modules to their own injectors and
create providers for only the exposed services you need / want in your
application. My AwesomeService needs the Red and Blue Services, so I create
2 provider methods for them; but the offending exposed ExecutorServices of
these modules are isolated to their respective injectors not polluting my
application's bindings:
public class MyAppModule extends PrivateModule {
private static final Injector blueInjector = Guice.createInjector(new
BlueModule());
private static final Injector redInjector = Guice.createInjector(new
RedModule());
void configure() {
bind(ExecutorService.class).toInstance(Executors.newSingleThreadExecutor());
bind(AwesomeService.class).to(AwesomeServiceImpl.class);
expose(AwesomeService.class);
}
@Provides
RedService getRedService() {
return redInjector.getInstance(RedService.class);
}
@Provides
BlueService getBlueService() {
return blueInjector.getInstance(BlueService.class);
}
}
Cheers!
On Friday, April 26, 2013 1:22:15 PM UTC-7, Matthew Madson wrote:
>
> Hi Marshall,
>
> Thanks for the reply, and I wholeheartedly agree with you, BlueModule's
> author lacked foresight.
>
> I did consider using Modules.override, but I actually don't want to change
> BlueModule's ExecutorService binding, you see BlueService actually uses
> BlueModule's ExecutorService, and if I replace it with my application's
> ExecutorService, BlueService will start kicking off threads in my
> application's executor service and neither I nor BlueService want that.
>
> class BlueServiceImpl {
> @Inject
> BlueServiceImpl(ExecutorService executor) { ... }
> }
>
> Thanks,
> Matt
>
> On Friday, April 26, 2013 12:17:44 PM UTC-7, Marshall Pierce wrote:
>>
>> Looks like you could just use Modules.override [1]:
>>
>> install(Modules.override(new BlueModule()).with(new
>> MyExecutorServiceModule()));
>>
>> [1]
>> http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/util/Modules.html#override(com.google.inject.Module...)
>>
>>
>>
>> Though, it would have been nice if BlueModule's author hadn't so rudely
>> bound ExecutorService, since that makes it hard on users of his library.
>> When releasing code externally I apply annotations to bindings of common
>> classes from the JDK or other libraries so that users don't get these
>> unpleasant surprises.
>>
>> -Marshall
>>
>> On Apr 26, 2013, at 11:54 AM, Matthew Madson <[email protected]>
>> wrote:
>>
>> > Let's say I'm developing an application which uses a service to do
>> something totally awesome.
>> >
>> > public class MyAppModule extends PrivateModule {
>> > bind(AwesomeService.class).to(AwesomeServiceImpl.class);
>> > expose(AwesomeService.class);
>> > }
>> >
>> > Now the particular implementation of my AwesomeService needs a few
>> things to be as awesome as it is:
>> >
>> > class AwesomeServiceImpl implements AwesomeService {
>> > @Inject
>> > AwesomeServiceImpl(BlueService blue, RedService red, ExecutorService
>> executor) { ... }
>> > }
>> >
>> > It just so happens that some upstanding internet denizen has created a
>> standalone jar with guice modules that provide both Red and Blue Services.
>> So I'll add the jar to my classpath and modify MyAppModule so that my
>> AwesomeService can use the third pary Red and Blue Services:
>> >
>> > public class MyAppModule extends PrivateModule {
>> > install(new RedModule());
>> > install(new BlueModule());
>> > bind(AwesomeService.class).to(AwesomeServiceImpl.class);
>> > expose(AwesomeService.class);
>> > }
>> >
>> > I also need an ExecutorService for my AwesomeService, so I'll go ahead
>> and bind to an explicit instance for now:
>> >
>> > public class MyAppModule extends PrivateModule {
>> > install(new RedModule());
>> > install(new BlueModule());
>> >
>> bind(ExecutorService.class).toInstance(Executors.newSingleThreadExecutor());
>>
>> > bind(AwesomeService.class).to(AwesomeServiceImpl.class);
>> > expose(AwesomeService.class);
>> > }
>> >
>> > Ah, but damn, apparently my good internet friend decided to expose not
>> only the RedService and BlueService bindings that my AwesomeService needs,
>> but also an ExecutorService that I don't want:
>> >
>> > public final class BlueModule extends PrivateModule {
>> >
>> bind(ExecutorService.class).toInstance(Executors.newCachedThreadPool());
>> > bind(BlueService.class).to(BlueServiceImpl.class);
>> >
>> > expose(ExecutorService.class);
>> > expose(BlueService.class);
>> > }
>> >
>> > public final class RedModule extends PrivateModule {
>> >
>> bind(ExecutorService.class).toInstance(Executors.newCachedThreadPool());
>> > bind(RedService.class).to(RedServiceImpl.class);
>> >
>> > expose(ExecutorService.class);
>> > expose(RedService.class);
>> > }
>> >
>> > No problem, I'll just wrap his modules in a private module and expose
>> only the services I care about:
>> >
>> > public class MyAppModule extends PrivateModule {
>> > install(new PrivateModule() {
>> > install(new RedModule());
>> > expose(RedService.class);
>> > });
>> > install(new PrivateModule() {
>> > install(new BlueModule());
>> > expose(BlueService.class);
>> > });
>> >
>> bind(ExecutorService.class).toInstance(Executors.newSingleThreadExecutor());
>>
>> > bind(AwesomeService.class).to(AwesomeServiceImpl.class);
>> > expose(AwesomeService.class);
>> > }
>> >
>> > Ahh, but damn again, my ExecutorService binding is inherited by my
>> private wrapper modules and is conflicting with the internal bindings
>> defined in the RedModule and BlueModule. I guess I could annotate or Name
>> my ExecutorService in my AwesomeService constructor, but what if I want
>> that ExecutorService to be a singleton shared all over my app, by 20, 30 or
>> 40 different services. I'll have to pollute all my ExecutorService
>> injections with this annotation.
>> >
>> > Or I suppose I could do some trickery, staggering the bindings and
>> hiding the ExecutorService so it doesn't conflict with the ExecutorService
>> that RedModule and BlueModule need, but this just seems wrong:
>> >
>> > public class MyAppModule extends PrivateModule {
>> > install(new PrivateModule() {
>> > install(new RedModule());
>> > expose(RedService.class);
>> > });
>> > install(new PrivateModule() {
>> > install(new BlueModule());
>> > expose(BlueService.class);
>> > });
>> >
>> > final Module myAppExecutorService = new PrivateModule() {
>> >
>> bind(ExecutorService.class).toInstance(Executors.newSingleThreadExecutor());
>>
>> > expose(ExecutorService.class);
>> > };
>> > install(new PrivateModule() {
>> > install(myAppExecutorService);
>> > bind(AwesomeService.class).to(AwesomeServiceImpl.class);
>> > expose(AwesomeService.class);
>> > });
>> > expose(AwesomeService.class);
>> > }
>> >
>> > This seems like a common problem one would have using guice. Am I
>> missing something here? What is the best practice for this use case?
>> >
>> > Any assistance would be greatly appreciated!
>> >
>> > Minor Notes:
>> >
>> > - the configure method declaration was elided in all modules shown
>> > - BlueModule and RedModule are intentionally marked final, so extending
>> and overriding the expose method is not a viable solution to the problem
>> >
>> >
>> > --
>> > You received this message because you are subscribed to the Google
>> Groups "google-guice" group.
>> > To unsubscribe from this group and stop receiving emails from it, send
>> an email to [email protected].
>> > To post to this group, send email to [email protected].
>> > Visit this group at http://groups.google.com/group/google-guice?hl=en.
>> > For more options, visit https://groups.google.com/groups/opt_out.
>> >
>> >
>>
>>
--
You received this message because you are subscribed to the Google Groups
"google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-guice?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.