Make SpringModule aware of bindings in wrapping Injector

See gh-13, gh-12
This commit is contained in:
Taylor Wicksell
2017-05-23 11:23:13 -07:00
committed by Dave Syer
parent c42ba69532
commit b26bdb4125
6 changed files with 348 additions and 65 deletions

View File

@@ -25,7 +25,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.guice.annotation.GuiceModule;
import org.springframework.guice.module.SpringModule;
import com.google.inject.ConfigurationException;
@@ -73,10 +72,7 @@ public class GuiceModuleAnnotationTests {
}
private Injector createInjector(Class<?>... config) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(config);
context.refresh();
Injector injector = Guice.createInjector(new SpringModule(context));
Injector injector = Guice.createInjector(new SpringModule(new AnnotationConfigApplicationContext(config)));
return injector;
}

View File

@@ -0,0 +1,201 @@
package org.springframework.guice.module;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
import javax.inject.Provider;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Scopes;
import com.google.inject.spi.ProvisionListener;
import com.google.inject.util.Providers;
import org.junit.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.guice.injector.GuiceAutowireCandidateResolver;
import org.springframework.test.util.AopTestUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
public class SpringModuleGuiceBindingAwareTest {
@Test
public void testAllDependenciesInjectedAndLifeycleMethodsCalledOnce() {
Injector injector = Guice.createInjector(new SimpleGuiceModule(),
new SpringModule(new Provider<ConfigurableListableBeanFactory>() {
@Override
public ConfigurableListableBeanFactory get() {
PartiallyRefreshableApplicationContext context = new PartiallyRefreshableApplicationContext();
context.register(
GuiceProjectWithSpringLibraryTestSpringConfig.class);
context.getBeanFactory().registerSingleton("refreshListener",
new ContextRefreshingProvisionListener(context));
context.partialRefresh();
return context.getBeanFactory();
}
}));
// check guice provided bindings
assertNotNull(injector.getInstance(GuiceDependency1.class));
assertNotNull(injector.getInstance(IGuiceDependency1.class));
// check spring bindings as interface
ISpringBean springBean = injector.getInstance(ISpringBean.class);
assertNotNull(springBean);
assertNotNull(springBean.getDep1());
assertNotNull(springBean.getDep2());
assertNotNull(springBean.getDep3());
// invoke a method to make sure we aren't dealing with a lazy proxy
assertEquals("done", springBean.getDep1().doWork());
// check binding equality
assertSame(injector.getInstance(IGuiceDependency1.class),
AopTestUtils.getTargetObject(springBean.getDep1()));
assertSame(injector.getInstance(IGuiceDependency2.class),
AopTestUtils.getTargetObject(springBean.getDep2()));
assertSame(injector.getInstance(IGuiceDependency3.class),
AopTestUtils.getTargetObject(springBean.getDep3()));
}
static class SimpleGuiceModule extends AbstractModule {
@Override
protected void configure() {
bind(IGuiceDependency1.class).to(GuiceDependency1.class).in(Scopes.SINGLETON); // test
// normal
// binding
bind(IGuiceDependency2.class).toInstance(new IGuiceDependency2() {
}); // test instance binding
bind(IGuiceDependency3.class)
.toProvider(Providers.of(new IGuiceDependency3() {
})); // test provider binding
}
}
@Configuration
static class GuiceProjectWithSpringLibraryTestSpringConfig {
@Bean
public ISpringBean springDefinedSomething(IGuiceDependency1 dependency) {
return new SpringBean(dependency);
}
@Bean
public ApplicationListener<ApplicationEvent> eventListener(
final IGuiceDependency1 dependency) {
return new ApplicationListener<ApplicationEvent>() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
dependency.doWork();
}
};
}
}
static interface IGuiceDependency1 {
String doWork();
}
static interface IGuiceDependency2 {
}
static interface IGuiceDependency3 {
}
static class GuiceDependency1 implements IGuiceDependency1 {
public String doWork() {
return "done";
}
}
static interface ISpringBean {
IGuiceDependency1 getDep1();
IGuiceDependency2 getDep2();
IGuiceDependency3 getDep3();
}
static class SpringBean implements ISpringBean {
private final IGuiceDependency1 dep1;
@Inject
private IGuiceDependency2 dep2;
@Inject
private IGuiceDependency3 dep3;
@Inject
public SpringBean(IGuiceDependency1 dependency) {
this.dep1 = dependency;
}
@Override
public IGuiceDependency1 getDep1() {
return dep1;
}
@Override
public IGuiceDependency2 getDep2() {
return dep2;
}
@Override
public IGuiceDependency3 getDep3() {
return dep3;
}
}
private static final class PartiallyRefreshableApplicationContext
extends AnnotationConfigApplicationContext {
private final AtomicBoolean partiallyRefreshed = new AtomicBoolean(false);
/*
* Initializes beanFactoryPostProcessors only to ensure that all BeanDefinition's
* are available
*/
void partialRefresh() {
invokeBeanFactoryPostProcessors(getBeanFactory());
}
@Override
protected void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory) {
if (partiallyRefreshed.compareAndSet(false, true)) {
super.invokeBeanFactoryPostProcessors(beanFactory);
}
}
}
private static final class ContextRefreshingProvisionListener
implements ProvisionListener {
private final ConfigurableApplicationContext context;
private final AtomicBoolean initialized = new AtomicBoolean(false);
private ContextRefreshingProvisionListener(
ConfigurableApplicationContext context) {
this.context = context;
}
@Override
public <T> void onProvision(ProvisionInvocation<T> provision) {
if (!initialized.getAndSet(true) && !context.isActive()) {
context.refresh();
}
provision.provision();
}
}
}

View File

@@ -15,6 +15,7 @@ package org.springframework.guice.module;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -34,10 +35,8 @@ public class SpringModuleWiringTests extends AbstractCompleteWiringTests {
@Override
protected Injector createInjector() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(TestConfig.class);
context.refresh();
return Guice.createInjector(new SpringModule(context));
return Guice.createInjector(new SpringModule(
new AnnotationConfigApplicationContext(TestConfig.class)));
}
@Configuration
@@ -61,10 +60,11 @@ public class SpringModuleWiringTests extends AbstractCompleteWiringTests {
public Thing that() {
return new Thing();
}
@Bean
public Parameterized<String> parameterizedBean() {
return new Parameterized<String>(){};
return new Parameterized<String>() {
};
}
}
}