Add bean name filters to GuiceModule

This commit is contained in:
Dave Syer
2014-04-17 11:32:14 -07:00
parent 81ac3999f9
commit 16172b9f6e
6 changed files with 198 additions and 25 deletions

View File

@@ -22,6 +22,6 @@ package org.springframework.guice;
*/
public interface BindingTypeMatcher {
boolean matches(Class<?> type);
boolean matches(String name, Class<?> type);
}

View File

@@ -52,4 +52,24 @@ public @interface GuiceModule {
*/
Filter[] excludeFilters() default {};
/**
* Specifies which names (by regex) are eligible for inclusion in Guice module
*/
String[] includePatterns() default {};
/**
* Specifies which bean names (by regex) are not eligible for inclusion in Guice module.
*/
String[] excludePatterns() default {};
/**
* Specifies which names (by simple wildcard match) are eligible for inclusion in Guice module
*/
String[] includeNames() default {};
/**
* Specifies which bean names (by simple wildcard match) are not eligible for inclusion in Guice module.
*/
String[] excludeNames() default {};
}

View File

@@ -20,6 +20,7 @@ import java.io.IOException;
import java.lang.reflect.Modifier;
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;
@@ -27,12 +28,12 @@ import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.PatternMatchUtils;
/**
* Encapsulates some metadata about a Guice module that is to be created from the parent
* context of a <code>@Bean</code> of this type. Can be used directly as a
* <code>@Bean</code>, but it is easier to just add <code>@</code>{@link GuiceModule} to
* your <code>@Configuration</code>.
* Encapsulates some metadata about a Guice module that is to be created from the parent context of a <code>@Bean</code>
* of this type. Can be used directly as a <code>@Bean</code>, but it is easier to just add <code>@</code>
* {@link GuiceModule} to your <code>@Configuration</code>.
*
* @author Dave Syer
*
@@ -43,6 +44,14 @@ public class GuiceModuleMetadata implements BindingTypeMatcher {
private TypeFilter[] excludeFilters;
private Pattern[] includePatterns;
private Pattern[] excludePatterns;
private String[] includeNames;
private String[] excludeNames;
private Set<Class<?>> infrastructureTypes = new HashSet<Class<?>>();
{
@@ -52,6 +61,26 @@ public class GuiceModuleMetadata implements BindingTypeMatcher {
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
public GuiceModuleMetadata include(String... filters) {
includeNames = filters;
return this;
}
public GuiceModuleMetadata exclude(String... filters) {
excludeNames = filters;
return this;
}
public GuiceModuleMetadata include(Pattern... filters) {
includePatterns = filters;
return this;
}
public GuiceModuleMetadata exclude(Pattern... filters) {
excludePatterns = filters;
return this;
}
public GuiceModuleMetadata include(TypeFilter... filters) {
includeFilters = filters;
return this;
@@ -63,8 +92,42 @@ public class GuiceModuleMetadata implements BindingTypeMatcher {
}
@Override
public boolean matches(Class<?> type) {
public boolean matches(String name, Class<?> type) {
if (!matches(name) || !matches(type)) {
return false;
}
return true;
}
private boolean matches(String name) {
if (includePatterns != null) {
for (Pattern filter : includePatterns) {
if (!filter.matcher(name).matches()) {
return false;
}
}
}
if (excludePatterns != null) {
for (Pattern filter : excludePatterns) {
if (filter.matcher(name).matches()) {
return false;
}
}
}
if (includeNames != null && includeNames.length>0) {
if (!PatternMatchUtils.simpleMatch(includeNames, name)) {
return false;
}
}
if (excludeNames != null && excludeNames.length>0) {
if (PatternMatchUtils.simpleMatch(excludeNames, name)) {
return false;
}
}
return true;
}
private boolean matches(Class<?> type) {
if (infrastructureTypes.contains(type)) {
return false;
}
@@ -75,30 +138,28 @@ public class GuiceModuleMetadata implements BindingTypeMatcher {
if (includeFilters != null) {
try {
MetadataReader reader = metadataReaderFactory.getMetadataReader(type
.getName());
MetadataReader reader = metadataReaderFactory.getMetadataReader(type.getName());
for (TypeFilter filter : includeFilters) {
if (!filter.match(reader, metadataReaderFactory)) {
return false;
}
}
} catch (IOException e) {
throw new IllegalStateException("Cannot read metadata for class " + type,
e);
}
catch (IOException e) {
throw new IllegalStateException("Cannot read metadata for class " + type, e);
}
}
if (excludeFilters != null) {
try {
MetadataReader reader = metadataReaderFactory.getMetadataReader(type
.getName());
MetadataReader reader = metadataReaderFactory.getMetadataReader(type.getName());
for (TypeFilter filter : excludeFilters) {
if (filter.match(reader, metadataReaderFactory)) {
return false;
}
}
} catch (IOException e) {
throw new IllegalStateException("Cannot read metadata for class " + type,
e);
}
catch (IOException e) {
throw new IllegalStateException("Cannot read metadata for class " + type, e);
}
}
return true;
@@ -107,8 +168,7 @@ public class GuiceModuleMetadata implements BindingTypeMatcher {
private boolean visible(Class<?> type) {
Class<?> cls = type;
while (cls != null && cls != Object.class) {
if (!Modifier.isInterface(cls.getModifiers())
&& !Modifier.isPublic(cls.getModifiers())
if (!Modifier.isInterface(cls.getModifiers()) && !Modifier.isPublic(cls.getModifiers())
&& !Modifier.isProtected(cls.getModifiers())) {
return false;
}

View File

@@ -67,6 +67,14 @@ public class GuiceModuleRegistrar implements ImportBeanDefinitionRegistrar,
parseFilters(annotation, "includeFilters"));
builder.addPropertyValue("excludeFilters",
parseFilters(annotation, "excludeFilters"));
builder.addPropertyValue("includePatterns",
parsePatterns(annotation, "includePatterns"));
builder.addPropertyValue("excludePatterns",
parsePatterns(annotation, "excludePatterns"));
builder.addPropertyValue("includeNames",
parseNames(annotation, "includeNames"));
builder.addPropertyValue("excludeNames",
parseNames(annotation, "excludeNames"));
AbstractBeanDefinition definition = builder.getBeanDefinition();
String name = new DefaultBeanNameGenerator().generateBeanName(definition,
registry);
@@ -80,6 +88,14 @@ public class GuiceModuleRegistrar implements ImportBeanDefinitionRegistrar,
private Collection<? extends TypeFilter> excludeFilters;
private Collection<Pattern> includePatterns;
private Collection<Pattern> excludePatterns;
private Collection<String> includeNames;
private Collection<String> excludeNames;
public void setIncludeFilters(Collection<? extends TypeFilter> includeFilters) {
this.includeFilters = includeFilters;
}
@@ -88,13 +104,37 @@ public class GuiceModuleRegistrar implements ImportBeanDefinitionRegistrar,
this.excludeFilters = excludeFilters;
}
public void setIncludePatterns(Collection<Pattern> includePatterns) {
this.includePatterns = includePatterns;
}
public void setExcludePatterns(Collection<Pattern> excludePatterns) {
this.excludePatterns = excludePatterns;
}
public void setIncludeNames(Collection<String> includeNames) {
this.includeNames = includeNames;
}
public void setExcludeNames(Collection<String> excludeNames) {
this.excludeNames = excludeNames;
}
@Override
public GuiceModuleMetadata getObject() throws Exception {
return new GuiceModuleMetadata()
.include(
includeFilters.toArray(new TypeFilter[includeFilters.size()]))
.exclude(
excludeFilters.toArray(new TypeFilter[excludeFilters.size()]));
excludeFilters.toArray(new TypeFilter[excludeFilters.size()]))
.include(
includePatterns.toArray(new Pattern[includePatterns.size()]))
.exclude(
excludePatterns.toArray(new Pattern[excludePatterns.size()]))
.include(
includeNames.toArray(new String[includeNames.size()]))
.exclude(
excludeNames.toArray(new Pattern[excludeNames.size()]));
}
@Override
@@ -109,6 +149,32 @@ public class GuiceModuleRegistrar implements ImportBeanDefinitionRegistrar,
}
private Set<Pattern> parsePatterns(AnnotationMetadata annotation, String attributeName) {
Set<Pattern> result = new HashSet<Pattern>();
AnnotationAttributes attributes = new AnnotationAttributes(
annotation.getAnnotationAttributes(GuiceModule.class.getName()));
String[] filters = attributes.getStringArray(attributeName);
for (String filter : filters) {
result.add(Pattern.compile(filter));
}
return result;
}
private Set<String> parseNames(AnnotationMetadata annotation, String attributeName) {
Set<String> result = new HashSet<String>();
AnnotationAttributes attributes = new AnnotationAttributes(
annotation.getAnnotationAttributes(GuiceModule.class.getName()));
String[] filters = attributes.getStringArray(attributeName);
for (String filter : filters) {
result.add(filter);
}
return result;
}
private Set<TypeFilter> parseFilters(AnnotationMetadata annotation,
String attributeName) {

View File

@@ -63,23 +63,23 @@ public class SpringModule implements Module {
final String beanName = name;
Provider<Object> provider = new BeanFactoryProvider(beanFactory, beanName, type);
if (!cls.isInterface() && !ClassUtils.isCglibProxyClass(cls)) {
bindConditionally(binder, cls, provider);
bindConditionally(binder, name, cls, provider);
}
for (Class<?> iface : ClassUtils.getAllInterfacesForClass(cls)) {
@SuppressWarnings("unchecked")
Class<Object> unchecked = (Class<Object>) iface;
bindConditionally(binder, unchecked, provider);
bindConditionally(binder, name, unchecked, provider);
}
}
}
}
private void bindConditionally(Binder binder, Class<Object> type, Provider<Object> provider) {
private void bindConditionally(Binder binder, String name, Class<Object> type, Provider<Object> provider) {
if (bound.get(type) != null) {
// Only bind one provider for each type
return; // TODO: named beans
}
if (!matcher.matches(type)) {
if (!matcher.matches(name, type)) {
return;
}
if (type.getName().startsWith("com.google.inject")) {
@@ -136,9 +136,9 @@ public class SpringModule implements Module {
}
@Override
public boolean matches(Class<?> type) {
public boolean matches(String name, Class<?> type) {
for (BindingTypeMatcher matcher : matchers) {
if (matcher.matches(type)) {
if (matcher.matches(name, type)) {
return true;
}
}

View File

@@ -45,6 +45,18 @@ public class GuiceModuleAnnotationTests {
assertNotNull(injector.getBinding(Service.class));
}
@Test
public void includesNames() throws Exception {
Injector injector = createInjector(TestConfig.class, MetadataIncludeNamesConfig.class);
assertNotNull(injector.getBinding(Service.class));
}
@Test
public void includesPatterns() throws Exception {
Injector injector = createInjector(TestConfig.class, MetadataIncludePatternsConfig.class);
assertNotNull(injector.getBinding(Service.class));
}
@Test
public void excludes() throws Exception {
Injector injector = createInjector(TestConfig.class, MetadataExcludesConfig.class);
@@ -85,11 +97,26 @@ public class GuiceModuleAnnotationTests {
protected static class MetadataExcludesConfig {
}
@Configuration
@GuiceModule(excludePatterns=".*")
protected static class MetadataExcludesNameConfig {
}
@Configuration
@GuiceModule(includeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=Service.class))
protected static class MetadataIncludesConfig {
}
@Configuration
@GuiceModule(includeNames="*service") // Bean name filter
protected static class MetadataIncludeNamesConfig {
}
@Configuration
@GuiceModule(includePatterns=".*service") // Bean name filter
protected static class MetadataIncludePatternsConfig {
}
@Configuration
@GuiceModule(includeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class))
protected static class MetadataMoreIncludesConfig {