From 33931b3216e8b8681465aa8504ae238ebe882ddd Mon Sep 17 00:00:00 2001 From: halo Date: Wed, 10 Jun 2026 09:50:53 +0900 Subject: [PATCH 1/3] GH-36738: Document rendering-scope limitation of WebFlux RequestContext.changeLocale() The Spring MVC RequestContext.changeLocale() delegates through the configured LocaleResolver and persists the locale across requests. The WebFlux equivalent only updates a field on the current RequestContext instance and the change is discarded at the end of the rendering cycle. This asymmetry was undocumented, leading developers to expect that calling changeLocale() in a WebFlux template would produce the same durable effect as in Spring MVC. Add Javadoc to both WebFlux overloads explaining that the change affects only the current rendering context, does not delegate to a LocaleContextResolver, and pointing developers towards the correct approach (WebFilter + LocaleContextResolver) for durable locale changes. Co-Authored-By: Claude Sonnet 4.6 --- .../reactive/result/view/RequestContext.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java index 624fd0ce493a..8a7656da483f 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java @@ -134,6 +134,16 @@ public TimeZone getTimeZone() { /** * Change the current locale to the specified one. + *

Note: Unlike the Spring MVC counterpart, this method only + * updates the locale within the scope of the current {@code RequestContext} instance + * for view rendering purposes. It does not delegate to a + * {@link org.springframework.web.server.i18n.LocaleContextResolver} and the change + * is not persisted beyond the current rendering cycle. For a durable locale change + * across requests in WebFlux, configure a + * {@link org.springframework.web.server.i18n.LocaleContextResolver} and update it + * from a {@link org.springframework.web.server.WebFilter} or handler method instead. + * @param locale the new locale + * @see #changeLocale(java.util.Locale, java.util.TimeZone) */ public void changeLocale(Locale locale) { this.locale = locale; @@ -141,6 +151,14 @@ public void changeLocale(Locale locale) { /** * Change the current locale to the specified locale and time zone context. + *

Note: Unlike the Spring MVC counterpart, this method only + * updates the locale and time zone within the scope of the current + * {@code RequestContext} instance for view rendering purposes. It does not + * delegate to a {@link org.springframework.web.server.i18n.LocaleContextResolver} + * and the change is not persisted beyond the current rendering cycle. + * @param locale the new locale + * @param timeZone the new time zone + * @see #changeLocale(java.util.Locale) */ public void changeLocale(Locale locale, TimeZone timeZone) { this.locale = locale; From 5e5d299887eda2617ddc05283d6e20bdc899d2bf Mon Sep 17 00:00:00 2001 From: halo Date: Wed, 10 Jun 2026 09:56:28 +0900 Subject: [PATCH 2/3] GH-36631: Fix MockHttpServletRequest.isRequestedSessionIdValid() to follow Servlet 6.1 spec Previously isRequestedSessionIdValid() always returned true by default, regardless of whether a session ID was actually supplied by the client or whether it still matched the current session. Per the Servlet 6.1 spec, isRequestedSessionIdValid() must return: - false when the client submitted no session ID - false when the submitted ID no longer matches the current session (e.g., after changeSessionId() is called) Change the backing field from boolean (default true) to @Nullable Boolean (default null = calculate dynamically). When explicitly set via setRequestedSessionIdValid(), that value takes precedence, preserving full backwards-compatibility for tests that control the flag manually. When null, the method derives its answer from the current request state. Co-Authored-By: Claude Sonnet 4.6 --- .../mock/web/MockHttpServletRequest.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java index e0074b2b8892..842401d325a0 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java @@ -250,7 +250,7 @@ public class MockHttpServletRequest implements HttpServletRequest { private @Nullable HttpSession session; - private boolean requestedSessionIdValid = true; + private @Nullable Boolean requestedSessionIdValid; private boolean requestedSessionIdFromCookie = true; @@ -1344,13 +1344,37 @@ public String changeSessionId() { return this.session.getId(); } + /** + * Set the flag returned by {@link #isRequestedSessionIdValid()}. + *

If not explicitly set, the flag is computed dynamically to follow the + * {@link jakarta.servlet.http.HttpServletRequest#isRequestedSessionIdValid()} contract: + * {@code false} when no session ID was submitted ({@link #getRequestedSessionId()} is + * {@code null}), and {@code false} after {@link #changeSessionId()} invalidates the + * originally-requested ID. + */ public void setRequestedSessionIdValid(boolean requestedSessionIdValid) { this.requestedSessionIdValid = requestedSessionIdValid; } + /** + * Returns whether the requested session ID is still valid in the current session context. + *

If {@link #setRequestedSessionIdValid} has been called explicitly, the value set + * there is returned. Otherwise this method follows the Servlet specification contract: + * returns {@code false} when the client did not submit any session ID (i.e., + * {@link #getRequestedSessionId()} is {@code null}), and returns {@code false} if the + * session ID submitted by the client no longer matches the current session (e.g., after + * {@link #changeSessionId()} was called). Returns {@code true} when a session exists and + * the submitted session ID matches the current session ID. + */ @Override public boolean isRequestedSessionIdValid() { - return this.requestedSessionIdValid; + if (this.requestedSessionIdValid != null) { + return this.requestedSessionIdValid; + } + if (this.requestedSessionId == null) { + return false; + } + return (this.session != null && this.requestedSessionId.equals(this.session.getId())); } public void setRequestedSessionIdFromCookie(boolean requestedSessionIdFromCookie) { From ff2c19cd498396c8d029c5bc98690a486610d197 Mon Sep 17 00:00:00 2001 From: halo Date: Wed, 10 Jun 2026 09:59:44 +0900 Subject: [PATCH 3/3] GH-36684: Document transaction coordination between managers sharing a DataSource Two DataSourceTransactionManager instances configured with the same DataSource object participate in the same JDBC connection and physical transaction. This follows from both managers using the same TransactionSynchronizationManager key (the DataSource instance), so the second getTransaction() call detects and joins the existing connection. This shared-DataSource behavior was previously undocumented, leading to confusion about whether multiple transaction managers can truly coordinate. Add a Javadoc paragraph to DataSourceTransactionManager explaining the behavior and when to prefer separate DataSource instances or JTA. Co-Authored-By: Claude Sonnet 4.6 --- .../jdbc/datasource/DataSourceTransactionManager.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java index cd63aad89324..23dd6cf04df9 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java @@ -106,6 +106,17 @@ * is available as an extended subclass which includes commit/rollback exception * translation, aligned with {@link org.springframework.jdbc.core.JdbcTemplate}. * + *

Transaction manager coordination: When two {@code DataSourceTransactionManager} + * instances are configured with the same {@code DataSource} object, the second + * manager's {@code getTransaction()} will detect and participate in the connection already + * bound to the thread by the first, because both managers use the same + * {@link TransactionSynchronizationManager} key (the {@code DataSource} instance). + * As a result, nested {@code TransactionTemplate}/{@code @Transactional} calls that use + * different manager beans but the same underlying {@code DataSource} share the same JDBC + * connection and physical transaction. This is generally the desired behavior for such + * setups. If separate, independent transactions are required, configure each manager with + * a distinct {@code DataSource} (or use JTA). + * * @author Juergen Hoeller * @since 02.05.2003 * @see #setNestedTransactionAllowed