diff --git a/framework-platform/framework-platform.gradle b/framework-platform/framework-platform.gradle index 536cb66d86f..51a8955720f 100644 --- a/framework-platform/framework-platform.gradle +++ b/framework-platform/framework-platform.gradle @@ -9,7 +9,7 @@ javaPlatform { dependencies { api(platform("com.fasterxml.jackson:jackson-bom:2.20.2")) api(platform("io.micrometer:micrometer-bom:1.16.4")) - api(platform("io.netty:netty-bom:4.2.10.Final")) + api(platform("io.netty:netty-bom:4.2.12.Final")) api(platform("io.projectreactor:reactor-bom:2025.0.4")) api(platform("io.rsocket:rsocket-bom:1.1.5")) api(platform("org.apache.groovy:groovy-bom:5.0.4")) diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/Cacheable.java b/spring-context/src/main/java/org/springframework/cache/annotation/Cacheable.java index fa24ab07524..4f0ccd85fd6 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/Cacheable.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/Cacheable.java @@ -193,8 +193,18 @@ public @interface Cacheable { * This is effectively a hint and the chosen cache provider might not actually * support it in a synchronized fashion. Check your provider documentation for * more details on the actual semantics. + *
Note that `sync=true` leads to a combined callback operation against the + * cache provider. If this combined operation fails on initial cache access, + * there is no separate put operation to attempt anymore. Whereas for a default + * `sync=false` setup, there are independent get and put steps: If the get step + * fails but its error is suppressed in the {@code CacheErrorHandler} setup, + * there will still be a put attempt after calling the underlying method. * @since 4.3 * @see org.springframework.cache.Cache#get(Object, Callable) + * @see org.springframework.cache.Cache#get(Object) + * @see org.springframework.cache.Cache#put(Object, Object) + * @see org.springframework.cache.interceptor.CacheErrorHandler#handleCacheGetError + * @see org.springframework.cache.interceptor.CacheErrorHandler#handleCachePutError */ boolean sync() default false; diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheErrorHandler.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheErrorHandler.java index d77ab171384..60cc938ec59 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheErrorHandler.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheErrorHandler.java @@ -16,6 +16,8 @@ package org.springframework.cache.interceptor; +import java.util.concurrent.Callable; + import org.jspecify.annotations.Nullable; import org.springframework.cache.Cache; @@ -39,10 +41,17 @@ public interface CacheErrorHandler { * Handle the given runtime exception thrown by the cache provider when * retrieving an item with the specified {@code key}, possibly * rethrowing it as a fatal exception. + *
Note that for a default {@code @Cacheable} setup, this will be called
+ * after an initial cache access failure, whereas the subsequent put step may
+ * independently fail and be handled in {@link #handleCachePutError} still.
+ * However, for {@code @Cacheable(sync=true)}, there is only a combined get step
+ * with {@code handleCacheGetError} being called in case of failure; there won't
+ * be a separate put attempt after initial cache access failure anymore.
* @param exception the exception thrown by the cache provider
* @param cache the cache
* @param key the key used to get the item
* @see Cache#get(Object)
+ * @see Cache#get(Object, Callable)
*/
void handleCacheGetError(RuntimeException exception, Cache cache, Object key);
diff --git a/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java b/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java
index e2f7c2bec6a..d5cf67ecb28 100644
--- a/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java
+++ b/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java
@@ -126,10 +126,10 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
}
private static List Matches the {@code condition} attribute of the {@link EventListener}
+ * annotation or any matching attribute on a composed annotation that
+ * is meta-annotated with {@code @EventListener}.
+ */
+ protected final @Nullable String getCondition() {
+ return this.condition;
+ }
+
+ /**
+ * Return whether default execution is applicable for the target listener.
+ * @since 6.2
+ * @see #onApplicationEvent
+ * @see EventListener#defaultExecution()
+ */
+ protected final boolean isDefaultExecution() {
+ return this.defaultExecution;
+ }
+
+
/**
* Initialize this instance.
*/
@@ -167,7 +196,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
@Override
public void onApplicationEvent(ApplicationEvent event) {
- if (isDefaultExecution()) {
+ if (this.defaultExecution) {
processEvent(event);
}
}
@@ -222,22 +251,11 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
* @see #getListenerId()
*/
protected String getDefaultListenerId() {
- Method method = getTargetMethod();
StringJoiner sj = new StringJoiner(",", "(", ")");
- for (Class> paramType : method.getParameterTypes()) {
+ for (Class> paramType : this.targetMethod.getParameterTypes()) {
sj.add(paramType.getName());
}
- return ClassUtils.getQualifiedMethodName(method) + sj;
- }
-
- /**
- * Return whether default execution is applicable for the target listener.
- * @since 6.2
- * @see #onApplicationEvent
- * @see EventListener#defaultExecution()
- */
- protected boolean isDefaultExecution() {
- return this.defaultExecution;
+ return ClassUtils.getQualifiedMethodName(this.targetMethod) + sj;
}
@@ -274,11 +292,10 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
if (args == null) {
return false;
}
- String condition = getCondition();
- if (StringUtils.hasText(condition)) {
+ if (StringUtils.hasText(this.condition)) {
Assert.notNull(this.evaluator, "EventExpressionEvaluator must not be null");
return this.evaluator.condition(
- condition, event, this.targetMethod, this.methodKey, args);
+ this.condition, event, this.targetMethod, this.methodKey, args);
}
return true;
}
@@ -402,24 +419,6 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
return this.applicationContext.getBean(this.beanName);
}
- /**
- * Return the target listener method.
- * @since 5.3
- */
- protected Method getTargetMethod() {
- return this.targetMethod;
- }
-
- /**
- * Return the condition to use.
- * Matches the {@code condition} attribute of the {@link EventListener}
- * annotation or any matching attribute on a composed annotation that
- * is meta-annotated with {@code @EventListener}.
- */
- protected @Nullable String getCondition() {
- return this.condition;
- }
-
/**
* Add additional details such as the bean type and method signature to
* the given error message.
@@ -427,7 +426,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
*/
protected String getDetailedErrorMessage(Object bean, @Nullable String message) {
StringBuilder sb = (StringUtils.hasLength(message) ? new StringBuilder(message).append('\n') : new StringBuilder());
- sb.append("HandlerMethod details: \n");
+ sb.append("ApplicationListenerMethodAdapter details: \n");
sb.append("Bean [").append(bean.getClass().getName()).append("]\n");
sb.append("Method [").append(this.method.toGenericString()).append("]\n");
return sb.toString();
diff --git a/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java b/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java
index 46c506f2c81..d09482040e5 100644
--- a/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java
+++ b/spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java
@@ -324,8 +324,7 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
catch (Throwable ex) {
// Release concurrency permit if thread creation fails
this.concurrencyThrottle.afterAccess();
- throw new TaskRejectedException(
- "Failed to start execution thread for task: " + task, ex);
+ throw new TaskRejectedException("Failed to start execution thread for task: " + task, ex);
}
}
else if (this.activeThreads != null) {
diff --git a/spring-core/src/main/java/org/springframework/core/task/support/TaskExecutorAdapter.java b/spring-core/src/main/java/org/springframework/core/task/support/TaskExecutorAdapter.java
index fa7c290613c..b9a81cecf6a 100644
--- a/spring-core/src/main/java/org/springframework/core/task/support/TaskExecutorAdapter.java
+++ b/spring-core/src/main/java/org/springframework/core/task/support/TaskExecutorAdapter.java
@@ -42,7 +42,6 @@ import org.springframework.util.Assert;
* @see java.util.concurrent.ExecutorService
* @see java.util.concurrent.Executors
*/
-@SuppressWarnings("deprecation")
public class TaskExecutorAdapter implements AsyncTaskExecutor {
private final Executor concurrentExecutor;
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/SseServerResponse.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/SseServerResponse.java
index b12a672c6d5..1ca6712f64a 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/SseServerResponse.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/SseServerResponse.java
@@ -105,7 +105,6 @@ final class SseServerResponse extends AbstractServerResponse {
private static final byte[] NL_NL = new byte[]{'\n', '\n'};
-
private final ServerHttpResponse outputMessage;
private final DeferredResult> deferredResult;
@@ -118,7 +117,6 @@ final class SseServerResponse extends AbstractServerResponse {
private boolean sendFailed;
-
public DefaultSseBuilder(HttpServletResponse response, Context context, DeferredResult> deferredResult,
HttpHeaders httpHeaders) {
this.outputMessage = new ServletServerHttpResponse(response);
@@ -184,7 +182,6 @@ final class SseServerResponse extends AbstractServerResponse {
@Override
public void data(Object object) throws IOException {
Assert.notNull(object, "Object must not be null");
-
if (object instanceof String text) {
writeString(text);
}
@@ -206,7 +203,6 @@ final class SseServerResponse extends AbstractServerResponse {
this.builder.append("data:");
try {
this.outputMessage.getBody().write(builderBytes());
-
Class> dataClass = data.getClass();
for (HttpMessageConverter> converter : this.messageConverters) {
if (converter.canWrite(dataClass, MediaType.APPLICATION_JSON)) {
@@ -291,8 +287,7 @@ final class SseServerResponse extends AbstractServerResponse {
public HttpHeaders getHeaders() {
return this.mutableHeaders;
}
-
}
-
}
+
}