diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessor.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessor.java index 0647386c21cb..cae1c9a88c6a 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessor.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -124,18 +124,19 @@ private static void processAheadOfTime(Class clazz, Collection> vali try { descriptor = validator.getConstraintsForClass(clazz); } - catch (RuntimeException ex) { + catch (RuntimeException | LinkageError ex) { + String className = clazz.getName(); if (KotlinDetector.isKotlinType(clazz) && ex instanceof ArrayIndexOutOfBoundsException) { // See https://hibernate.atlassian.net/browse/HV-1796 and https://youtrack.jetbrains.com/issue/KT-40857 - logger.warn("Skipping validation constraint hint inference for class " + clazz + + logger.warn("Skipping validation constraint hint inference for class " + className + " due to an ArrayIndexOutOfBoundsException at validator level"); } else if (ex instanceof TypeNotPresentException) { logger.debug("Skipping validation constraint hint inference for class " + - clazz + " due to a TypeNotPresentException at validator level: " + ex.getMessage()); + className + " due to a TypeNotPresentException at validator level: " + ex.getMessage()); } else { - logger.warn("Skipping validation constraint hint inference for class " + clazz, ex); + logger.warn("Skipping validation constraint hint inference for class " + className, ex); } return; } diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessorTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessorTests.java index d43d8033317d..065dff57ec87 100644 --- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -40,6 +40,7 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.core.OverridingClassLoader; import org.springframework.lang.Nullable; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; @@ -121,6 +122,14 @@ void shouldProcessTransitiveGenericTypeLevelConstraint() { .withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.generationContext.getRuntimeHints()); } + @Test // gh-33940 + void shouldSkipConstraintWithMissingDependency() throws Exception { + FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader()); + Class beanClass = classLoader.loadClass(ConstraintWithMissingDependency.class.getName()); + process(beanClass); + assertThat(this.generationContext.getRuntimeHints().reflection().typeHints()).isEmpty(); + } + private void process(Class beanClass) { BeanRegistrationAotContribution contribution = createContribution(beanClass); if (contribution != null) { @@ -244,4 +253,31 @@ public void setExclude(List exclude) { } } + static class ConstraintWithMissingDependency { + + private final Filtered filtered = new Filtered(); + } + + static class Filtered {} + + static class FilteringClassLoader extends OverridingClassLoader { + + FilteringClassLoader(ClassLoader parent) { + super(parent); + } + + @Override + protected boolean isEligibleForOverriding(String className) { + return className.startsWith(BeanValidationBeanRegistrationAotProcessorTests.class.getName()); + } + + @Override + protected Class loadClassForOverriding(String name) throws ClassNotFoundException { + if (name.contains("Filtered")) { + throw new NoClassDefFoundError(name); + } + return super.loadClassForOverriding(name); + } + } + }