diff --git a/src/main/java/org/springframework/guice/annotation/ModuleRegistryConfiguration.java b/src/main/java/org/springframework/guice/annotation/ModuleRegistryConfiguration.java index 57a02ed..bc19bb1 100644 --- a/src/main/java/org/springframework/guice/annotation/ModuleRegistryConfiguration.java +++ b/src/main/java/org/springframework/guice/annotation/ModuleRegistryConfiguration.java @@ -37,9 +37,11 @@ import com.google.inject.spi.PrivateElements; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.support.AutowireCandidateQualifier; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -66,7 +68,6 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor private static final String SPRING_GUICE_DEDUPE_BINDINGS_PROPERTY_NAME = "spring.guice.dedup"; private ApplicationContext applicationContext; private List modules; - private ConfigurableListableBeanFactory beanFactory; private AtomicBoolean injectorCreated = new AtomicBoolean(false); private void createInjector(List modules, @@ -122,19 +123,33 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor bean.setResourceDescription(SpringModule.SPRING_GUICE_SOURCE); } bean.setAttribute(SpringModule.SPRING_GUICE_SOURCE, true); + if (key.getAnnotation() != null) { + bean.addQualifier(new AutowireCandidateQualifier(Qualifier.class, + getValueAttributeForNamed(key.getAnnotation()))); + } registry.registerBeanDefinition(extractName(key), bean); } } private String extractName(Key key) { - final Annotation annotation = key.getAnnotation(); + final String className = key.getTypeLiteral().getRawType().getSimpleName(); + String valueAttribute = getValueAttributeForNamed(key.getAnnotation()); + if (valueAttribute != null) { + return valueAttribute + "_" + className; + } else { + return className; + } + } + + private String getValueAttributeForNamed(Annotation annotation) { if (annotation instanceof Named) { return ((Named) annotation).value(); } else if (annotation instanceof javax.inject.Named) { return ((javax.inject.Named) annotation).value(); + } else { + return null; } - return key.getTypeLiteral().getRawType().getSimpleName(); } @Override @@ -235,7 +250,6 @@ class ModuleRegistryConfiguration implements BeanDefinitionRegistryPostProcessor @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; beanFactory.registerSingleton("guiceInjectorInitializer", new GuiceInjectorInitializingBeanPostProcessor(){ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { diff --git a/src/main/java/org/springframework/guice/injector/SpringInjector.java b/src/main/java/org/springframework/guice/injector/SpringInjector.java index 8be196b..44e4d3b 100644 --- a/src/main/java/org/springframework/guice/injector/SpringInjector.java +++ b/src/main/java/org/springframework/guice/injector/SpringInjector.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; @@ -128,8 +129,12 @@ public class SpringInjector implements Injector { @SuppressWarnings("unchecked") final Class cls = (Class) type; return new Provider() { + @SuppressWarnings("unchecked") @Override public T get() { + if(key.getAnnotation() != null) { + return (T) BeanFactoryAnnotationUtils.qualifiedBeanOfType(SpringInjector.this.beanFactory, type, name); + } return SpringInjector.this.beanFactory.getBean(cls); } }; diff --git a/src/test/java/org/springframework/guice/BindingAnnotationTests.java b/src/test/java/org/springframework/guice/BindingAnnotationTests.java index dcefd4b..0d3e28c 100644 --- a/src/test/java/org/springframework/guice/BindingAnnotationTests.java +++ b/src/test/java/org/springframework/guice/BindingAnnotationTests.java @@ -20,6 +20,8 @@ import org.springframework.guice.BindingAnnotationTests.SomeDependencyWithNamedA import org.springframework.guice.BindingAnnotationTests.SomeDependencyWithQualifierOnProvider; import org.springframework.guice.BindingAnnotationTests.SomeDependencyWithQualifierOnProviderWhichImplementsSomeInterface; import org.springframework.guice.BindingAnnotationTests.SomeInterface; +import org.springframework.guice.BindingAnnotationTests.SomeNamedDepWithType1; +import org.springframework.guice.BindingAnnotationTests.SomeNamedDepWithType2; import org.springframework.guice.annotation.EnableGuiceModules; import com.google.inject.BindingAnnotation; @@ -61,6 +63,12 @@ public class BindingAnnotationTests { SomeInterface someInterface = injector.getInstance(Key.get(SomeInterface.class, SomeQualifierAnnotation.class)); assertNotNull(someInterface); + //Check different types with same @Named + assertNotNull(injector.getInstance(SomeNamedDepWithType1.class)); + assertNotNull(injector.getInstance(SomeNamedDepWithType2.class)); + + assertNotNull(injector.getInstance(Key.get(SomeNamedDepWithType1.class, Names.named("sameNameDifferentType")))); + assertNotNull(injector.getInstance(Key.get(SomeNamedDepWithType2.class, Names.named("sameNameDifferentType")))); context.close(); } @@ -71,6 +79,8 @@ public class BindingAnnotationTests { public static class SomeDependencyWithGuiceNamedAnnotationOnProvider {} public static interface SomeInterface{} public static class SomeDependencyWithQualifierOnProviderWhichImplementsSomeInterface implements SomeInterface {} + public static class SomeNamedDepWithType1 {} + public static class SomeNamedDepWithType2 {} } @Qualifier @@ -129,4 +139,16 @@ class BindingAnnotationTestsConfig { public SomeInterface someInterface() { return new SomeDependencyWithQualifierOnProviderWhichImplementsSomeInterface(); } + + @Bean + @Named("sameNameDifferentType") + public SomeNamedDepWithType1 someNamedDepWithType1() { + return new SomeNamedDepWithType1(); + } + + @Bean + @Named("sameNameDifferentType") + public SomeNamedDepWithType2 someNamedDepWithType2() { + return new SomeNamedDepWithType2(); + } } \ No newline at end of file diff --git a/src/test/java/org/springframework/guice/DuplicateNamesDifferentTypesTests.java b/src/test/java/org/springframework/guice/DuplicateNamesDifferentTypesTests.java new file mode 100644 index 0000000..bcb59a5 --- /dev/null +++ b/src/test/java/org/springframework/guice/DuplicateNamesDifferentTypesTests.java @@ -0,0 +1,94 @@ +package org.springframework.guice; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.guice.DuplicateNamesDifferentTypesTests.SomeJavaxNamedDepWithType1; +import org.springframework.guice.DuplicateNamesDifferentTypesTests.SomeJavaxNamedDepWithType2; +import org.springframework.guice.DuplicateNamesDifferentTypesTests.SomeNamedDepWithType1; +import org.springframework.guice.DuplicateNamesDifferentTypesTests.SomeNamedDepWithType2; +import org.springframework.guice.annotation.EnableGuiceModules; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Provides; +import com.google.inject.name.Named; +import com.google.inject.name.Names; + +public class DuplicateNamesDifferentTypesTests { + + @Test + public void verifyNoDuplicateBindingErrorWhenDedupeEnabled() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + DuplicateNamesDifferentTypesTestsConfig.class); + + //Check Guice @Named + assertNotNull(context.getBean(SomeNamedDepWithType1.class)); + assertNotNull(context.getBean(SomeNamedDepWithType2.class)); + assertNotNull(BeanFactoryAnnotationUtils.qualifiedBeanOfType(context.getBeanFactory(), SomeNamedDepWithType1.class, "sameNameDifferentType")); + + //Check javax @Named + assertNotNull(context.getBean(SomeJavaxNamedDepWithType1.class)); + assertNotNull(context.getBean(SomeJavaxNamedDepWithType2.class)); + assertNotNull(BeanFactoryAnnotationUtils.qualifiedBeanOfType(context.getBeanFactory(), SomeJavaxNamedDepWithType1.class, "sameJavaxName")); + context.getBeansOfType(SomeJavaxNamedDepWithType1.class); + + context.close(); + } + + public static class SomeNamedDepWithType1 {} + public static class SomeNamedDepWithType2 {} + public static class SomeJavaxNamedDepWithType1 {} + public static class SomeJavaxNamedDepWithType2 {} + + public static class SomeClassWithDeps{ + @Autowired + @Qualifier("sameJavaxName2") + SomeJavaxNamedDepWithType1 qualified; + + @Autowired + @Named("sameJavaxName2") + SomeJavaxNamedDepWithType1 named; + + @Autowired + @javax.inject.Named("sameJavaxName2") + SomeJavaxNamedDepWithType1 javaxNamed; + } +} + +@EnableGuiceModules +@Configuration +class DuplicateNamesDifferentTypesTestsConfig { + + @Bean + public Module module() { + return new AbstractModule() { + @Override + protected void configure() { + bind(SomeNamedDepWithType1.class).annotatedWith(Names.named("sameNameDifferentType")) + .to(SomeNamedDepWithType1.class); + bind(SomeNamedDepWithType2.class).annotatedWith(Names.named("sameNameDifferentType")) + .to(SomeNamedDepWithType2.class); + } + + @Provides + @Named("sameJavaxName") + public SomeJavaxNamedDepWithType1 someJavaxNamedDepWithType1() { + return new SomeJavaxNamedDepWithType1(); + } + + + @Provides + @Named("sameJavaxName") + public SomeJavaxNamedDepWithType2 someJavaxNamedDepWithType2() { + return new SomeJavaxNamedDepWithType2(); + } + }; + } +} \ No newline at end of file diff --git a/src/test/java/org/springframework/guice/PrivateModuleTests.java b/src/test/java/org/springframework/guice/PrivateModuleTests.java index 329341d..e3a3c4d 100644 --- a/src/test/java/org/springframework/guice/PrivateModuleTests.java +++ b/src/test/java/org/springframework/guice/PrivateModuleTests.java @@ -7,6 +7,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -45,7 +46,8 @@ public class PrivateModuleTests { assertNotNull(injectorProvidedPrivateBinding); SomeInterface springProvidedPrivateBinding = context.getBean(SomeInterface.class); assertNotNull(springProvidedPrivateBinding); - SomeInterface namedPrivateBinding = context.getBean("exposed",SomeInterface.class); + SomeInterface namedPrivateBinding = BeanFactoryAnnotationUtils.qualifiedBeanOfType(context.getBeanFactory(), + SomeInterface.class, "exposed"); assertNotNull(namedPrivateBinding); assertEquals(injectorProvidedPrivateBinding, springProvidedPrivateBinding); assertEquals(injectorProvidedPrivateBinding, namedPrivateBinding); @@ -64,6 +66,11 @@ public class PrivateModuleTests { public void verifyPrivateModulesPrivateBindingsAreNotExposedViaSpring() { context.getBean("notexposed",SomeInterface.class); } + + @Test(expected = NoSuchBeanDefinitionException.class) + public void verifyPrivateModulesPrivateBindingsAreNotExposedViaSpringWithQualifier() { + BeanFactoryAnnotationUtils.qualifiedBeanOfType(context.getBeanFactory(), SomeInterface.class, "notexposed"); + } public static interface SomeInterface {} public static class SomePrivateBinding implements SomeInterface {}