diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..578c97e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: java + +install: mvn install diff --git a/src/main/java/org/springframework/guice/SpringModule.java b/src/main/java/org/springframework/guice/SpringModule.java index bf4c7e8..e2b5794 100644 --- a/src/main/java/org/springframework/guice/SpringModule.java +++ b/src/main/java/org/springframework/guice/SpringModule.java @@ -13,6 +13,9 @@ package org.springframework.guice; +import java.util.HashMap; +import java.util.Map; + import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; @@ -23,6 +26,7 @@ import org.springframework.util.ClassUtils; import com.google.inject.Binder; import com.google.inject.Module; import com.google.inject.Provider; +import com.google.inject.ProvisionException; /** * @author Dave Syer @@ -31,16 +35,19 @@ import com.google.inject.Provider; public class SpringModule implements Module { private DefaultListableBeanFactory beanFactory; - + private GuiceModuleMetadata metadata = new GuiceModuleMetadata(); + private Map, Provider> bound = new HashMap, Provider>(); + public SpringModule(GenericApplicationContext context) { this.beanFactory = (DefaultListableBeanFactory) context .getAutowireCapableBeanFactory(); - if (beanFactory.getBeanNamesForType(GuiceModuleMetadata.class).length>0) { + if (beanFactory.getBeanNamesForType(GuiceModuleMetadata.class).length > 0) { + this.metadata = beanFactory.getBean(GuiceModuleMetadata.class); + } else if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, + GuiceModuleMetadata.class).length > 0) { this.metadata = beanFactory.getBean(GuiceModuleMetadata.class); - } else if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, GuiceModuleMetadata.class).length>0) { - this.metadata = beanFactory.getBean(GuiceModuleMetadata.class); } } @@ -54,12 +61,11 @@ public class SpringModule implements Module { @SuppressWarnings("unchecked") final Class cls = (Class) type; final String beanName = name; - Provider provider = new Provider() { - @Override - public Object get() { - return beanFactory.getBean(beanName, cls); - } - }; + Provider provider = new BeanFactoryProvider(beanFactory, + beanName, type); + if (bound.get(type) != null) { + continue; // TODO: named beans + } if (!cls.isInterface() && !ClassUtils.isCglibProxyClass(cls)) { bindConditionally(binder, cls, provider); } @@ -78,6 +84,45 @@ public class SpringModule implements Module { return; } binder.bind(cls).toProvider(provider); + bound.put(cls, provider); + } + + private static class BeanFactoryProvider implements Provider { + + private DefaultListableBeanFactory beanFactory; + private String name; + private Class type; + private Object result; + + public BeanFactoryProvider(DefaultListableBeanFactory beanFactory, String name, + Class type) { + this.beanFactory = beanFactory; + this.name = name; + this.type = type; + } + + @Override + public Object get() { + if (result == null) { + String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( + beanFactory, type); + if (names.length == 1) { + result = beanFactory.getBean(name, type); + } else { + for (String name : names) { + if (beanFactory.getBeanDefinition(name).isPrimary()) { + result = beanFactory.getBean(name, type); + break; + } + } + if (result == null) { + throw new ProvisionException( + "No primary bean definition for type: " + type); + } + } + } + return result; + } } } diff --git a/src/test/java/org/springframework/guice/SpringModuleMetadataTests.java b/src/test/java/org/springframework/guice/SpringModuleMetadataTests.java index 22c005a..5cad5a0 100644 --- a/src/test/java/org/springframework/guice/SpringModuleMetadataTests.java +++ b/src/test/java/org/springframework/guice/SpringModuleMetadataTests.java @@ -25,13 +25,14 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AssignableTypeFilter; import com.google.inject.ConfigurationException; -import com.google.inject.CreationException; import com.google.inject.Guice; import com.google.inject.Injector; +import com.google.inject.ProvisionException; /** * @author Dave Syer @@ -45,15 +46,20 @@ public class SpringModuleMetadataTests { @Test public void twoConfigClasses() throws Exception { Injector injector = createInjector(TestConfig.class, OtherConfig.class); - assertNotNull(injector.getBinding(Service.class)); + assertNotNull(injector.getInstance(Service.class)); } @Test public void twoServices() throws Exception { - // Two beans with the same interface cause problems at startup - expected.expect(CreationException.class); Injector injector = createInjector(TestConfig.class, MoreConfig.class); - assertNotNull(injector.getBinding(Service.class)); + expected.expect(ProvisionException.class); + assertNotNull(injector.getInstance(Service.class)); + } + + @Test + public void twoServicesOnePrimary() throws Exception { + Injector injector = createInjector(TestConfig.class, PrimaryConfig.class); + assertNotNull(injector.getInstance(Service.class)); } @Test @@ -67,7 +73,7 @@ public class SpringModuleMetadataTests { public void excludes() throws Exception { Injector injector = createInjector(TestConfig.class, MetadataExcludesConfig.class); expected.expect(ConfigurationException.class); - assertNull(injector.getBinding(Service.class)); + assertNull(injector.getInstance(Service.class)); } private Injector createInjector(Class... config) { @@ -120,6 +126,15 @@ public class SpringModuleMetadataTests { } } + @Configuration + public static class PrimaryConfig { + @Bean + @Primary + public Service primary() { + return new MyService(); + } + } + @Configuration public static class MoreConfig { @Bean