From 8d390f4e5a36fbf2342a0b54b39030a17f5fb26a Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Tue, 7 Apr 2026 17:54:43 +0200 Subject: [PATCH 1/2] Polish SpEL documentation --- .../ROOT/pages/core/expressions/language-ref.adoc | 10 +++++++++- .../pages/core/expressions/language-ref/literal.adoc | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) 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 From f3b6c222f98f152643a006d0295a4324050f5c3e Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Tue, 7 Apr 2026 18:16:09 +0200 Subject: [PATCH 2/2] Use ClassLoader for method or field in MergedAnnotation Prior to this commit, the `return` keyword was missing in TypeMappedAnnotation's getClassLoader() implementation, which prevented the ClassLoader of the Member (Method or Field) from being used. This commit fixes that by adding the missing `return` keyword and adds a test using a custom ClassLoader to verify the correct behavior. Closes gh-36606 --- .../core/annotation/TypeMappedAnnotation.java | 2 +- .../annotation/TypeMappedAnnotationTests.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) 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 b7b7bdf1ddc..781078f6580 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 @@ -593,7 +593,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 e2d7f4f049c..087dc23ed66 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; @@ -124,6 +126,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,