diff --git a/framework-docs/modules/ROOT/pages/core/expressions/language-ref.adoc b/framework-docs/modules/ROOT/pages/core/expressions/language-ref.adoc index ebce4c294cc..5cee5eac42b 100644 --- a/framework-docs/modules/ROOT/pages/core/expressions/language-ref.adoc +++ b/framework-docs/modules/ROOT/pages/core/expressions/language-ref.adoc @@ -2,4 +2,12 @@ = Language Reference :page-section-summary-toc: 1 -This section describes how the Spring Expression Language works. +Spring Expression Language (SpEL) expressions are composed of a sequence of tokens such +as literals, operators, method invocations, and so forth. + +Whitespace can be used freely between tokens to format and improve the readability of +expressions. Specifically, the `\s` (space), `\t` (tab), `\r` (carriage return), and `\n` +(newline) characters are all valid separators between tokens. However, whitespace is +ignored by the expression parser unless it is part of a string literal. + +The following sections describe the features and syntax of SpEL. diff --git a/framework-docs/modules/ROOT/pages/core/expressions/language-ref/literal.adoc b/framework-docs/modules/ROOT/pages/core/expressions/language-ref/literal.adoc index 5e60c6697ba..a191ed6f935 100644 --- a/framework-docs/modules/ROOT/pages/core/expressions/language-ref/literal.adoc +++ b/framework-docs/modules/ROOT/pages/core/expressions/language-ref/literal.adoc @@ -2,7 +2,6 @@ = Literal Expressions SpEL supports the following types of literal expressions. -The `\s`, `\t`, `\r` and `\n` characters are all valid separators between tokens. String :: Strings can be delimited by single quotation marks (`'`) or double quotation marks diff --git a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java index cf784b29743..f28f7760582 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java @@ -604,7 +604,7 @@ final class TypeMappedAnnotation extends AbstractMergedAnn return clazz.getClassLoader(); } if (this.source instanceof Member member) { - member.getDeclaringClass().getClassLoader(); + return member.getDeclaringClass().getClassLoader(); } } return null; diff --git a/spring-core/src/test/java/org/springframework/core/annotation/TypeMappedAnnotationTests.java b/spring-core/src/test/java/org/springframework/core/annotation/TypeMappedAnnotationTests.java index b46fd990af7..38b06d20880 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/TypeMappedAnnotationTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/TypeMappedAnnotationTests.java @@ -20,6 +20,7 @@ import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -28,6 +29,7 @@ import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.springframework.core.OverridingClassLoader; import org.springframework.core.annotation.MergedAnnotation.Adapt; import static org.assertj.core.api.Assertions.assertThat; @@ -125,6 +127,23 @@ class TypeMappedAnnotationTests { assertThat(annotation.getClass("classValue")).isEqualTo(InputStream.class); } + @Test // gh-36606 + void adaptFromStringToClassWithMemberSourceUsesMemberClassLoader() throws Exception { + OverridingClassLoader classLoader = new OverridingClassLoader(getClass().getClassLoader()) { + @Override + protected boolean isEligibleForOverriding(String className) { + return ClassAttributes.class.getName().equals(className); + } + }; + Class sourceClass = classLoader.loadClass(ClassAttributes.class.getName()); + Method sourceMethod = sourceClass.getDeclaredMethod("classValue"); + + MergedAnnotation annotation = TypeMappedAnnotation.of(null, sourceMethod, + ClassAttributes.class, Map.of("classValue", sourceClass.getName())); + + assertThat(annotation.getClass("classValue").getClassLoader()).isSameAs(classLoader); + } + @Test void adaptFromStringArrayToClassArray() { MergedAnnotation annotation = TypeMappedAnnotation.of(null, null, ClassAttributes.class,