Skip to content

Instantly share code, notes, and snippets.

@electrotype
Last active December 14, 2015 15:08
Show Gist options
  • Save electrotype/5105100 to your computer and use it in GitHub Desktop.
Save electrotype/5105100 to your computer and use it in GitHub Desktop.
Bug Guice - ConstructorInjector
import static org.junit.Assert.*;
import java.util.UUID;
import org.junit.Test;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.matcher.AbstractMatcher;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
public class ExampleTest
{
public static class ClassAAA
{
private final String id;
private final boolean myParam;
private final IClassBBBFactory classBBBFactory;
private ClassBBB clasBBB;
@Inject
public ClassAAA(@Assisted boolean myParam,
IClassBBBFactory classBBBFactory)
{
this.myParam = myParam;
this.id = UUID.randomUUID().toString();
this.classBBBFactory = classBBBFactory;
}
public void init()
{
if(this.myParam)
{
this.clasBBB = this.classBBBFactory.create(UUID.randomUUID().toString());
}
}
public String getId()
{
return this.id;
}
public ClassBBB getClassBBB()
{
return this.clasBBB;
}
}
public static class ClassBBB
{
private final IClassAAAFactory classAAAFactory;
private ClassAAA clasAAACreatedByClassBBB;
@Inject
public ClassBBB(@Assisted String myParam,
IClassAAAFactory classAAAFactory)
{
this.classAAAFactory = classAAAFactory;
}
public void init()
{
// The problem is here. Even if the factory should return a new instance
// of ClassAAA, it returns the one that is creating ClassBBB!
this.clasAAACreatedByClassBBB = this.classAAAFactory.create(false);
}
public ClassAAA getClasAAACreatedByClassBBB()
{
return this.clasAAACreatedByClassBBB;
}
}
public static interface IClassAAAFactory
{
public ClassAAA create(boolean someParam);
}
public static interface IClassBBBFactory
{
public ClassBBB create(String someParam);
}
@Test
public void bugGuiceConstructorInjector()
{
Injector injector = Guice.createInjector(new AbstractModule()
{
@Override
protected void configure()
{
bindListener(new AbstractMatcher<TypeLiteral<?>>()
{
@Override
public boolean matches(TypeLiteral<?> typeLiteral)
{
return ClassAAA.class.isAssignableFrom(typeLiteral.getRawType()) ||
ClassBBB.class.isAssignableFrom(typeLiteral.getRawType());
}
},
new TypeListener()
{
@Override
public <I> void hear(final TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter)
{
typeEncounter.register(new InjectionListener<I>()
{
@Override
public void afterInjection(Object obj)
{
if(obj instanceof ClassAAA)
{
((ClassAAA)obj).init();
}
if(obj instanceof ClassBBB)
{
((ClassBBB)obj).init();
}
}
});
}
});
install(new FactoryModuleBuilder().build(IClassAAAFactory.class));
install(new FactoryModuleBuilder().build(IClassBBBFactory.class));
}
});
IClassAAAFactory classAAAFactory = injector.getInstance(IClassAAAFactory.class);
ClassAAA classAAA = classAAAFactory.create(true);
// Shouldn't be the same id!
assertNotEquals(classAAA.getClassBBB().getClasAAACreatedByClassBBB().getId(), classAAA.getId());
}
}
@electrotype
Copy link
Author

Note that I check the IDs and not the references, in case there are proxies involved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment