Here's my problem:
It's first important to know that I'm writing a simulation. This is a standalone application, and is single-threaded. I have essentially two classes of objects that have different scoping requirements.
Classes that should be used as singletons throughout the entire simulation. An instance of Random, as an example.
Groups of classes that are created together, and within the group, each instance should be treated like a Singleton. For example, say
RootObject
is the top level class, and has a dependency toClassA
andClassB
, both of which have a dependency toClassD
. For any givenRootObject
, both of its dependencies (ClassA
andClassB
) should depend on the same instance ofClassD
. However, instances ofClassD
should not be shared across different instances ofRootObject
.
Hopefully that makes sense. I can think of two approaches to this. One is to mark all of the injected objects as Singletons, create the root injector, and spin off a child injector each time I need to create a new RootObject
instance. Then, the instances of RootObject
and all of its dependencies are created as Singletons, but that scoping information is thrown away the next time I go to create another RootObject
.
The second approach is to implement some type of custom scope.
The Guice documentation gives conflicting advice... On one hand, it says that you should have a single injector, and that ideally it is called once to create some top level class. On the other hand, it says to stay away from custom scopes.
May I ask why do you need to have singletons ?
I would not recommend to create custom scope. The best and easiest way to mix scopes is to inject providers instead of objects. With the providers you can control the scope of your object from you business code logic.
See this Guice documentation for details.
It seems to me like you need a scope for each instance of
RootObject
and all its dependencies.In Guice you can create a custom scope, say
@ObjectScoped
, like this:Now just place
RootObject
,A
,B
andD
into this scope:Now each
RootObject
has its own scope. You can implement this as a simpleHashMap
:To integrate these scopes with Guice you will need a
com.google.inject.Scope
-implementation which lets you switch the scopes and the corresponding wiring in yourModule
.Initialize your program like this:
Create the first instance of
RootObject
and its corresponding scope:Just switch the scope for a second group of objects:
Test if your requirements are met:
To work with a group of objects just enter its scope and use the injector:
Have you considered using a provider? It would be easy to write one that meets your requirements, e.g.:
You can use the provider two ways:
@Provides
interface or the.toProvider()
binding decoration.RootObject
instances as needed.Hope that this helps.
With a little setup, Guice can provide two-tier scoping without a custom scope. The outer one is
@Singleton
, and the inner is@RequestScoped
, provided by theservlet
extension. This works even if you're talking about something other than a Java EE servlet container.Have a single, root-level injector to handle your singletons. Be sure to declare the request scope annotation in your root-level module as so:
When you want to enter a sub-scope, you do this:
What we're doing here is using
ServletScopes.scopeRequest
to run the anonymousCallable
inside a new request scope. TheCallable
then creates a child injector and adds a new binding for any per-request seed objects.The seeds are objects that
@RequestScoped
things would need but couldn't be created by Guice alone, like requests or iteration IDs. The newHashMap
passed as the second argument toscopeRequest
is another way to literally insert seeds into the new scope. I prefer the submodule way, since the bindings for the seeded values are always required anyway.The child injector is then "in" the request scope and can be used to provide
@RequestScoped
things.See this also: How to use ServletScopes.scopeRequest() and ServletScopes.continueRequest()?