Browse Source

Merge branch '7.0.x'

pull/36475/head
Juergen Hoeller 2 days ago
parent
commit
99e7543a7f
  1. 2
      framework-platform/framework-platform.gradle
  2. 10
      spring-context/src/main/java/org/springframework/cache/annotation/Cacheable.java
  3. 9
      spring-context/src/main/java/org/springframework/cache/interceptor/CacheErrorHandler.java
  4. 77
      spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java
  5. 3
      spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java
  6. 1
      spring-core/src/main/java/org/springframework/core/task/support/TaskExecutorAdapter.java
  7. 7
      spring-webmvc/src/main/java/org/springframework/web/servlet/function/SseServerResponse.java

2
framework-platform/framework-platform.gradle

@ -9,7 +9,7 @@ javaPlatform {
dependencies { dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.20.2")) api(platform("com.fasterxml.jackson:jackson-bom:2.20.2"))
api(platform("io.micrometer:micrometer-bom:1.16.4")) 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.projectreactor:reactor-bom:2025.0.4"))
api(platform("io.rsocket:rsocket-bom:1.1.5")) api(platform("io.rsocket:rsocket-bom:1.1.5"))
api(platform("org.apache.groovy:groovy-bom:5.0.4")) api(platform("org.apache.groovy:groovy-bom:5.0.4"))

10
spring-context/src/main/java/org/springframework/cache/annotation/Cacheable.java vendored

@ -193,8 +193,18 @@ public @interface Cacheable {
* This is effectively a hint and the chosen cache provider might not actually * This is effectively a hint and the chosen cache provider might not actually
* support it in a synchronized fashion. Check your provider documentation for * support it in a synchronized fashion. Check your provider documentation for
* more details on the actual semantics. * more details on the actual semantics.
* <p>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 * @since 4.3
* @see org.springframework.cache.Cache#get(Object, Callable) * @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; boolean sync() default false;

9
spring-context/src/main/java/org/springframework/cache/interceptor/CacheErrorHandler.java vendored

@ -16,6 +16,8 @@
package org.springframework.cache.interceptor; package org.springframework.cache.interceptor;
import java.util.concurrent.Callable;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
@ -39,10 +41,17 @@ public interface CacheErrorHandler {
* Handle the given runtime exception thrown by the cache provider when * Handle the given runtime exception thrown by the cache provider when
* retrieving an item with the specified {@code key}, possibly * retrieving an item with the specified {@code key}, possibly
* rethrowing it as a fatal exception. * rethrowing it as a fatal exception.
* <p>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 exception the exception thrown by the cache provider
* @param cache the cache * @param cache the cache
* @param key the key used to get the item * @param key the key used to get the item
* @see Cache#get(Object) * @see Cache#get(Object)
* @see Cache#get(Object, Callable)
*/ */
void handleCacheGetError(RuntimeException exception, Cache cache, Object key); void handleCacheGetError(RuntimeException exception, Cache cache, Object key);

77
spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java

@ -126,10 +126,10 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
} }
private static List<ResolvableType> resolveDeclaredEventTypes(Method method, @Nullable EventListener ann) { private static List<ResolvableType> resolveDeclaredEventTypes(Method method, @Nullable EventListener ann) {
int count = (KotlinDetector.isSuspendingFunction(method) ? method.getParameterCount() - 1 : method.getParameterCount()); int count = (KotlinDetector.isSuspendingFunction(method) ? method.getParameterCount() - 1 :
method.getParameterCount());
if (count > 1) { if (count > 1) {
throw new IllegalStateException( throw new IllegalStateException("Maximum one parameter is allowed for event listener method: " + method);
"Maximum one parameter is allowed for event listener method: " + method);
} }
if (ann != null) { if (ann != null) {
@ -156,6 +156,35 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
} }
/**
* Return the target listener method.
* @since 7.0.7 in public form (with protected visibility since 5.3)
*/
public final Method getTargetMethod() {
return this.targetMethod;
}
/**
* Return the condition to use.
* <p>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. * Initialize this instance.
*/ */
@ -167,7 +196,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
@Override @Override
public void onApplicationEvent(ApplicationEvent event) { public void onApplicationEvent(ApplicationEvent event) {
if (isDefaultExecution()) { if (this.defaultExecution) {
processEvent(event); processEvent(event);
} }
} }
@ -222,22 +251,11 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
* @see #getListenerId() * @see #getListenerId()
*/ */
protected String getDefaultListenerId() { protected String getDefaultListenerId() {
Method method = getTargetMethod();
StringJoiner sj = new StringJoiner(",", "(", ")"); StringJoiner sj = new StringJoiner(",", "(", ")");
for (Class<?> paramType : method.getParameterTypes()) { for (Class<?> paramType : this.targetMethod.getParameterTypes()) {
sj.add(paramType.getName()); sj.add(paramType.getName());
} }
return ClassUtils.getQualifiedMethodName(method) + sj; return ClassUtils.getQualifiedMethodName(this.targetMethod) + 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;
} }
@ -274,11 +292,10 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
if (args == null) { if (args == null) {
return false; return false;
} }
String condition = getCondition(); if (StringUtils.hasText(this.condition)) {
if (StringUtils.hasText(condition)) {
Assert.notNull(this.evaluator, "EventExpressionEvaluator must not be null"); Assert.notNull(this.evaluator, "EventExpressionEvaluator must not be null");
return this.evaluator.condition( return this.evaluator.condition(
condition, event, this.targetMethod, this.methodKey, args); this.condition, event, this.targetMethod, this.methodKey, args);
} }
return true; return true;
} }
@ -402,24 +419,6 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
return this.applicationContext.getBean(this.beanName); 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.
* <p>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 * Add additional details such as the bean type and method signature to
* the given error message. * the given error message.
@ -427,7 +426,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
*/ */
protected String getDetailedErrorMessage(Object bean, @Nullable String message) { protected String getDetailedErrorMessage(Object bean, @Nullable String message) {
StringBuilder sb = (StringUtils.hasLength(message) ? new StringBuilder(message).append('\n') : new StringBuilder()); 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("Bean [").append(bean.getClass().getName()).append("]\n");
sb.append("Method [").append(this.method.toGenericString()).append("]\n"); sb.append("Method [").append(this.method.toGenericString()).append("]\n");
return sb.toString(); return sb.toString();

3
spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java

@ -324,8 +324,7 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
catch (Throwable ex) { catch (Throwable ex) {
// Release concurrency permit if thread creation fails // Release concurrency permit if thread creation fails
this.concurrencyThrottle.afterAccess(); this.concurrencyThrottle.afterAccess();
throw new TaskRejectedException( throw new TaskRejectedException("Failed to start execution thread for task: " + task, ex);
"Failed to start execution thread for task: " + task, ex);
} }
} }
else if (this.activeThreads != null) { else if (this.activeThreads != null) {

1
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.ExecutorService
* @see java.util.concurrent.Executors * @see java.util.concurrent.Executors
*/ */
@SuppressWarnings("deprecation")
public class TaskExecutorAdapter implements AsyncTaskExecutor { public class TaskExecutorAdapter implements AsyncTaskExecutor {
private final Executor concurrentExecutor; private final Executor concurrentExecutor;

7
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 static final byte[] NL_NL = new byte[]{'\n', '\n'};
private final ServerHttpResponse outputMessage; private final ServerHttpResponse outputMessage;
private final DeferredResult<?> deferredResult; private final DeferredResult<?> deferredResult;
@ -118,7 +117,6 @@ final class SseServerResponse extends AbstractServerResponse {
private boolean sendFailed; private boolean sendFailed;
public DefaultSseBuilder(HttpServletResponse response, Context context, DeferredResult<?> deferredResult, public DefaultSseBuilder(HttpServletResponse response, Context context, DeferredResult<?> deferredResult,
HttpHeaders httpHeaders) { HttpHeaders httpHeaders) {
this.outputMessage = new ServletServerHttpResponse(response); this.outputMessage = new ServletServerHttpResponse(response);
@ -184,7 +182,6 @@ final class SseServerResponse extends AbstractServerResponse {
@Override @Override
public void data(Object object) throws IOException { public void data(Object object) throws IOException {
Assert.notNull(object, "Object must not be null"); Assert.notNull(object, "Object must not be null");
if (object instanceof String text) { if (object instanceof String text) {
writeString(text); writeString(text);
} }
@ -206,7 +203,6 @@ final class SseServerResponse extends AbstractServerResponse {
this.builder.append("data:"); this.builder.append("data:");
try { try {
this.outputMessage.getBody().write(builderBytes()); this.outputMessage.getBody().write(builderBytes());
Class<?> dataClass = data.getClass(); Class<?> dataClass = data.getClass();
for (HttpMessageConverter<?> converter : this.messageConverters) { for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter.canWrite(dataClass, MediaType.APPLICATION_JSON)) { if (converter.canWrite(dataClass, MediaType.APPLICATION_JSON)) {
@ -291,8 +287,7 @@ final class SseServerResponse extends AbstractServerResponse {
public HttpHeaders getHeaders() { public HttpHeaders getHeaders() {
return this.mutableHeaders; return this.mutableHeaders;
} }
} }
} }
} }

Loading…
Cancel
Save