support binding of generic interfaces from SpringModule

This commit is contained in:
Taylor Wicksell
2017-05-24 14:51:58 -07:00
committed by Dave Syer
parent 9dae979d39
commit 061aef36f2
9 changed files with 68 additions and 31 deletions

View File

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

View File

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

View File

@@ -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;