diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 316220dfdc6..33ca3421fbe 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1451,11 +1451,18 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } } + @Override + protected void addSingleton(String beanName, Object singletonObject) { + super.addSingleton(beanName, singletonObject); + Predicate> filter = (beanType -> beanType != Object.class && beanType.isInstance(singletonObject)); + this.allBeanNamesByType.keySet().removeIf(filter); + this.singletonBeanNamesByType.keySet().removeIf(filter); + } + @Override public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException { super.registerSingleton(beanName, singletonObject); updateManualSingletonNames(set -> set.add(beanName), set -> !this.beanDefinitionMap.containsKey(beanName)); - clearByTypeCache(); } @Override diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index eb5e0da5746..b741f363826 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -3211,6 +3211,29 @@ class DefaultListableBeanFactoryTests { assertThat(holder.getNonPublicEnum()).isEqualTo(NonPublicEnum.VALUE_1); } + @Test + void mostSpecificCacheEntryForTypeMatching() { + RootBeanDefinition bd1 = new RootBeanDefinition(); + bd1.setFactoryBeanName("config"); + bd1.setFactoryMethodName("create"); + lbf.registerBeanDefinition("config", new RootBeanDefinition(BeanWithFactoryMethod.class)); + lbf.registerBeanDefinition("bd1", bd1); + lbf.registerBeanDefinition("bd2", new RootBeanDefinition(NestedTestBean.class)); + lbf.freezeConfiguration(); + + String[] allBeanNames = lbf.getBeanNamesForType(Object.class); + String[] nestedBeanNames = lbf.getBeanNamesForType(NestedTestBean.class); + assertThat(lbf.getType("bd1")).isEqualTo(TestBean.class); + assertThat(lbf.getBeanNamesForType(TestBean.class)).containsExactly("bd1"); + assertThat(lbf.getBeanNamesForType(DerivedTestBean.class)).isEmpty(); + lbf.getBean("bd1"); + assertThat(lbf.getType("bd1")).isEqualTo(DerivedTestBean.class); + assertThat(lbf.getBeanNamesForType(TestBean.class)).containsExactly("bd1"); + assertThat(lbf.getBeanNamesForType(DerivedTestBean.class)).containsExactly("bd1"); + assertThat(lbf.getBeanNamesForType(NestedTestBean.class)).isSameAs(nestedBeanNames); + assertThat(lbf.getBeanNamesForType(Object.class)).isSameAs(allBeanNames); + } + private int registerBeanDefinitions(Properties p) { return registerBeanDefinitions(p, null); @@ -3427,7 +3450,7 @@ class DefaultListableBeanFactoryTests { } public TestBean create() { - TestBean tb = new TestBean(); + DerivedTestBean tb = new DerivedTestBean(); tb.setName(this.name); return tb; } @@ -3655,11 +3678,11 @@ class DefaultListableBeanFactoryTests { private FactoryBean factoryBean; - public final FactoryBean getFactoryBean() { + public FactoryBean getFactoryBean() { return this.factoryBean; } - public final void setFactoryBean(final FactoryBean factoryBean) { + public void setFactoryBean(FactoryBean factoryBean) { this.factoryBean = factoryBean; } } diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java index 39b7287a793..2aed7cc6967 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java @@ -361,12 +361,22 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { * @throws IOException if thrown from URLConnection methods */ protected void customizeConnection(URLConnection con) throws IOException { - ResourceUtils.useCachesIfNecessary(con); + useCachesIfNecessary(con); if (con instanceof HttpURLConnection httpCon) { customizeConnection(httpCon); } } + /** + * Apply {@link URLConnection#setUseCaches useCaches} if necessary. + * @param con the URLConnection to customize + * @since 6.2.10 + * @see ResourceUtils#useCachesIfNecessary(URLConnection) + */ + void useCachesIfNecessary(URLConnection con) { + ResourceUtils.useCachesIfNecessary(con); + } + /** * Customize the given {@link HttpURLConnection} before fetching the resource. *

Can be overridden in subclasses for configuring request headers and timeouts. diff --git a/spring-core/src/main/java/org/springframework/core/io/FileUrlResource.java b/spring-core/src/main/java/org/springframework/core/io/FileUrlResource.java index b4dfb84a187..03f5be8cbd1 100644 --- a/spring-core/src/main/java/org/springframework/core/io/FileUrlResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/FileUrlResource.java @@ -109,7 +109,9 @@ public class FileUrlResource extends UrlResource implements WritableResource { @Override public Resource createRelative(String relativePath) throws MalformedURLException { - return new FileUrlResource(createRelativeURL(relativePath)); + FileUrlResource resource = new FileUrlResource(createRelativeURL(relativePath)); + resource.useCaches = this.useCaches; + return resource; } } diff --git a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java index c7412060d8d..bfaa2627356 100644 --- a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java @@ -66,6 +66,12 @@ public class UrlResource extends AbstractFileResolvingResource { */ private volatile @Nullable String cleanedUrl; + /** + * Whether to use URLConnection caches ({@code null} means default). + */ + @Nullable + volatile Boolean useCaches; + /** * Create a new {@code UrlResource} based on the given URL object. @@ -215,11 +221,22 @@ public class UrlResource extends AbstractFileResolvingResource { return cleanedUrl; } + /** + * Set an explicit flag for {@link URLConnection#setUseCaches}, + * to be applied for any {@link URLConnection} operation in this resource. + *

By default, caching will be applied only to jar resources. + * An explicit {@code true} flag applies caching to all resources, whereas an + * explicit {@code false} flag turns off caching for jar resources as well. + * @since 6.2.10 + * @see ResourceUtils#useCachesIfNecessary + */ + public void setUseCaches(boolean useCaches) { + this.useCaches = useCaches; + } + /** * This implementation opens an InputStream for the given URL. - *

It sets the {@code useCaches} flag to {@code false}, - * mainly to avoid jar file locking on Windows. * @see java.net.URL#openConnection() * @see java.net.URLConnection#setUseCaches(boolean) * @see java.net.URLConnection#getInputStream() @@ -250,6 +267,17 @@ public class UrlResource extends AbstractFileResolvingResource { } } + @Override + void useCachesIfNecessary(URLConnection con) { + Boolean useCaches = this.useCaches; + if (useCaches != null) { + con.setUseCaches(useCaches); + } + else { + super.useCachesIfNecessary(con); + } + } + /** * This implementation returns the underlying URL reference. */ @@ -304,7 +332,9 @@ public class UrlResource extends AbstractFileResolvingResource { */ @Override public Resource createRelative(String relativePath) throws MalformedURLException { - return new UrlResource(createRelativeURL(relativePath)); + UrlResource resource = new UrlResource(createRelativeURL(relativePath)); + resource.useCaches = this.useCaches; + return resource; } /** 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 01e1ca7d3c9..bee2f4d806f 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 @@ -259,7 +259,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol private PathMatcher pathMatcher = new AntPathMatcher(); - private boolean useCaches = true; + @Nullable + private Boolean useCaches; private final Map rootDirCache = new ConcurrentHashMap<>(); @@ -339,10 +340,12 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol * 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. + * {@code JarURLConnection#setUseCaches(true/false)} if necessary but otherwise + * leaves the JVM-level default in place (if this setter has not been called). + *

As of 6.2.10, this setting propagates to {@link UrlResource#setUseCaches}. * @since 6.1.19 * @see JarURLConnection#setUseCaches + * @see UrlResource#setUseCaches * @see #clearCache() */ public void setUseCaches(boolean useCaches) { @@ -352,7 +355,11 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol @Override public Resource getResource(String location) { - return getResourceLoader().getResource(location); + Resource resource = getResourceLoader().getResource(location); + if (this.useCaches != null && resource instanceof UrlResource urlResource) { + urlResource.setUseCaches(this.useCaches); + } + return resource; } @Override @@ -470,20 +477,27 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol } } else { + UrlResource resource = null; String urlString = url.toString(); String cleanedPath = StringUtils.cleanPath(urlString); if (!cleanedPath.equals(urlString)) { // Prefer cleaned URL, aligned with UrlResource#createRelative(String) try { // Retain original URL instance, potentially including custom URLStreamHandler. - return new UrlResource(new URL(url, cleanedPath)); + resource = new UrlResource(new URL(url, cleanedPath)); } catch (MalformedURLException ex) { // Fallback to regular URL construction below... } } // Retain original URL instance, potentially including custom URLStreamHandler. - return new UrlResource(url); + if (resource == null) { + resource = new UrlResource(url); + } + if (this.useCaches != null) { + resource.setUseCaches(this.useCaches); + } + return resource; } } @@ -502,6 +516,9 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol UrlResource jarResource = (ResourceUtils.URL_PROTOCOL_JAR.equals(url.getProtocol()) ? new UrlResource(url) : new UrlResource(ResourceUtils.JAR_URL_PREFIX + url + ResourceUtils.JAR_URL_SEPARATOR)); + if (this.useCaches != null) { + jarResource.setUseCaches(this.useCaches); + } if (jarResource.exists()) { result.add(jarResource); } @@ -553,7 +570,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol Set entries = this.manifestEntriesCache; if (entries == null) { entries = getClassPathManifestEntries(); - if (this.useCaches) { + if (this.useCaches == null || this.useCaches) { this.manifestEntriesCache = entries; } } @@ -574,7 +591,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol try { File jar = new File(path).getAbsoluteFile(); if (jar.isFile() && seen.add(jar)) { - manifestEntries.add(ClassPathManifestEntry.of(jar)); + manifestEntries.add(ClassPathManifestEntry.of(jar, this.useCaches)); manifestEntries.addAll(getClassPathManifestEntriesFromJar(jar)); } } @@ -613,7 +630,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol } File candidate = new File(parent, path); if (candidate.isFile() && candidate.getCanonicalPath().contains(parent.getCanonicalPath())) { - manifestEntries.add(ClassPathManifestEntry.of(candidate)); + manifestEntries.add(ClassPathManifestEntry.of(candidate, this.useCaches)); } } } @@ -707,7 +724,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol if (rootDirResources == null) { // Lookup for specific directory, creating a cache entry for it. rootDirResources = getResources(rootDirPath); - if (this.useCaches) { + if (this.useCaches == null || this.useCaches) { this.rootDirCache.put(rootDirPath, rootDirResources); } } @@ -726,7 +743,11 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol if (resolvedUrl != null) { rootDirUrl = resolvedUrl; } - rootDirResource = new UrlResource(rootDirUrl); + UrlResource urlResource = new UrlResource(rootDirUrl); + if (this.useCaches != null) { + urlResource.setUseCaches(this.useCaches); + } + rootDirResource = urlResource; } if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher())); @@ -862,8 +883,8 @@ 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); + if (this.useCaches != null) { + jarCon.setUseCaches(this.useCaches); } try { jarFile = jarCon.getJarFile(); @@ -928,7 +949,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol } } } - if (this.useCaches) { + if (this.useCaches == null || this.useCaches) { // Cache jar entries in TreeSet for efficient searching on re-encounter. this.jarEntriesCache.put(jarFileUrl, entriesCache); } @@ -1251,10 +1272,10 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol private static final String JARFILE_URL_PREFIX = ResourceUtils.JAR_URL_PREFIX + ResourceUtils.FILE_URL_PREFIX; - static ClassPathManifestEntry of(File file) throws MalformedURLException { + static ClassPathManifestEntry of(File file, @Nullable Boolean useCaches) throws MalformedURLException { String path = fixPath(file.getAbsolutePath()); - Resource resource = asJarFileResource(path); - Resource alternative = createAlternative(path); + Resource resource = asJarFileResource(path, useCaches); + Resource alternative = createAlternative(path, useCaches); return new ClassPathManifestEntry(resource, alternative); } @@ -1274,18 +1295,22 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol * @param path the file path (with or without a leading slash) * @return the alternative form or {@code null} */ - private static @Nullable Resource createAlternative(String path) { + private static @Nullable Resource createAlternative(String path, @Nullable Boolean useCaches) { try { String alternativePath = path.startsWith("/") ? path.substring(1) : "/" + path; - return asJarFileResource(alternativePath); + return asJarFileResource(alternativePath, useCaches); } catch (MalformedURLException ex) { return null; } } - private static Resource asJarFileResource(String path) throws MalformedURLException { - return new UrlResource(JARFILE_URL_PREFIX + path + ResourceUtils.JAR_URL_SEPARATOR); + private static Resource asJarFileResource(String path, @Nullable Boolean useCaches) throws MalformedURLException { + UrlResource resource = new UrlResource(JARFILE_URL_PREFIX + path + ResourceUtils.JAR_URL_SEPARATOR); + if (useCaches != null) { + resource.setUseCaches(useCaches); + } + return resource; } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/PathResourceResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/PathResourceResolverTests.java index 2038df4ee13..a147b0bd921 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/PathResourceResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/PathResourceResolverTests.java @@ -184,21 +184,19 @@ class PathResourceResolverTests { private String relativePath; - public TestUrlResource(String path) throws MalformedURLException { super(path); } - - public String getSavedRelativePath() { - return this.relativePath; - } - @Override public Resource createRelative(String relativePath) { this.relativePath = relativePath; return this; } + + public String getSavedRelativePath() { + return this.relativePath; + } } }