support Guice binding annotations in SpringModule
This commit is contained in:
committed by
Taylor Wicksell
parent
1680610faf
commit
b2a767e51a
@@ -13,13 +13,17 @@
|
||||
|
||||
package org.springframework.guice.module;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
@@ -30,17 +34,23 @@ import com.google.inject.Key;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.Stage;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.internal.Annotations;
|
||||
import com.google.inject.matcher.Matchers;
|
||||
import com.google.inject.name.Names;
|
||||
import com.google.inject.spi.ProvisionListener;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.StandardMethodMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
@@ -95,9 +105,11 @@ public class SpringModule extends AbstractModule {
|
||||
private void bind(ConfigurableListableBeanFactory beanFactory) {
|
||||
for (String name : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition definition = beanFactory.getBeanDefinition(name);
|
||||
|
||||
if (definition.hasAttribute(SPRING_GUICE_SOURCE)) {
|
||||
continue;
|
||||
}
|
||||
Optional<Annotation> bindingAnnotation = getAnnotationForBeanDefinition(definition);
|
||||
if (definition.isAutowireCandidate()
|
||||
&& definition.getRole() == AbstractBeanDefinition.ROLE_APPLICATION) {
|
||||
Class<?> type = beanFactory.getType(name);
|
||||
@@ -109,21 +121,84 @@ public class SpringModule extends AbstractModule {
|
||||
Provider<?> namedProvider = BeanFactoryProvider.named(beanFactory,
|
||||
beanName, type);
|
||||
if (!type.isInterface() && !ClassUtils.isCglibProxyClass(type)) {
|
||||
bindConditionally(binder(), name, type, typeProvider, namedProvider);
|
||||
bindConditionally(binder(), name, type, typeProvider, namedProvider, bindingAnnotation);
|
||||
}
|
||||
for (Class<?> iface : ClassUtils.getAllInterfacesForClass(type)) {
|
||||
bindConditionally(binder(), name, iface, typeProvider, namedProvider);
|
||||
bindConditionally(binder(), name, iface, typeProvider, namedProvider, bindingAnnotation);
|
||||
}
|
||||
for (Type iface : type.getGenericInterfaces()) {
|
||||
bindConditionally(binder(), name, iface, typeProvider, namedProvider);
|
||||
bindConditionally(binder(), name, iface, typeProvider, namedProvider, bindingAnnotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Annotation> getAnnotationForBeanDefinition(BeanDefinition definition) {
|
||||
if (definition instanceof AnnotatedBeanDefinition
|
||||
&& ((AnnotatedBeanDefinition) definition).getFactoryMethodMetadata() != null) {
|
||||
try {
|
||||
Method factoryMethod = getFactoryMethod(beanFactory, definition);
|
||||
return Arrays.stream(AnnotationUtils.getAnnotations(factoryMethod))
|
||||
.filter(a -> Annotations.isBindingAnnotation(a.annotationType())).findFirst();
|
||||
} catch (Exception e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private Method getFactoryMethod(ConfigurableListableBeanFactory beanFactory,
|
||||
BeanDefinition definition) throws Exception {
|
||||
if (definition instanceof AnnotatedBeanDefinition) {
|
||||
MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) definition)
|
||||
.getFactoryMethodMetadata();
|
||||
if (factoryMethodMetadata instanceof StandardMethodMetadata) {
|
||||
return ((StandardMethodMetadata) factoryMethodMetadata)
|
||||
.getIntrospectedMethod();
|
||||
}
|
||||
}
|
||||
BeanDefinition factoryDefinition = beanFactory
|
||||
.getBeanDefinition(definition.getFactoryBeanName());
|
||||
Class<?> factoryClass = ClassUtils.forName(factoryDefinition.getBeanClassName(),
|
||||
beanFactory.getBeanClassLoader());
|
||||
return getFactoryMethod(definition, factoryClass);
|
||||
}
|
||||
|
||||
private Method getFactoryMethod(BeanDefinition definition, Class<?> factoryClass) {
|
||||
Method uniqueMethod = null;
|
||||
for (Method candidate : getCandidateFactoryMethods(definition, factoryClass)) {
|
||||
if (candidate.getName().equals(definition.getFactoryMethodName())) {
|
||||
if (uniqueMethod == null) {
|
||||
uniqueMethod = candidate;
|
||||
}
|
||||
else if (!hasMatchingParameterTypes(candidate, uniqueMethod)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return uniqueMethod;
|
||||
}
|
||||
|
||||
private Method[] getCandidateFactoryMethods(BeanDefinition definition,
|
||||
Class<?> factoryClass) {
|
||||
return shouldConsiderNonPublicMethods(definition)
|
||||
? ReflectionUtils.getAllDeclaredMethods(factoryClass)
|
||||
: factoryClass.getMethods();
|
||||
}
|
||||
|
||||
private boolean shouldConsiderNonPublicMethods(BeanDefinition definition) {
|
||||
return (definition instanceof AbstractBeanDefinition)
|
||||
&& ((AbstractBeanDefinition) definition).isNonPublicAccessAllowed();
|
||||
}
|
||||
|
||||
private boolean hasMatchingParameterTypes(Method candidate, Method current) {
|
||||
return Arrays.equals(candidate.getParameterTypes(), current.getParameterTypes());
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void bindConditionally(Binder binder, String name, Type type,
|
||||
Provider typeProvider, Provider namedProvider) {
|
||||
Provider typeProvider, Provider namedProvider, Optional<Annotation> bindingAnnotation) {
|
||||
if (!this.matcher.matches(name, type)) {
|
||||
return;
|
||||
}
|
||||
@@ -141,7 +216,8 @@ public class SpringModule extends AbstractModule {
|
||||
StageTypeKey stageTypeKey = new StageTypeKey(binder.currentStage(), type);
|
||||
if (this.bound.get(stageTypeKey) == null) {
|
||||
// Only bind one provider for each type
|
||||
binder.withSource(SPRING_GUICE_SOURCE).bind(Key.get(type))
|
||||
Key<Object> key = bindingAnnotation.map(a ->(Key<Object>)Key.get(type, a)).orElse((Key<Object>)Key.get(type));
|
||||
binder.withSource(SPRING_GUICE_SOURCE).bind(key)
|
||||
.toProvider(typeProvider);
|
||||
this.bound.put(stageTypeKey, typeProvider);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package org.springframework.guice;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Qualifier;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.guice.BindingAnnotationTests.SomeDependencyWithBindingAnnotationOnProvider;
|
||||
import org.springframework.guice.BindingAnnotationTests.SomeDependencyWithGuiceNamedAnnotationOnProvider;
|
||||
import org.springframework.guice.BindingAnnotationTests.SomeDependencyWithNamedAnnotationOnProvider;
|
||||
import org.springframework.guice.BindingAnnotationTests.SomeDependencyWithQualifierOnProvider;
|
||||
import org.springframework.guice.BindingAnnotationTests.SomeDependencyWithQualifierOnProviderWhichImplementsSomeInterface;
|
||||
import org.springframework.guice.BindingAnnotationTests.SomeInterface;
|
||||
import org.springframework.guice.annotation.EnableGuiceModules;
|
||||
|
||||
import com.google.inject.BindingAnnotation;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
public class BindingAnnotationTests {
|
||||
|
||||
@Test
|
||||
public void verifyBindingAnnotationsAreRespected() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
BindingAnnotationTestsConfig.class);
|
||||
Injector injector = context.getBean(Injector.class);
|
||||
|
||||
//Check @Qualifier
|
||||
SomeDependencyWithQualifierOnProvider someDependencyWithQualifierOnClass = injector.getInstance(Key.get(SomeDependencyWithQualifierOnProvider.class, SomeQualifierAnnotation.class));
|
||||
assertNotNull(someDependencyWithQualifierOnClass);
|
||||
|
||||
//Check @BindingAnnotaiton
|
||||
SomeDependencyWithQualifierOnProvider someDependencyWithBindingAnnotationOnProvider = injector.getInstance(Key.get(SomeDependencyWithQualifierOnProvider.class, SomeQualifierAnnotation.class));
|
||||
assertNotNull(someDependencyWithBindingAnnotationOnProvider);
|
||||
|
||||
//Check javax @Named
|
||||
SomeDependencyWithNamedAnnotationOnProvider someDependencyWithNamedAnnotationOnProvider = injector.getInstance(Key.get(SomeDependencyWithNamedAnnotationOnProvider.class, Names.named("javaxNamed")));
|
||||
assertNotNull(someDependencyWithNamedAnnotationOnProvider);
|
||||
|
||||
//Check Guice @Named
|
||||
SomeDependencyWithGuiceNamedAnnotationOnProvider someDependencyWithGuiceNamedAnnotationOnProvider = injector.getInstance(Key.get(SomeDependencyWithGuiceNamedAnnotationOnProvider.class, Names.named("guiceNamed")));
|
||||
assertNotNull(someDependencyWithGuiceNamedAnnotationOnProvider);
|
||||
|
||||
//Check @Qualifier with Interface
|
||||
SomeInterface someInterface = injector.getInstance(Key.get(SomeInterface.class, SomeQualifierAnnotation.class));
|
||||
assertNotNull(someInterface);
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
|
||||
public static class SomeDependencyWithQualifierOnProvider {}
|
||||
public static class SomeDependencyWithBindingAnnotationOnProvider {}
|
||||
public static class SomeDependencyWithNamedAnnotationOnProvider {}
|
||||
public static class SomeDependencyWithGuiceNamedAnnotationOnProvider {}
|
||||
public static interface SomeInterface{}
|
||||
public static class SomeDependencyWithQualifierOnProviderWhichImplementsSomeInterface implements SomeInterface {}
|
||||
}
|
||||
|
||||
@Qualifier
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface SomeQualifierAnnotation {}
|
||||
|
||||
@BindingAnnotation
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface SomeBindingAnnotation {}
|
||||
|
||||
|
||||
@EnableGuiceModules
|
||||
@Configuration
|
||||
class BindingAnnotationTestsConfig {
|
||||
|
||||
@Bean
|
||||
@SomeQualifierAnnotation
|
||||
public SomeDependencyWithQualifierOnProvider someDependencyWithQualifierOnProvider() {
|
||||
return new SomeDependencyWithQualifierOnProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SomeBindingAnnotation
|
||||
public SomeDependencyWithBindingAnnotationOnProvider someDependencyWithBindingAnnotationOnProvider() {
|
||||
return new SomeDependencyWithBindingAnnotationOnProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Named("javaxNamed")
|
||||
public SomeDependencyWithNamedAnnotationOnProvider someDependencyWithNamedAnnotationOnProvider() {
|
||||
return new SomeDependencyWithNamedAnnotationOnProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@com.google.inject.name.Named("guiceNamed")
|
||||
public SomeDependencyWithGuiceNamedAnnotationOnProvider someDependencyWithGuiceNamedAnnotationOnProvider() {
|
||||
return new SomeDependencyWithGuiceNamedAnnotationOnProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SomeQualifierAnnotation
|
||||
public SomeInterface someInterface() {
|
||||
return new SomeDependencyWithQualifierOnProviderWhichImplementsSomeInterface();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user