Using assisted injection create a complex dependency tree [duplicate] - java

This question already has answers here:
Guice assisted injection deeper down the dependency hierarchy
(2 answers)
Closed 3 years ago.
I have recently learned about the AssistedInject extension to Guice and I thought it would be a nice solution to some design issues that I have. Unfortunately it seems that this solution is limited to just a one level assisted injection. Here comes an illustration of my problem - let's say we have three classes:
public class AImpl implements A{
#AssistedInject
public AImpl(#Assisted Integer number, B b){
}
}
public class BImpl implements B {
}
public class CImpl implements C {
#AssistedInject
public CImpl(A a){
}
}
a factory interface:
public interface CFactory {
C create(Integer number);
}
and a module:
public class ABCModule extends AbstractModule {
#Override
protected void configure() {
bind(A.class).to(AImpl.class);
bind(B.class).to(BImpl.class);
install(new FactoryModuleBuilder().implement(C.class, CImpl.class).build(CFactory.class));
}
public static void main(String[] args) {
Guice.createInjector(new ABCModule()).getInstance(CFactory.class).create(123);
}
}
Above fails with following stacktrace:
Exception in thread "main" com.google.inject.CreationException: Guice
creation errors:
1) Could not find a suitable constructor in stack.AImpl. Classes must
have either one (and only one) constructor annotated with #Inject or a
zero-argument constructor that is not private. at
stack.AImpl.class(AImpl.java:12) at
stack.ABCModule.configure(ABCModule.java:14)
2) stack.CImpl has #AssistedInject constructors, but none of them
match the parameters in method stack.CFactory.create(). Unable to
create AssistedInject factory. while locating stack.CImpl while
locating stack.C at stack.CFactory.create(CFactory.java:1)
2 errors at
com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:435)
at
com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:154)
at
com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:106)
at com.google.inject.Guice.createInjector(Guice.java:95) at
com.google.inject.Guice.createInjector(Guice.java:72) at
com.google.inject.Guice.createInjector(Guice.java:62) at
stack.ABCModule.main(ABCModule.java:21)
This obviously means that I want too much from the extension - I hoped that the injector will search deep down in the dependecy tree searching for the #Assisted dependency. Is there any way to do this kind of assisted injection or do I need to implement my factory by myself?

Your code has a couple problems. You need to inject an "A" into "C" but your factory is set up to inject an integer
public interface CFactory {
C create(Integer number);
}
Also I don't think there is an #AssistedInject annotation provided by Guice. Use #Inject on the constructor instead, and mark the params you want to pass in with #Assisted

Related

Get around early binding of static methods in java

I have a AbstractBaseRepository. All my Repositories extends from this class. I created another class RepositoryFactory to create any instance of Repository. Due to early binding of static method, I am facing problem.
public abstract class AbstractBaseRepository {
public static <T extends AbstractBaseRepository> T getNewInstance(EntityManagerFactory entityManagerFactory) {
throw new RuntimeException("Override and provide valid initialization");
}
...
}
public class RepositoryFactory {
public static <T extends AbstractBaseRepository> T getRepository(Class<T> cls) {
return T.getNewInstance(entityManagerFactory);
}
...
}
an example subclass
public class DeviceModelRepo extends AbstractBaseRepository {
public static DeviceModelRepo getNewInstance(EntityManagerFactory entityManagerFactory) {
return new DeviceModelRepo(entityManagerFactory);
}
...
}
Whenever I call getRepository() with a valid subclass of AbstractBaseRepository, runtime exception is thrown. This is due to early binding of static methods. During compile time, getNewInstance gets bound with AbstractBaseRepository rather than at runtime with actual type of the class. Any good workarounds?
My first suggestion is using Spring. It is very easy to get a list of all beans created with a certain interface.
Also, if you think of your Repository instances as a type of "plug-in" you might see how Java's ServiceLoader class can help.
Also, another approach is to use a switch statement in the factory and create the instances for each case rather than using static methods on the Repository subclasses.
Finally, I don't recommend reflection solutions but there are ways to load the class based on its name and reflectively creating a new instance.
But overriding static methods is not possible.
What I have understood by seeing your code is that you want to have different implementations of AbstractBaseRepository such as DeviceModelRepo. Then you want a factory class to create the instance of specific implementation of AbstractBaseRepository. Here the major problem is you try to overriding static methods which can never be overwritten but subclass will hide the parent implementation. Please don't use static method for overriding. You can change your implementation as given below and this issue will be resolved.
public abstract class AbstractBaseRepository {
public AbstractBaseRepository(EntityManagerFactory entityManagerFactory){
...
}
//removed method getNewInstance(EntityManagerFactory entityManagerFactory)
...
}
Then below implementation for subclass.
public class DeviceModelRepo extends AbstractBaseRepository {
public DeviceModelRepo(EntityManagerFactory entityManagerFactory) {
super(entityManagerFactory);
...
}
//removed method getNewInstance(EntityManagerFactory entityManagerFactory)
...
}
Now I am providing you two implementation of factory class.
One is having different method for each of implementation, such as getDeviceModelRepository().
Another solution is to use reflection and get repository instance by passing the implementation repository class.
public class RepositoryFactory {
//Solution-1, create separate method for each of repository like below
public static AbstractBaseRepository getDeviceModelRepository() {
return new DeviceModelRepo(entityManagerFactory);
}
//Solution-2, use reflection to get instance of specific implementation
//of AbstractBaseRepository
public static <T extends AbstractBaseRepository> T
getRepository(Class<T> repoClass) throws Exception{
return repoClass.getConstructor(EntityManagerFactory.class)
.newInstance(entityManagerFactory);
}
...
}
With reflection solution, you can get the repository instance as given below.
RepositoryFactory.getRepository(DeviceModelRepo.class)

