diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolver.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolver.java index 64e1ea1b4f2..cbba9e9870a 100644 --- a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolver.java +++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.boot.devtools.restart; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -40,6 +41,7 @@ import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.util.AntPathMatcher; import org.springframework.util.ClassUtils; import org.springframework.util.PathMatcher; +import org.springframework.util.ReflectionUtils; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.ServletContextResource; import org.springframework.web.context.support.ServletContextResourcePatternResolver; @@ -59,7 +61,7 @@ final class ClassLoaderFilesResourcePatternResolver implements ResourcePatternRe private static final String WEB_CONTEXT_CLASS = "org.springframework.web.context." + "WebApplicationContext"; - private final ResourcePatternResolver delegate; + private final ResourcePatternResolver patternResolverDelegate; private final PathMatcher antPathMatcher = new AntPathMatcher(); @@ -68,8 +70,19 @@ final class ClassLoaderFilesResourcePatternResolver implements ResourcePatternRe ClassLoaderFilesResourcePatternResolver(ApplicationContext applicationContext, ClassLoaderFiles classLoaderFiles) { this.classLoaderFiles = classLoaderFiles; - this.delegate = getResourcePatternResolverFactory() - .getResourcePatternResolver(applicationContext); + this.patternResolverDelegate = getResourcePatternResolverFactory() + .getResourcePatternResolver(applicationContext, + retrieveResourceLoader(applicationContext)); + } + + private ResourceLoader retrieveResourceLoader(ApplicationContext applicationContext) { + Field field = ReflectionUtils.findField(applicationContext.getClass(), + "resourceLoader", ResourceLoader.class); + if (field == null) { + return null; + } + ReflectionUtils.makeAccessible(field); + return (ResourceLoader) ReflectionUtils.getField(field, applicationContext); } private ResourcePatternResolverFactory getResourcePatternResolverFactory() { @@ -81,12 +94,12 @@ final class ClassLoaderFilesResourcePatternResolver implements ResourcePatternRe @Override public ClassLoader getClassLoader() { - return this.delegate.getClassLoader(); + return this.patternResolverDelegate.getClassLoader(); } @Override public Resource getResource(String location) { - Resource candidate = this.delegate.getResource(location); + Resource candidate = this.patternResolverDelegate.getResource(location); if (isDeleted(candidate)) { return new DeletedClassLoaderFileResource(location); } @@ -96,7 +109,8 @@ final class ClassLoaderFilesResourcePatternResolver implements ResourcePatternRe @Override public Resource[] getResources(String locationPattern) throws IOException { List resources = new ArrayList(); - Resource[] candidates = this.delegate.getResources(locationPattern); + Resource[] candidates = this.patternResolverDelegate + .getResources(locationPattern); for (Resource candidate : candidates) { if (!isDeleted(candidate)) { resources.add(candidate); @@ -190,8 +204,9 @@ final class ClassLoaderFilesResourcePatternResolver implements ResourcePatternRe private static class ResourcePatternResolverFactory { public ResourcePatternResolver getResourcePatternResolver( - ApplicationContext applicationContext) { - return new PathMatchingResourcePatternResolver(); + ApplicationContext applicationContext, ResourceLoader resourceLoader) { + return new PathMatchingResourcePatternResolver(resourceLoader == null + ? new DefaultResourceLoader() : resourceLoader); } } @@ -205,13 +220,14 @@ final class ClassLoaderFilesResourcePatternResolver implements ResourcePatternRe @Override public ResourcePatternResolver getResourcePatternResolver( - ApplicationContext applicationContext) { + ApplicationContext applicationContext, ResourceLoader resourceLoader) { if (applicationContext instanceof WebApplicationContext) { - return new ServletContextResourcePatternResolver( - new WebApplicationContextResourceLoader( - (WebApplicationContext) applicationContext)); + return new ServletContextResourcePatternResolver(resourceLoader == null + ? new WebApplicationContextResourceLoader( + (WebApplicationContext) applicationContext) + : resourceLoader); } - return super.getResourcePatternResolver(applicationContext); + return super.getResourcePatternResolver(applicationContext, resourceLoader); } } diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolverTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolverTests.java index 298079f485b..789920b94d2 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolverTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,17 +31,21 @@ import org.springframework.boot.devtools.restart.classloader.ClassLoaderFiles; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; import org.springframework.mock.web.MockServletContext; import org.springframework.util.FileCopyUtils; import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.context.support.ServletContextResource; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * Tests for {@link ClassLoaderFilesResourcePatternResolver}. * * @author Phillip Webb + * @author Andy Wilkinson */ public class ClassLoaderFilesResourcePatternResolverTests { @@ -111,6 +115,27 @@ public class ClassLoaderFilesResourcePatternResolverTests { assertThat(resources).isEmpty(); } + @Test + public void customResourceLoaderIsUsedInNonWebApplication() throws Exception { + GenericApplicationContext context = new GenericApplicationContext(); + ResourceLoader resourceLoader = mock(ResourceLoader.class); + context.setResourceLoader(resourceLoader); + this.resolver = new ClassLoaderFilesResourcePatternResolver(context, this.files); + this.resolver.getResource("foo.txt"); + verify(resourceLoader).getResource("foo.txt"); + } + + @Test + public void customResourceLoaderIsUsedInWebApplication() throws Exception { + GenericWebApplicationContext context = new GenericWebApplicationContext( + new MockServletContext()); + ResourceLoader resourceLoader = mock(ResourceLoader.class); + context.setResourceLoader(resourceLoader); + this.resolver = new ClassLoaderFilesResourcePatternResolver(context, this.files); + this.resolver.getResource("foo.txt"); + verify(resourceLoader).getResource("foo.txt"); + } + private File createFile(File folder, String name) throws IOException { File file = new File(folder, name); FileCopyUtils.copy("test".getBytes(), file);