make bean names unique when considering binding annotations

translate binding annotations into Spring @Qualifiers
This commit is contained in:
Taylor Wicksell
2018-08-21 14:38:01 -07:00
committed by Taylor Wicksell
parent 0153079327
commit 2207c4f1d9
5 changed files with 147 additions and 5 deletions

View File

@@ -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<Module> modules;
private ConfigurableListableBeanFactory beanFactory;
private AtomicBoolean injectorCreated = new AtomicBoolean(false);
private void createInjector(List<Module> 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 {

View File

@@ -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<T> cls = (Class<T>) type;
return new Provider<T>() {
@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);
}
};

View File

@@ -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();
}
}

View File

@@ -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();
}
};
}
}

View File

@@ -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 {}