From 7b08feeb6dc32890ae0e1374c6fbee0cae84d937 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 31 Mar 2025 16:41:16 +0200 Subject: [PATCH] Make jar caching configurable through setUseCaches Closes gh-34678 --- .../PathMatchingResourcePatternResolver.java | 43 ++++++++++++++++--- ...hMatchingResourcePatternResolverTests.java | 3 +- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java index 127d457d9ec..7fe7c54b082 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java @@ -215,6 +215,8 @@ import org.springframework.util.StringUtils; */ public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { + private static final Resource[] EMPTY_RESOURCE_ARRAY = {}; + private static final Log logger = LogFactory.getLog(PathMatchingResourcePatternResolver.class); /** @@ -257,6 +259,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol private PathMatcher pathMatcher = new AntPathMatcher(); + private boolean useCaches = true; + private final Map rootDirCache = new ConcurrentHashMap<>(); private final Map> jarEntriesCache = new ConcurrentHashMap<>(); @@ -331,6 +335,22 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol return this.pathMatcher; } + /** + * Specify whether this resolver should use jar caches. Default is {@code true}. + *

Switch this flag to {@code false} in order to avoid any jar caching, at + * the {@link JarURLConnection} level as well as within this resolver instance. + *

Note that {@link JarURLConnection#setDefaultUseCaches} can be turned off + * independently. This resolver-level setting is designed to only enforce + * {@code JarURLConnection#setUseCaches(false)} if necessary but otherwise + * leaves the JVM-level default in place. + * @since 6.1.19 + * @see JarURLConnection#setUseCaches + * @see #clearCache() + */ + public void setUseCaches(boolean useCaches) { + this.useCaches = useCaches; + } + @Override public Resource getResource(String location) { @@ -354,7 +374,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol // all class path resources with the given name Collections.addAll(resources, findAllClassPathResources(locationPatternWithoutPrefix)); } - return resources.toArray(new Resource[0]); + return resources.toArray(EMPTY_RESOURCE_ARRAY); } else { // Generally only look for a pattern after a prefix here, @@ -398,7 +418,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol if (logger.isTraceEnabled()) { logger.trace("Resolved class path location [" + path + "] to resources " + result); } - return result.toArray(new Resource[0]); + return result.toArray(EMPTY_RESOURCE_ARRAY); } /** @@ -535,7 +555,9 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol Set entries = this.manifestEntriesCache; if (entries == null) { entries = getClassPathManifestEntries(); - this.manifestEntriesCache = entries; + if (this.useCaches) { + this.manifestEntriesCache = entries; + } } for (ClassPathManifestEntry entry : entries) { if (!result.contains(entry.resource()) && @@ -687,7 +709,9 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol if (rootDirResources == null) { // Lookup for specific directory, creating a cache entry for it. rootDirResources = getResources(rootDirPath); - this.rootDirCache.put(rootDirPath, rootDirResources); + if (this.useCaches) { + this.rootDirCache.put(rootDirPath, rootDirResources); + } } } @@ -719,7 +743,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol if (logger.isTraceEnabled()) { logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result); } - return result.toArray(new Resource[0]); + return result.toArray(EMPTY_RESOURCE_ARRAY); } /** @@ -840,6 +864,9 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol if (con instanceof JarURLConnection jarCon) { // Should usually be the case for traditional JAR files. + if (!this.useCaches) { + jarCon.setUseCaches(false); + } try { jarFile = jarCon.getJarFile(); jarFileUrl = jarCon.getJarFileURL().toExternalForm(); @@ -903,8 +930,10 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol } } } - // Cache jar entries in TreeSet for efficient searching on re-encounter. - this.jarEntriesCache.put(jarFileUrl, entriesCache); + if (this.useCaches) { + // Cache jar entries in TreeSet for efficient searching on re-encounter. + this.jarEntriesCache.put(jarFileUrl, entriesCache); + } return result; } finally { diff --git a/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java b/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java index af1d12e0b4a..780fa233169 100644 --- a/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -132,6 +132,7 @@ class PathMatchingResourcePatternResolverTests { Path rootDir = Paths.get("src/test/resources/custom%23root").toAbsolutePath(); URL root = new URL("file:" + rootDir + "/"); resolver = new PathMatchingResourcePatternResolver(new DefaultResourceLoader(new URLClassLoader(new URL[] {root}))); + resolver.setUseCaches(false); assertExactFilenames("classpath*:scanned/*.txt", "resource#test1.txt", "resource#test2.txt"); }