Introduce ClassFormatException and spring.classformat.ignore property
Closes gh-27691
This commit is contained in:
parent
77b0382a6c
commit
c57b7e8418
|
|
@ -36,6 +36,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.context.index.CandidateComponentsIndex;
|
||||
import org.springframework.context.index.CandidateComponentsIndexLoader;
|
||||
import org.springframework.core.SpringProperties;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.EnvironmentCapable;
|
||||
|
|
@ -47,6 +48,7 @@ import org.springframework.core.io.support.ResourcePatternResolver;
|
|||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.ClassFormatException;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
|
|
@ -93,6 +95,18 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
|||
|
||||
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
||||
|
||||
/**
|
||||
* System property that instructs Spring to ignore class format exceptions during
|
||||
* classpath scanning, in particular for unsupported class file versions.
|
||||
* By default, such a class format mismatch leads to a classpath scanning failure.
|
||||
* @since 6.1.2
|
||||
* @see ClassFormatException
|
||||
*/
|
||||
public static final String IGNORE_CLASSFORMAT_PROPERTY_NAME = "spring.classformat.ignore";
|
||||
|
||||
private static final boolean shouldIgnoreClassFormatException =
|
||||
SpringProperties.getFlag(IGNORE_CLASSFORMAT_PROPERTY_NAME);
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
|
@ -480,9 +494,20 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
|||
logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
catch (ClassFormatException ex) {
|
||||
if (shouldIgnoreClassFormatException) {
|
||||
if (debugEnabled) {
|
||||
logger.debug("Ignored incompatible class format in " + resource + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new BeanDefinitionStoreException("Incompatible class format in " + resource +
|
||||
": set system property 'spring.classformat.ignore' to 'true' " +
|
||||
"if you mean to ignore such files during classpath scanning", ex);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"Failed to read candidate component class: " + resource, ex);
|
||||
throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2002-2023 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
|
||||
*
|
||||
* https://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.core.type.classreading;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Exception that indicates an incompatible class format encountered
|
||||
* in a class file during metadata reading.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 6.1.2
|
||||
* @see MetadataReaderFactory#getMetadataReader(Resource)
|
||||
* @see ClassFormatError
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ClassFormatException extends IOException {
|
||||
|
||||
/**
|
||||
* Construct a new {@code ClassFormatException} with the
|
||||
* supplied message.
|
||||
* @param message the detail message
|
||||
*/
|
||||
public ClassFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code ClassFormatException} with the
|
||||
* supplied message and cause.
|
||||
* @param message the detail message
|
||||
* @param cause the root cause
|
||||
*/
|
||||
public ClassFormatException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
|
|
@ -35,6 +35,7 @@ public interface MetadataReaderFactory {
|
|||
* Obtain a MetadataReader for the given class name.
|
||||
* @param className the class name (to be resolved to a ".class" file)
|
||||
* @return a holder for the ClassReader instance (never {@code null})
|
||||
* @throws ClassFormatException in case of an incompatible class format
|
||||
* @throws IOException in case of I/O failure
|
||||
*/
|
||||
MetadataReader getMetadataReader(String className) throws IOException;
|
||||
|
|
@ -43,6 +44,7 @@ public interface MetadataReaderFactory {
|
|||
* Obtain a MetadataReader for the given resource.
|
||||
* @param resource the resource (pointing to a ".class" file)
|
||||
* @return a holder for the ClassReader instance (never {@code null})
|
||||
* @throws ClassFormatException in case of an incompatible class format
|
||||
* @throws IOException in case of I/O failure
|
||||
*/
|
||||
MetadataReader getMetadataReader(Resource resource) throws IOException;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
|
|
@ -35,8 +35,8 @@ import org.springframework.lang.Nullable;
|
|||
*/
|
||||
final class SimpleMetadataReader implements MetadataReader {
|
||||
|
||||
private static final int PARSING_OPTIONS = ClassReader.SKIP_DEBUG
|
||||
| ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES;
|
||||
private static final int PARSING_OPTIONS =
|
||||
(ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
|
|
@ -56,8 +56,10 @@ final class SimpleMetadataReader implements MetadataReader {
|
|||
return new ClassReader(is);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
throw new IOException("ASM ClassReader failed to parse class file - " +
|
||||
"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
|
||||
throw new ClassFormatException("ASM ClassReader failed to parse class file - " +
|
||||
"probably due to a new Java class file version that is not supported yet. " +
|
||||
"Consider compiling with a lower '-target' or upgrade your framework version. " +
|
||||
"Affected class: " + resource, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
|||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.InfrastructureProxy;
|
||||
import org.springframework.core.SpringProperties;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
|
|
@ -59,6 +60,7 @@ import org.springframework.core.io.support.ResourcePatternResolver;
|
|||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.ClassFormatException;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
|
|
@ -110,6 +112,11 @@ public class LocalSessionFactoryBuilder extends Configuration {
|
|||
|
||||
private static final TypeFilter CONVERTER_TYPE_FILTER = new AnnotationTypeFilter(Converter.class, false);
|
||||
|
||||
private static final String IGNORE_CLASSFORMAT_PROPERTY_NAME = "spring.classformat.ignore";
|
||||
|
||||
private static final boolean shouldIgnoreClassFormatException =
|
||||
SpringProperties.getFlag(IGNORE_CLASSFORMAT_PROPERTY_NAME);
|
||||
|
||||
|
||||
private final ResourcePatternResolver resourcePatternResolver;
|
||||
|
||||
|
|
@ -335,6 +342,14 @@ public class LocalSessionFactoryBuilder extends Configuration {
|
|||
catch (FileNotFoundException ex) {
|
||||
// Ignore non-readable resource
|
||||
}
|
||||
catch (ClassFormatException ex) {
|
||||
if (!shouldIgnoreClassFormatException) {
|
||||
throw new MappingException("Incompatible class format in " + resource, ex);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new MappingException("Failed to read candidate component class: " + resource, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,11 +33,13 @@ import jakarta.persistence.PersistenceException;
|
|||
|
||||
import org.springframework.context.index.CandidateComponentsIndex;
|
||||
import org.springframework.context.index.CandidateComponentsIndexLoader;
|
||||
import org.springframework.core.SpringProperties;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.ClassFormatException;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
|
|
@ -59,6 +61,11 @@ public final class PersistenceManagedTypesScanner {
|
|||
|
||||
private static final String PACKAGE_INFO_SUFFIX = ".package-info";
|
||||
|
||||
private static final String IGNORE_CLASSFORMAT_PROPERTY_NAME = "spring.classformat.ignore";
|
||||
|
||||
private static final boolean shouldIgnoreClassFormatException =
|
||||
SpringProperties.getFlag(IGNORE_CLASSFORMAT_PROPERTY_NAME);
|
||||
|
||||
private static final Set<AnnotationTypeFilter> entityTypeFilters = new LinkedHashSet<>(4);
|
||||
|
||||
static {
|
||||
|
|
@ -79,6 +86,7 @@ public final class PersistenceManagedTypesScanner {
|
|||
this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(resourceLoader.getClassLoader());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan the specified packages and return a {@link PersistenceManagedTypes} that
|
||||
* represents the result of the scanning.
|
||||
|
|
@ -130,6 +138,14 @@ public final class PersistenceManagedTypesScanner {
|
|||
catch (FileNotFoundException ex) {
|
||||
// Ignore non-readable resource
|
||||
}
|
||||
catch (ClassFormatException ex) {
|
||||
if (!shouldIgnoreClassFormatException) {
|
||||
throw new PersistenceException("Incompatible class format in " + resource, ex);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new PersistenceException("Failed to read candidate component class: " + resource, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
|
|
@ -150,6 +166,7 @@ public final class PersistenceManagedTypesScanner {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static class ScanResult {
|
||||
|
||||
private final List<String> managedClassNames = new ArrayList<>();
|
||||
|
|
@ -163,6 +180,6 @@ public final class PersistenceManagedTypesScanner {
|
|||
return new SimplePersistenceManagedTypes(this.managedClassNames,
|
||||
this.managedPackages, this.persistenceUnitRootUrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue