Just a quick question see if I understand scope correctly.
I understand that scope is to control how/when the instance is created.
Now I have a class in an application, and I want two instance of it; each one will be injected to some other instance.
Is it possible in Guice to create two scopes, for each of the instance? and then I can inject each of the scoped instance to any other instance as I like?
Scopes:
Scopes allow you to control the lifecycle of an object.
You can only bindScope() a scope annotation once. The concept of a scope is injector-level: It's wiring configuration.
You can bind multiple things (keys) into once scope.
You can define as many scopes as you want, but each binding can only be in one scope.
You haven't given the full details of your problem, but it is my experience that in most cases where a custom scope appears initially to be the right answer, it rarely actually is!
Perhaps what you want is to annotate two instances of one type? Something like:
bind(SomeService.class).in(First.class).to(FirstServiceImpl.class);
bind(SomeService.class).in(Second.class).to(SecondServiceImpl.class);
Then you can inject the one you want:
#Inject
SomeConstructor(#First SomeService service) {
}
or:
#Inject
SomeConstructor(#Second SomeService service) {
}
If that doesn't help then you might need to give some more detail of your problem.
Related
So I'm implementing a Dependency Injection framework into my Java project (Google Guice) and everything is pretty good, I like it but there's a small problem;
I want to make a static instance of my main project's class (where it instantiates the dependencies, etc). But I don't know any method to instantiate it using Guice, I can't instantiate it manually because I'm using DI in the constructor of it (I have objects in the constructor) which means that I am not able to access the class' non-static variables needed to instantiate the class.
I tried using a Provider but I couldn't really understand where to bind it, because I don't want to have an interface for the main class (will if needed).
If you are wanting to mix a static instance of a class with dependency injection, you have somewhat missed the point of dependency injection: you can simply inject the instance of the class.
If you want there to be a single instance of a class for your injector, bind it in #Singleton scope: either:
bind(YourClass.class).in(Singleton.class);
in your module's configure() method, or
#Provides #Singleton YourClass provideYourClassInstance() {
// ...
}
in your module, or
#Singleton class YourClass {
// ...
}
in the actual class declaration.
Then just inject this instance like any other:
class SomeOtherClass {
#Inject SomeOtherClass(YourClass instance) {
// ... Do something with instance, like assign it to a field.
}
}
The point is that SomeOtherClass shouldn't need know anything about the lifetime of instance: it simply doesn't matter whether this is a singleton instance, or every class using it has its own instance.
You can get three different answers here depending on the question.
To directly answer the question in the title (DI with arguments in the constructor), you can mix DI with constuctor arguments by instead injecting a Factory. Though you're welcome to write one manually, Guice can do this for you as assisted injection (see FactoryModuleBuilder), or you can use the equivalent code-generated solution AutoFactory popular through Dagger.
If you're trying to initialize a static class's fields in a Guice application, Guice can do that for you as soon as the Injector is created. Simply call requestStaticInjection in a Module you feed to Guice. This, as Andy Turner pointed out, will cause you to miss out on some of the benefits of Guice: Because you're injecting the instance statically, there's very little opportunity for you to provide replacement implementations in tests or in other class reuse. Guice describes this more in the static injections section of its wiki:
When migrating an application from static factories to Guice, it is possible to change incrementally. Static injection is a helpful crutch here. It makes it possible for objects to partially participate in dependency injection, by gaining access to injected types without being injected themselves. [...]
Static members will not be injected at instance-injection time. This API is not recommended for general use because it suffers many of the same problems as static factories: it's clumsy to test, it makes dependencies opaque, and it relies on global state.
The best overall solution is in Andy's answer: Adapt your application to use DI, which will let it inject the objects that you would otherwise make static.
In Guice, when you indicate the lifetime of a certain instance, you will use a scoping annotation like bind(Applebees.class).in(Singleton.class);.
Or you appear to be able to use scope instances like
bind(UserPreferences.class)
.toProvider(UserPreferencesProvider.class)
.in(ServletScopes.REQUEST);
And officially Guice recommends the former approach because the former approach allows us to reuse the Module class.
But I am not sure of the point. My assumption is like this, so please check if this is right.
Scope instances is the term of Servelet, so if you adopt scope instances instead of scoping annotations, the Module class is only applicable to the Servelet. On the other hand, if you use scoping annotations, you can reuse your Module class unless you abandon Guice.
So, is this right? Could you check?
I believe your understanding is correct, but there's a little subtlety about what kind of reuse would be affected.
You're probably referring to this text from the Guice wiki on Scopes (emphasis mine):
The in() clause accepts either a scoping annotation like RequestScoped.class and also Scope instances like ServletScopes.REQUEST:
bind(UserPreferences.class)
.toProvider(UserPreferencesProvider.class)
.in(ServletScopes.REQUEST);
The annotation is preferred because it allows the module to be reused in different types of applications. For example, an #RequestScoped object could be scoped to the HTTP request in a web app and to the RPC when it's in an API server.
Even with Guice's servlet-specific scopes, you can choose between the Scope instance ServletScopes.REQUEST and the #RequestScoped annotation, and choose between in(Scope scope) and in(Class scopeAnnotation) accordingly (see ScopedBindingBuilder). Nearly every scope should have a corresponding annotation, as they are especially useful on classes and #Provides methods.
It's important to realize here that there's always a Scope instance that actually implements the scoping behavior (specifically, wrapping an unscoped Provider so that it can return already-returned instances in the right conditions). To associate an annotation to a Scope instance, you need to make sure that a module calls bindScope, which accepts the Scope annotation class and the Scope instance; for Servlets, Guice has this binding automatically installed via InternalServletModule.
#Override
protected void configure() {
bindScope(RequestScoped.class, REQUEST);
bindScope(SessionScoped.class, SESSION);
// ...
}
So what's the advantage of using in(Class scopeAnnotation)? When binding to a Scope instance, you are telling Guice exactly which Scope instance you want to use, rather than allowing the user an opportunity to use bindScope to bind the annotation to a different Scope instance. In the example I bolded above, you can imagine using the same Module without using the actual Guice servlet extensions (other than the annotations), but this would only be possible if you bind to annotation classes and then call bindScope yourself. If you bind using in(Scope), you'll need to change that line or write a new Module.
This is especially important for your own custom Scope instances and annotations, because it allows you to change your Scoping behavior consistently across your application:
#Override public void configure() {
// BAD: To change the scope, you'll need to change three lines.
// If you don't change all three together, you'll get inconsistent behavior.
bind(A.class).to(AImpl.class).in(MyScope.INSTANCE);
bind(B.class).to(BImpl.class).in(MyScope.INSTANCE);
bindScope(AScoped.class, MyScope.INSTANCE);
}
#Override public void configure() {
// GOOD: To change the scope, you can change one line, and optionally
// extract that line to a separate Module.
bind(A.class).to(AImpl.class).in(AScoped.class);
bind(B.class).to(BImpl.class).in(AScoped.class);
bindScope(AScoped.class, MyScope.INSTANCE);
}
This question is a continuation of Using guice for a framework with injected classes, proper way to initialize?, which I've tried to implement, and also tried other ways to get around the issue, but nothing has worked so far.
The main issue is this. I have an InterfaceA and InterfaceB that are exposed in different parts of the API. There are two classes that implement both of these interfaces, TestClass and RealClass, so that depending on whether I'm testing or doing something else, I can do the following:
bind(InterfaceA.class).to(TestClass.class);
bind(InterfaceB.class).to(TestClass.class);
or, for production:
bind(InterfaceA.class).to(RealClass.class);
bind(InterfaceB.class).to(RealClass.class);
I have two requirements for using these classes:
I need the same instance of TestClass or RealClass to be bound to all injections of InterfaceA and InterfaceB; so, like a singleton pattern, except that:
The singleton is only for a specific scope or child injector, many of which are created during the execution of the program.
The default no-scope approach causes multiple instances of RealClass/TestClass to be created for each interface injection. I don't want that, so I've tried implementing this with scopes, child injectors, and other methods. Nothing has worked:
Child injector approach: I create a new injector and try to bind the TestClass or RealClass to a singleton instance in that injector. The problem is, whether TestClass or RealClass is being used is configured in the parent injector, and since it's a singleton, it's already instantiated (unless in Stage.DEVELOPMENT). There's no way to bind InterfaceA to TestClass, in the parent injector, for example, and then re-bind it as a singleton in the child injector.
Scope approach: I create a custom scope and annotate TestClass and RealClass. Then, I enter and exit this scope to get single instances in that scope. The problem is that my code is multithreaded and having the scope change from one thread affects what the global injector can see and mucks up creating other instances.
Combined child injector and scope approach. I tried creating a child injector for each use of this custom scope, but then binding RealClass in the parent fails with
No scope is bound to name.package.WhateverScope.
because it seems to insist that the WhateverScope is available all the time, not just in the child injector.
All these problems seem to be due to the fact that I need to be able to configure whether to use TestClass or RealClass in the parent, but then to be able to instantiate them later, as a singleton, for a specific group of objects. I'm pulling my hair out over how to get this done!
By the way, the documentation for Guice scopes is horrible and almost impossible to understand. This article is the only one that has gotten me anywhere:
Apologies for a somewhat-breakthrough less than an hour after posting.
I seem to have been able to fix this by somewhat abusing the thread-local scope implementation provided at http://code.google.com/p/google-guice/wiki/CustomScopes. It seems to be a somewhat clean way to solve this problem without using child injectors. I'm not sure if it's 'proper', though. I'll still accept other answers.
Here's what I did. First, I create one instance of the scope, bind it to the appropriate annotation, and make it available in the injector:
ThreadLocalScope scope = new ThreadLocalScope();
bindScope(ExperimentScoped.class, scope);
bind(ThreadLocalScope.class).toInstance(scope);
Then, as the documentation says, I need to bind a fake provider for every type of key that would be seeded in the scope:
bind(SomeKey.class)
.toProvider(ThreadLocalScope.<SomeKey>seededKeyProvider())
.in(ExperimentScoped.class);
bind(SomeOtherKey.class)
.toProvider(ThreadLocalScope.<SomeOtherKey>seededKeyProvider())
.in(ExperimentScoped.class);
I may also have some other scope-able objects that I want to be distinct within each scope, so I bind those too. These are the TestClass and RealClass above. There may be also SomeScopedClass that was annotated with the #ExperimentScoped:
bind(InterfaceA.class).to(TestClass.class).in(ExperimentScoped.class);
bind(InterfaceB.class).to(TestClass.class).in(ExperimentScoped.class);
bind(SomeInterface.class).to(SomeScopedClass.class);
Finally, I can use the scope to create distinct sets of interdependent objects, in parallel from different threads. Each thread can do something like the following, even though they are using the same injector:
ThreadLocalScope scope = injector.getInstance(ThreadLocalScope.class);
scope.enter();
try {
// Seed the seed-able keys
scope.seed(SomeKey.class, keyInstance);
scope.seed(SomeOtherKey.class, otherKeyInstance);
SomeScopedClass instance = injector.getInstance(SomeScopedClass.class);
// Hooray! instance was injected with the seeds and created just for this scope!
}
finally {
scope.exit(); // Throws away the scope and referenced objects.
}
In my case, I can discard the scope completely because I don't care about keeping track of the set of objects in the scope once they're wired up properly. But it probably wouldn't work if I wanted to come back to this scope later and inject some more objects.
Hope this helped someone. The Guice scoping documentation is terrible!
Does scoping work on Guice providers? Suppose I have a FooProvider and bind like thus:
bind(Foo.class).toProvider(FooProvider.class).inScope(ServletScopes.REQUEST)
Will the FooProvider be instantiated once per request?
It should be
bind(Foo.class).toProvider(FooProvider.class).in(ServletScopes.REQUEST);
but otherwise this should work as expected.
No, FooProvider will be instantiated by Guice only once.
The scope applies to the binding, which means in your example that, if Foo is injected into another REQUEST-scoped object, Guice will call FooProvider.get() and will inject the returned Foo into that original object.
If you want the scope applied to FooProvider, then you would have to do something like that (NB: I haven't checked it but it should work):
bind(FooProvider.class).in(ServletScopes.REQUEST);
bind(Foo.class).toProvider(FooProvider.class).in(ServletScopes.REQUEST);
I want to be able to call Interfaces in my class, but have spring instantiate them with the right implementation class behind the scenes.
I.e. Normally you can do:
IClass clz = new Class();
I want to have the line IClass clz; preferable in the middle of a method (or as one of the attributes if it can't be done), where clz is instantiated to the class I want by Spring.
The reason I'd like to do it this way is because I want to be able to be able to change which implementation I want to use simply by editing the context files.
Even better, would be knowing how to do the above with class contructors that expect parameters. i.e. new Class(ar1, arg2);
I hope this makes sense. Any help would be much appreciated.
You can make your class implement BeanFactoryAware and then Spring will inject the bean factory in your class. If you then want to get an instance of a class implementing your interface you say something like:
beanFactory.getBean(IClass.class);
If there are multiple beans that implement the same interface you will have to resolve by name. To create a new object each time you ask this, set the bean scope of the bean you're asking for to "prototype".
You can include code such as:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
IClass clz = (IClass) context.getBean("beanName");
Not saying this is better per se than Gerco's answer btw, just it's an option, depending what you want to do.
You can also implement the ApplicationContextAware interface: I've found that using ApplicationContext gives me beans with filled-in properties, e.g. if you have an app.properties file which contains key/value property pairs which you expect to be resolved within the Spring config, beans retrieved via BeanFactory calls may not resolve those.
See this previous SO topic for more info.