From 715f300fa11e4c20a7aaa9f9ac8439fc4072b00b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 12 Oct 2021 15:15:51 +0200 Subject: [PATCH] Avoid expensive isReadable() check during classpath scan Closes gh-25741 See gh-21372 --- ...athScanningCandidateComponentProvider.java | 47 +++++++++---------- .../core/io/InputStreamSource.java | 5 +- .../LocalSessionFactoryBuilder.java | 8 +++- .../DefaultPersistenceUnitManager.java | 8 +++- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java index 05ea7a5159..92d502a13f 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -16,6 +16,7 @@ package org.springframework.context.annotation; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.annotation.Annotation; import java.util.ArrayList; @@ -424,40 +425,38 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC if (traceEnabled) { logger.trace("Scanning " + resource); } - if (resource.isReadable()) { - try { - MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); - if (isCandidateComponent(metadataReader)) { - ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); - sbd.setSource(resource); - if (isCandidateComponent(sbd)) { - if (debugEnabled) { - logger.debug("Identified candidate component class: " + resource); - } - candidates.add(sbd); - } - else { - if (debugEnabled) { - logger.debug("Ignored because not a concrete top-level class: " + resource); - } + try { + MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); + if (isCandidateComponent(metadataReader)) { + ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); + sbd.setSource(resource); + if (isCandidateComponent(sbd)) { + if (debugEnabled) { + logger.debug("Identified candidate component class: " + resource); } + candidates.add(sbd); } else { - if (traceEnabled) { - logger.trace("Ignored because not matching any filter: " + resource); + if (debugEnabled) { + logger.debug("Ignored because not a concrete top-level class: " + resource); } } } - catch (Throwable ex) { - throw new BeanDefinitionStoreException( - "Failed to read candidate component class: " + resource, ex); + else { + if (traceEnabled) { + logger.trace("Ignored because not matching any filter: " + resource); + } } } - else { + catch (FileNotFoundException ex) { if (traceEnabled) { - logger.trace("Ignored because not readable: " + resource); + logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage()); } } + catch (Throwable ex) { + throw new BeanDefinitionStoreException( + "Failed to read candidate component class: " + resource, ex); + } } } catch (IOException ex) { diff --git a/spring-core/src/main/java/org/springframework/core/io/InputStreamSource.java b/spring-core/src/main/java/org/springframework/core/io/InputStreamSource.java index b769895ece..8d72c9cd8b 100644 --- a/spring-core/src/main/java/org/springframework/core/io/InputStreamSource.java +++ b/spring-core/src/main/java/org/springframework/core/io/InputStreamSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 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. @@ -48,8 +48,9 @@ public interface InputStreamSource { * creating mail attachments. For such a use case, it is required * that each {@code getInputStream()} call returns a fresh stream. * @return the input stream for the underlying resource (must not be {@code null}) - * @throws java.io.FileNotFoundException if the underlying resource doesn't exist + * @throws java.io.FileNotFoundException if the underlying resource does not exist * @throws IOException if the content stream could not be opened + * @see Resource#isReadable() */ InputStream getInputStream() throws IOException; diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java b/spring-orm/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java index 9011db8fb5..a21b13579e 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -16,6 +16,7 @@ package org.springframework.orm.hibernate5; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; @@ -320,7 +321,7 @@ public class LocalSessionFactoryBuilder extends Configuration { Resource[] resources = this.resourcePatternResolver.getResources(pattern); MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver); for (Resource resource : resources) { - if (resource.isReadable()) { + try { MetadataReader reader = readerFactory.getMetadataReader(resource); String className = reader.getClassMetadata().getClassName(); if (matchesEntityTypeFilter(reader, readerFactory)) { @@ -333,6 +334,9 @@ public class LocalSessionFactoryBuilder extends Configuration { packageNames.add(className.substring(0, className.length() - PACKAGE_INFO_SUFFIX.length())); } } + catch (FileNotFoundException ex) { + // Ignore non-readable resource + } } } } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java index 8e2fd410d5..310e2e55e4 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -16,6 +16,7 @@ package org.springframework.orm.jpa.persistenceunit; +import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; import java.util.ArrayList; @@ -585,7 +586,7 @@ public class DefaultPersistenceUnitManager Resource[] resources = this.resourcePatternResolver.getResources(pattern); MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver); for (Resource resource : resources) { - if (resource.isReadable()) { + try { MetadataReader reader = readerFactory.getMetadataReader(resource); String className = reader.getClassMetadata().getClassName(); if (matchesFilter(reader, readerFactory)) { @@ -602,6 +603,9 @@ public class DefaultPersistenceUnitManager className.substring(0, className.length() - PACKAGE_INFO_SUFFIX.length())); } } + catch (FileNotFoundException ex) { + // Ignore non-readable resource + } } } catch (IOException ex) {