diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java index b8e7a272464..f9bfac9b2b2 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java @@ -17,8 +17,10 @@ package org.springframework.boot.loader; import java.io.IOException; +import java.net.JarURLConnection; import java.net.URL; import java.net.URLClassLoader; +import java.net.URLConnection; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.util.Arrays; @@ -228,6 +230,30 @@ public class LaunchedURLClassLoader extends URLClassLoader { } } + /** + * Clear URL caches. + */ + public void clearCache() { + for (URL url : getURLs()) { + try { + URLConnection connection = url.openConnection(); + if (connection instanceof JarURLConnection) { + clearCache(connection); + } + } + catch (IOException ex) { + } + } + + } + + private void clearCache(URLConnection connection) throws IOException { + Object jarFile = ((JarURLConnection) connection).getJarFile(); + if (jarFile instanceof JarFile) { + ((JarFile) jarFile).clearCache(); + } + } + @UsesJava7 private static LockProvider setupLockProvider() { try { diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java index b9a23927a6e..6fb02a4af79 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFile.java @@ -347,6 +347,10 @@ public class JarFile extends java.util.jar.JarFile { } } + public void clearCache() { + this.entries.clearCache(); + } + /** * Register a {@literal 'java.protocol.handler.pkgs'} property so that a * {@link URLStreamHandler} will be located to deal with jar URLs. diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFileEntries.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFileEntries.java index d70c7b6863b..e577cfb45d9 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFileEntries.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarFileEntries.java @@ -270,6 +270,10 @@ class JarFileEntries implements CentralDirectoryVistor, Iterable { return index; } + public void clearCache() { + this.entriesCache.clear(); + } + private AsciiBytes applyFilter(AsciiBytes name) { return (this.filter == null ? name : this.filter.apply(name)); } diff --git a/spring-boot/src/main/java/org/springframework/boot/ClearCachesApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/ClearCachesApplicationListener.java new file mode 100644 index 00000000000..83ec2c389bb --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/ClearCachesApplicationListener.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2015 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot; + +import java.lang.reflect.Method; + +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.util.ReflectionUtils; + +/** + * {@link ApplicationListener} to cleanup caches once the context is loaded. + * + * @author Phillip Webb + */ +class ClearCachesApplicationListener + implements ApplicationListener { + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + ReflectionUtils.clearCache(); + clearClassLoaderCaches(Thread.currentThread().getContextClassLoader()); + } + + private void clearClassLoaderCaches(ClassLoader classLoader) { + if (classLoader == null) { + return; + } + try { + Method clearCacheMethod = classLoader.getClass() + .getDeclaredMethod("clearCache"); + clearCacheMethod.invoke(classLoader); + } + catch (Exception ex) { + // Ignore + } + clearClassLoaderCaches(classLoader.getParent()); + } + +} diff --git a/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot/src/main/resources/META-INF/spring.factories index aed00315ca0..da1d266b2f1 100644 --- a/spring-boot/src/main/resources/META-INF/spring.factories +++ b/spring-boot/src/main/resources/META-INF/spring.factories @@ -16,6 +16,7 @@ org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener=\ +org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\