Guice method injection error : "Unable to create binding for <class>. It was already configured on one or more child injectors or private modules"

I have the following class wherein I need method injection using Guice.
#Singleton
public class A {
private final Injector injector;
#Inject
public A(Injector injector) {
this.injector = injector;
}
public void method1() {
...
final XInterface x = this.injector.getInstance(Key.get(XInterface.class, Names.named("provideX")));
...
}
}
And the module contains the following provides method:
public class MyModule extends AbstractModule {
#Override void configure() {
// no binding and scope for class A
}
#Provides
#Named("provideX")
public XInterface provide(#Named("isTest") boolean isTest, X x, XMock xMock) {
return isTest ? xMock : x;
}
}
Where isTest has a provider in the same module and can be ignored for this discussion.
Now, this injection in class A gives me the following error:
Unable to create binding for `A`. It was already configured on one or more child injectors or private modules. If it was in a PrivateModule, did you forget to expose the binding?
and the rest of the stacktrace isn't very helpful.
After digging around for a while and trying out a few things, I found that just defining a Singleton scope for class A in the MyModule.configure() like so : bind(A.class).in(Singleton.class); and removing the existing #Singleton annotation on the class A solved the error. Now my question is two fold:
What caused the error in my first implementation?
What is the difference between defining a singleton scope in the module and annotating a class with singleton scope and why did that solve the problem?
Thanks in advance!

Does Guice support a way of method injection (non setter injection)?

