diff --git a/src/main/java/org/springframework/guice/BindingTypeMatcher.java b/src/main/java/org/springframework/guice/BindingTypeMatcher.java index a088e10..4006e5f 100644 --- a/src/main/java/org/springframework/guice/BindingTypeMatcher.java +++ b/src/main/java/org/springframework/guice/BindingTypeMatcher.java @@ -22,6 +22,6 @@ package org.springframework.guice; */ public interface BindingTypeMatcher { - boolean matches(Class type); + boolean matches(String name, Class type); } diff --git a/src/main/java/org/springframework/guice/GuiceModule.java b/src/main/java/org/springframework/guice/GuiceModule.java index c92d5dd..cfcdf55 100644 --- a/src/main/java/org/springframework/guice/GuiceModule.java +++ b/src/main/java/org/springframework/guice/GuiceModule.java @@ -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 {}; + } diff --git a/src/main/java/org/springframework/guice/GuiceModuleMetadata.java b/src/main/java/org/springframework/guice/GuiceModuleMetadata.java index c1f5a0c..c794f49 100644 --- a/src/main/java/org/springframework/guice/GuiceModuleMetadata.java +++ b/src/main/java/org/springframework/guice/GuiceModuleMetadata.java @@ -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 @Bean of this type. Can be used directly as a - * @Bean, but it is easier to just add @{@link GuiceModule} to - * your @Configuration. + * Encapsulates some metadata about a Guice module that is to be created from the parent context of a @Bean + * of this type. Can be used directly as a @Bean, but it is easier to just add @ + * {@link GuiceModule} to your @Configuration. * * @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> infrastructureTypes = new HashSet>(); { @@ -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; } diff --git a/src/main/java/org/springframework/guice/GuiceModuleRegistrar.java b/src/main/java/org/springframework/guice/GuiceModuleRegistrar.java index 9ad1ec7..b877b83 100644 --- a/src/main/java/org/springframework/guice/GuiceModuleRegistrar.java +++ b/src/main/java/org/springframework/guice/GuiceModuleRegistrar.java @@ -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 excludeFilters; + private Collection includePatterns; + + private Collection excludePatterns; + + private Collection includeNames; + + private Collection excludeNames; + public void setIncludeFilters(Collection includeFilters) { this.includeFilters = includeFilters; } @@ -88,13 +104,37 @@ public class GuiceModuleRegistrar implements ImportBeanDefinitionRegistrar, this.excludeFilters = excludeFilters; } + public void setIncludePatterns(Collection includePatterns) { + this.includePatterns = includePatterns; + } + + public void setExcludePatterns(Collection excludePatterns) { + this.excludePatterns = excludePatterns; + } + + public void setIncludeNames(Collection includeNames) { + this.includeNames = includeNames; + } + + public void setExcludeNames(Collection 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 parsePatterns(AnnotationMetadata annotation, String attributeName) { + Set result = new HashSet(); + 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 parseNames(AnnotationMetadata annotation, String attributeName) { + Set result = new HashSet(); + 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 parseFilters(AnnotationMetadata annotation, String attributeName) { diff --git a/src/main/java/org/springframework/guice/SpringModule.java b/src/main/java/org/springframework/guice/SpringModule.java index 2f5632d..f568c1e 100644 --- a/src/main/java/org/springframework/guice/SpringModule.java +++ b/src/main/java/org/springframework/guice/SpringModule.java @@ -63,23 +63,23 @@ public class SpringModule implements Module { final String beanName = name; Provider 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 unchecked = (Class) iface; - bindConditionally(binder, unchecked, provider); + bindConditionally(binder, name, unchecked, provider); } } } } - private void bindConditionally(Binder binder, Class type, Provider provider) { + private void bindConditionally(Binder binder, String name, Class type, Provider 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; } } diff --git a/src/test/java/org/springframework/guice/GuiceModuleAnnotationTests.java b/src/test/java/org/springframework/guice/GuiceModuleAnnotationTests.java index bf327bb..47abb8f 100644 --- a/src/test/java/org/springframework/guice/GuiceModuleAnnotationTests.java +++ b/src/test/java/org/springframework/guice/GuiceModuleAnnotationTests.java @@ -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 {