From 0277ce7ab256a77afc8ffc6d47a92cb4fd53436a Mon Sep 17 00:00:00 2001 From: Sergey Shcherbakov Date: Wed, 7 Aug 2013 17:41:59 +0200 Subject: [PATCH] Added wildcard and property placeholder support in SpringApplication * When a config source is a String it can now be a pattern * Default resource loaded in the BeanDefinitionLoader has been changed to PathMatchingResourcePatternResolver; * A check for the ResourcePatternLoader similar to that in AbstractBeanDefinitionReader and property placeholder resolution has been added to the load(CharSequence) method of the BeanDefinitionLoader; * Added a unit test illustrating the issue; --- .../boot/BeanDefinitionLoader.java | 49 +++++++++++++++---- .../boot/SpringApplicationTests.java | 8 +++ .../src/test/resources/application.properties | 3 +- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/BeanDefinitionLoader.java b/spring-boot/src/main/java/org/springframework/boot/BeanDefinitionLoader.java index f0abef730d7..119cdff6fdf 100644 --- a/spring-boot/src/main/java/org/springframework/boot/BeanDefinitionLoader.java +++ b/spring-boot/src/main/java/org/springframework/boot/BeanDefinitionLoader.java @@ -16,9 +16,11 @@ package org.springframework.boot; +import java.io.IOException; import java.util.HashSet; import java.util.Set; +import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; @@ -26,7 +28,6 @@ import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; @@ -49,7 +50,7 @@ import org.springframework.util.StringUtils; */ class BeanDefinitionLoader { - private static final ResourceLoader DEFAULT_RESOURCE_LOADER = new DefaultResourceLoader(); + private static final ResourceLoader DEFAULT_RESOURCE_LOADER = new PathMatchingResourcePatternResolver(); private Object[] sources; @@ -153,22 +154,50 @@ class BeanDefinitionLoader { } private int load(CharSequence source) { + String sourceString = xmlReader.getEnvironment().resolvePlaceholders(source.toString()); try { // Use class utils so that period separated nested class names work - return load(ClassUtils.forName(source.toString(), null)); + return load(ClassUtils.forName(sourceString, null)); } catch (ClassNotFoundException ex) { // swallow exception and continue } - Resource loadedResource = (this.resourceLoader != null ? this.resourceLoader - : DEFAULT_RESOURCE_LOADER).getResource(source.toString()); - if (loadedResource != null && loadedResource.exists()) { - return load(loadedResource); + ResourceLoader loader = this.resourceLoader != null ? + this.resourceLoader : DEFAULT_RESOURCE_LOADER; + + int loadCount = 0; + if( loader instanceof ResourcePatternResolver ) { + // Resource pattern matching available. + try { + Resource[] resources = ((ResourcePatternResolver) loader).getResources(sourceString); + for(Resource resource : resources) { + if( resource.exists() ) { + loadCount += load(resource); + } + } + } + catch (IOException ex) { + throw new BeanDefinitionStoreException( + "Could not resolve bean definition resource pattern [" + sourceString + "]", ex); + } } - Package packageResource = findPackage(source); - if (packageResource != null) { - return load(packageResource); + if( !(loader instanceof ResourcePatternResolver) ) { + // Can only load single resources by absolute URL. + Resource loadedResource = loader.getResource(sourceString); + if (loadedResource != null && loadedResource.exists()) { + return load(loadedResource); + } + } + if( loadCount > 0 ) { + return loadCount; + } + else { + // Attempt to treat the source as a package name, common to all PatternResolver types + Package packageResource = findPackage(source); + if (packageResource != null) { + return load(packageResource); + } } throw new IllegalArgumentException("Invalid source '" + source + "'"); } diff --git a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index 0951d1f32c5..9ced5a6a846 100644 --- a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -275,6 +275,14 @@ public class SpringApplicationTests { application, "initialSources"); assertThat(initialSources.toArray(), equalTo(sources)); } + + @Test + public void wildcardSources() { + Object[] sources = { "classpath:org/springframework/boot/sample-${sample.app.test.prop}.xml" }; + TestSpringApplication application = new TestSpringApplication(sources); + application.setWebEnvironment(false); + application.run(); + } @Test public void run() throws Exception { diff --git a/spring-boot/src/test/resources/application.properties b/spring-boot/src/test/resources/application.properties index 3fa3f5bae02..497e65a06b3 100644 --- a/spring-boot/src/test/resources/application.properties +++ b/spring-boot/src/test/resources/application.properties @@ -1 +1,2 @@ -foo: bucket \ No newline at end of file +foo: bucket +sample.app.test.prop: * \ No newline at end of file