From what I understand Guice supports injection for: Constructors, Setters (which they call method injection for some reason), fields.
Can it also inject method parameters? For example:
void foo(InterfaceA a, InterfaceA a1){
...
}
interface InterfaceA{
...
}
class A implements InterfaceA{
....
}
class B implements InterfaceA{
....
}
I want to be able to bind a in foo to type A and a1 to B (will probably need annotation but lets ignore that for a second).
I want this to be done on invocation.
This seems different from the normal use cases (c'tor, fields, setters) in the sense that the dependency injection will happen on invocation rather than on object creation.
So is this possible?
Vladimir's answer is correct, but rather than injecting the injector, you can use field injection and Providers to do the same more concisely and to check that dependencies are satisfied at injector creation time. This code is the same as his, but modified to use Providers:
Injector injector = Guice.createInjector(b -> {
b.bind(InterfaceA.class).annotatedWith(Names.named("a")).to(A.class);
b.bind(InterfaceA.class).annotatedWith(Names.named("a1")).to(B.class);
b.bind(Invoke.class);
});
public class Invoke {
// Constructor injection works too, of course. These fields could also be
// made private, but this could make things difficult to test.
#Inject #Named("a") Provider<InterfaceA> aProvider;
#Inject #Named("a1") Provider<InterfaceA> a1Provider;
public void invoke() {
this.foo(aProvider.get(), a1Provider.get());
}
void foo(InterfaceA a, InterfaceA a1){
...
}
}
Well, you can do this:
Injector injector = Guice.createInjector(b -> {
b.bind(InterfaceA.class).annotatedWith(Names.named("a")).to(A.class);
b.bind(InterfaceA.class).annotatedWith(Names.named("a1")).to(B.class);
b.bind(Invoke.class);
});
public class Invoke {
private final Injector injector;
#Inject
Invoke(Injector injector) {
this.injector = injector;
}
public void invoke() {
this.foo(
injector.getInstance(Key.get(InterfaceA.class, Names.named("a"))),
injector.getInstance(Key.get(InterfaceA.class, Names.named("a1")))
);
}
void foo(InterfaceA a, InterfaceA a1){
...
}
}
But nothing more. Guice is a dependency injection framework and it usually means "construct objects with all their dependencies". While method parameters are dependencies formally (since the class is supposed to use them - this is the definition of dependency), they are not usually regarded as those by DI frameworks. It is understandable - this would make these frameworks much more complex for little to no gain, and also Java is not expressive enough language for such things not to look obscenely ugly.

Guice Assisted Inject is ignored?

I've installed a factory in Google Guice with AssistedInject, but I get the following error (I'm running unit tests with JUnit):
com.google.inject.CreationException: Guice creation errors:
1) No implementation for clusterestimator.OptimalClusterEstimatorFactory was bound.
while locating clusterestimator.OptimalClusterEstimatorFactory
for parameter 0 at com.myfeed.algorithm.clusterer.tree.fca.BasicFCATreeFactory.<init>(BasicFCATreeFactory.java:16)
FCAModule.configure(FCAModule.java:29)
This error is the same even if I omit the install(new Factory...); line from my module, which makes me think that the line is somehow being ignored.
Here is the module code:
public class FCAModule extends AbstractModule {
#Override
protected void configure() {
install(new FactoryModuleBuilder() // <-- Factory line that's not working
.implement(OptimalClusterEstimator.class, FCAOptimalClusterEstimator.class)
.build(OptimalClusterEstimatorFactory.class));
bind(ValueWell.class).to(MapBackedValueWell.class).asEagerSingleton();
bind(FCATreeFactory.class).to(BasicFCATreeFactory.class).asEagerSingleton();
bind(ItemFactory.class).to(MapBackedItemFactory.class).asEagerSingleton();
bind(ClustererFactory.class).asEagerSingleton();
bind(ClusterFactory.class).to(MemoryBackedClusterFactory.class).asEagerSingleton();
}
}
Here is the factory interface:
public interface OptimalClusterEstimatorFactory {
public <T> OptimalClusterEstimator createFCA(int kValue, ItemPointReducer<T> pointReducer);
}
Here is the constructor of FCAOPtimalClusterEstimator:
#AssistedInject
public FCAOptimalClusterEstimator(#Assisted int kValue, #Assisted ItemPointReducer<T> pointReducer) {
this.kValue = kValue;
this.pointReducer = pointReducer;
}
Here is the constructor for BasicFCATreeFactory, the first thing to call for the other factory. Note that this factory is not created using AssistedInject because it uses generics.
#Inject
public BasicFCATreeFactory(OptimalClusterEstimatorFactory optimalClusterEstimatorFactory, ClustererFactory clustererFactory, ClusterFactory clusterFactory) {
this.optimalClusterEstimatorFactory = optimalClusterEstimatorFactory;
this.clustererFactory = clustererFactory;
this.clusterFactory = clusterFactory;
}
For some unknown reason, removing the <T> generic part from the factory interface solved the problem. I've had previous issues with generics and Guice, so maybe this is just another one of those quirks because of type erasure.
So the factory interface is now:
public interface OptimalClusterEstimatorFactory {
public OptimalClusterEstimator createFCA(int kValue, ItemPointReducer pointReducer);
}
Guice assisted injection doesn't work well with generics. If you want to use generics then you have write your own factory.

Problem with generic return type in guice assisted inject factory

So far, I successfully used google guice 2. While migrating to guice 3.0, I had troubles with assisted inject factories. Assume the following code
public interface Currency {}
public class SwissFrancs implements Currency {}
public interface Payment<T extends Currency> {}
public class RealPayment implements Payment<SwissFrancs> {
#Inject
RealPayment(#Assisted Date date) {}
}
public interface PaymentFactory {
Payment<Currency> create(Date date);
}
public SwissFrancPaymentModule extends AbstractModule {
protected void configure() {
install(new FactoryModuleBuilder()
.implement(Payment.class, RealPayment.class)
.build(PaymentFactory.class));
}
}
While creating the injector, I get the following exception:
com.google.inject.CreationException: Guice creation errors:
1) Payment<Currency> is an interface, not a concrete class.
Unable to create AssistedInject factory. while locating Payment<Currency>
at PaymentFactory.create(PaymentFactory.java:1)
With the assisted inject creator from guice 2 my configuration works:
bind(PaymentFactory.class).toProvider(
FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));
The only workaround I found so far is to remove the generic parameter from the return type of the factory method:
public interface PaymentFactory {
Payment create(Date date);
}
Does anybody know, why guice 3 doesn't like the generic parameter in the factory method or what I generally misunderstood about assisted inject factories? Thanks!
There are two issues with your code above.
First, RealPayment implements Payment<SwissFrancs>, but PaymentFactory.create returns Payment<Currency>. A Payment<SwissFrancs> cannot be returned from a method that returns Payment<Currency>. If you change the return type of create to Payment<? extends Currency>, then RealPayment will work (because it's a Payment for something that extends Currency).
Second, you DO need to use the version of implement that takes a TypeLiteral as its first argument. The way to do that is to use an anonymous inner class. To represent `Payment' you can use
new TypeLiteral<Payment<? extends Currency>>() {}
See the Javadoc for that TypeLiteral constructor for more information.

Categories

Resources