support binding of generic interfaces from SpringModule
This commit is contained in:
committed by
Dave Syer
parent
9dae979d39
commit
061aef36f2
@@ -16,12 +16,14 @@
|
||||
|
||||
package org.springframework.guice.module;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface BindingTypeMatcher {
|
||||
|
||||
boolean matches(String name, Class<?> type);
|
||||
boolean matches(String name, Type type);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,14 @@ package org.springframework.guice.module;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
@@ -93,7 +95,7 @@ public class GuiceModuleMetadata implements BindingTypeMatcher {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String name, Class<?> type) {
|
||||
public boolean matches(String name, Type type) {
|
||||
if (!matches(name) || !matches(type)) {
|
||||
return false;
|
||||
}
|
||||
@@ -128,7 +130,7 @@ public class GuiceModuleMetadata implements BindingTypeMatcher {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean matches(Class<?> type) {
|
||||
private boolean matches(Type type) {
|
||||
if (infrastructureTypes.contains(type)) {
|
||||
return false;
|
||||
}
|
||||
@@ -140,7 +142,7 @@ public class GuiceModuleMetadata implements BindingTypeMatcher {
|
||||
if (includeFilters != null) {
|
||||
try {
|
||||
MetadataReader reader = metadataReaderFactory.getMetadataReader(type
|
||||
.getName());
|
||||
.getTypeName());
|
||||
for (TypeFilter filter : includeFilters) {
|
||||
if (!filter.match(reader, metadataReaderFactory)) {
|
||||
return false;
|
||||
@@ -154,7 +156,7 @@ public class GuiceModuleMetadata implements BindingTypeMatcher {
|
||||
if (excludeFilters != null) {
|
||||
try {
|
||||
MetadataReader reader = metadataReaderFactory.getMetadataReader(type
|
||||
.getName());
|
||||
.getTypeName());
|
||||
for (TypeFilter filter : excludeFilters) {
|
||||
if (filter.match(reader, metadataReaderFactory)) {
|
||||
return false;
|
||||
@@ -168,8 +170,8 @@ public class GuiceModuleMetadata implements BindingTypeMatcher {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean visible(Class<?> type) {
|
||||
Class<?> cls = type;
|
||||
private boolean visible(Type type) {
|
||||
Class<?> cls = ResolvableType.forType(type).resolve();
|
||||
while (cls != null && cls != Object.class) {
|
||||
if (!Modifier.isInterface(cls.getModifiers())
|
||||
&& !Modifier.isPublic(cls.getModifiers())
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
package org.springframework.guice.module;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -28,8 +29,10 @@ import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
/**
|
||||
@@ -42,7 +45,7 @@ public class SpringModule implements Module {
|
||||
|
||||
private BindingTypeMatcher matcher = new GuiceModuleMetadata();
|
||||
|
||||
private Map<Class<?>, Provider<?>> bound = new HashMap<Class<?>, Provider<?>>();
|
||||
private Map<Type, Provider<?>> bound = new HashMap<Type, Provider<?>>();
|
||||
|
||||
public SpringModule(ApplicationContext context) {
|
||||
this((DefaultListableBeanFactory) context.getAutowireCapableBeanFactory());
|
||||
@@ -55,64 +58,65 @@ public class SpringModule implements Module {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void configure(Binder binder) {
|
||||
for (String name : this.beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition definition = this.beanFactory.getBeanDefinition(name);
|
||||
if (definition.isAutowireCandidate() && definition.getRole() == AbstractBeanDefinition.ROLE_APPLICATION) {
|
||||
Class<?> type = this.beanFactory.getType(name);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<Object> cls = (Class<Object>) type;
|
||||
final String beanName = name;
|
||||
Provider<Object> typeProvider = new BeanFactoryProvider(this.beanFactory, null, type);
|
||||
Provider<Object> namedProvider = new BeanFactoryProvider(this.beanFactory, beanName, type);
|
||||
if (!cls.isInterface() && !ClassUtils.isCglibProxyClass(cls)) {
|
||||
bindConditionally(binder, name, cls, typeProvider, namedProvider);
|
||||
if (!type.isInterface() && !ClassUtils.isCglibProxyClass(type)) {
|
||||
bindConditionally(binder, name, type, typeProvider, namedProvider);
|
||||
}
|
||||
for (Class<?> iface : ClassUtils.getAllInterfacesForClass(cls)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<Object> unchecked = (Class<Object>) iface;
|
||||
bindConditionally(binder, name, unchecked, typeProvider, namedProvider);
|
||||
for (Class<?> iface : ClassUtils.getAllInterfacesForClass(type)) {
|
||||
bindConditionally(binder, name, iface, typeProvider, namedProvider);
|
||||
}
|
||||
for (Type iface : type.getGenericInterfaces()) {
|
||||
bindConditionally(binder, name, iface, typeProvider, namedProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bindConditionally(Binder binder, String name, Class<Object> type, Provider<Object> typeProvider,
|
||||
Provider<Object> namedProvider) {
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private <T> void bindConditionally(Binder binder, String name, Type type, Provider typeProvider,
|
||||
Provider namedProvider) {
|
||||
if (!this.matcher.matches(name, type)) {
|
||||
return;
|
||||
}
|
||||
if (type.getName().startsWith("com.google.inject")) {
|
||||
if (type.getTypeName().startsWith("com.google.inject")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.bound.get(type) == null) {
|
||||
// Only bind one provider for each type
|
||||
binder.withSource("spring-guice").bind(type).toProvider(typeProvider);
|
||||
this.bound.put(type, typeProvider);
|
||||
binder.withSource("spring-guice").bind(Key.get(type)).toProvider(typeProvider);
|
||||
this.bound.put(type, typeProvider);
|
||||
}
|
||||
// But allow binding to named beans
|
||||
binder.withSource("spring-guice").bind(type).annotatedWith(Names.named(name)).toProvider(namedProvider);
|
||||
binder.withSource("spring-guice").bind(TypeLiteral.get(type)).annotatedWith(Names.named(name)).toProvider(namedProvider);
|
||||
}
|
||||
|
||||
private static class BeanFactoryProvider implements Provider<Object> {
|
||||
private static class BeanFactoryProvider<T> implements Provider<T> {
|
||||
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
private String name;
|
||||
|
||||
private Class<?> type;
|
||||
private Class<T> type;
|
||||
|
||||
private Object result;
|
||||
private T result;
|
||||
|
||||
public BeanFactoryProvider(DefaultListableBeanFactory beanFactory, String name, Class<?> type) {
|
||||
public BeanFactoryProvider(DefaultListableBeanFactory beanFactory, String name, Class<T> type) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get() {
|
||||
public T get() {
|
||||
if (this.result == null) {
|
||||
|
||||
String[] named = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, this.type);
|
||||
@@ -151,7 +155,7 @@ public class SpringModule implements Module {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String name, Class<?> type) {
|
||||
public boolean matches(String name, Type type) {
|
||||
for (BindingTypeMatcher matcher : this.matchers) {
|
||||
if (matcher.matches(name, type)) {
|
||||
return true;
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.junit.Test;
|
||||
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
public abstract class AbstractCompleteWiringTests {
|
||||
@@ -74,6 +75,12 @@ public abstract class AbstractCompleteWiringTests {
|
||||
public void getNamedInjectedInstance() {
|
||||
assertNotNull(this.injector.getInstance(Thing.class).thang);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParameterizedType() {
|
||||
Parameterized<String> instance = this.injector.getInstance(Key.get(new TypeLiteral<Parameterized<String>>() {}));
|
||||
assertNotNull(instance);
|
||||
}
|
||||
|
||||
public interface Service {
|
||||
}
|
||||
@@ -121,4 +128,7 @@ public abstract class AbstractCompleteWiringTests {
|
||||
|
||||
public static class Thang {
|
||||
}
|
||||
|
||||
public static interface Parameterized<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import javax.inject.Singleton;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
/**
|
||||
@@ -38,6 +39,7 @@ public class GuiceWiringTests extends AbstractCompleteWiringTests {
|
||||
bind(Service.class).to(MyService.class);
|
||||
bind(Baz.class).in(Singleton.class);
|
||||
bind(Thang.class).annotatedWith(Names.named("thing")).to(Thang.class);
|
||||
bind(new TypeLiteral<Parameterized<String>>(){}).toInstance(new Parameterized<String>(){});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,11 @@ public class ModuleBeanWiringTests extends AbstractCompleteWiringTests {
|
||||
public Baz baz(Service service) {
|
||||
return new Baz(service);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Parameterized<String> parameterizedBean() {
|
||||
return new Parameterized<String>() {};
|
||||
}
|
||||
}
|
||||
|
||||
protected static class Spam {
|
||||
|
||||
@@ -86,6 +86,11 @@ public class ModuleNamedBeanWiringTests extends AbstractCompleteWiringTests {
|
||||
public Baz baz(Service service) {
|
||||
return new Baz(service);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Parameterized<String> parameterizedBean() {
|
||||
return new Parameterized<String>() {};
|
||||
}
|
||||
}
|
||||
|
||||
protected static class Spam {
|
||||
|
||||
@@ -48,6 +48,10 @@ public class SpringWiringTests extends AbstractCompleteWiringTests {
|
||||
public Thang other() {
|
||||
return new Thang();
|
||||
}
|
||||
@Bean
|
||||
public Parameterized<String> parameterizedBean() {
|
||||
return new Parameterized<String>() {};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,7 +61,10 @@ public class SpringModuleWiringTests extends AbstractCompleteWiringTests {
|
||||
public Thing that() {
|
||||
return new Thing();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public Parameterized<String> parameterizedBean() {
|
||||
return new Parameterized<String>(){};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user