From 72d79d19dbadff85d064ccfcda8647d054c2d8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 10 Apr 2025 15:22:36 +0200 Subject: [PATCH] Add more flexible registration of DefaultMethodEndpointAdapter This commit updates WsConfigurer to get a callback with the list of default method argument resolvers and return value handlers. Previously, the callback only allowed to add custom instances and these were added after the defaults. Closes gh-1080 --- .../annotation/WsConfigurationSupport.java | 28 ++-- .../ws/config/annotation/WsConfigurer.java | 14 +- .../adapter/DefaultMethodEndpointAdapter.java | 20 +++ .../WsConfigurationSupportTest.java | 136 ++++++++++++------ .../annotation/WsConfigurerAdapterTest.java | 13 +- 5 files changed, 143 insertions(+), 68 deletions(-) diff --git a/spring-ws-core/src/main/java/org/springframework/ws/config/annotation/WsConfigurationSupport.java b/spring-ws-core/src/main/java/org/springframework/ws/config/annotation/WsConfigurationSupport.java index 65b048d7..3861b800 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/config/annotation/WsConfigurationSupport.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/config/annotation/WsConfigurationSupport.java @@ -17,6 +17,7 @@ package org.springframework.ws.config.annotation; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import org.springframework.context.annotation.Bean; @@ -141,37 +142,38 @@ public class WsConfigurationSupport { * annotated endpoint methods. Consider overriding one of these other more * fine-grained methods: * */ @Bean public DefaultMethodEndpointAdapter defaultMethodEndpointAdapter() { - List argumentResolvers = new ArrayList<>(); + DefaultMethodEndpointAdapter adapter = DefaultMethodEndpointAdapter.withDefaults(); + LinkedList argumentResolvers = new LinkedList<>(adapter.getMethodArgumentResolvers()); addArgumentResolvers(argumentResolvers); + adapter.setMethodArgumentResolvers(argumentResolvers); - List returnValueHandlers = new ArrayList<>(); + LinkedList returnValueHandlers = new LinkedList<>( + adapter.getMethodReturnValueHandlers()); addReturnValueHandlers(returnValueHandlers); - - DefaultMethodEndpointAdapter adapter = new DefaultMethodEndpointAdapter(); - adapter.setCustomMethodArgumentResolvers(argumentResolvers); - adapter.setCustomMethodReturnValueHandlers(returnValueHandlers); + adapter.setMethodReturnValueHandlers(returnValueHandlers); return adapter; } /** - * Add custom {@link MethodArgumentResolver}s to use in addition to the ones + * Configure the {@link MethodArgumentResolver}s to use in addition to the ones * registered by default. - * @param argumentResolvers the list of custom converters; initially an empty list. + * @param argumentResolvers the list of resolvers; initially the default resolvers */ protected void addArgumentResolvers(List argumentResolvers) { } /** - * Add custom {@link MethodReturnValueHandler}s in addition to the ones registered by - * default. - * @param returnValueHandlers the list of custom handlers; initially an empty list. + * Configure the {@link MethodReturnValueHandler}s to use in addition to the ones + * registered by default. + * @param returnValueHandlers the list of handlers; initially the default handlers */ protected void addReturnValueHandlers(List returnValueHandlers) { } diff --git a/spring-ws-core/src/main/java/org/springframework/ws/config/annotation/WsConfigurer.java b/spring-ws-core/src/main/java/org/springframework/ws/config/annotation/WsConfigurer.java index dec311c5..28747729 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/config/annotation/WsConfigurer.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/config/annotation/WsConfigurer.java @@ -43,20 +43,18 @@ public interface WsConfigurer { } /** - * Add resolvers to support custom endpoint method argument types. - * @param argumentResolvers initially an empty list + * Configure the {@link MethodArgumentResolver}s to use in addition to the ones + * registered by default. + * @param argumentResolvers the list of resolvers; initially the default resolvers */ default void addArgumentResolvers(List argumentResolvers) { } /** - * Add handlers to support custom controller method return value types. - *

- * Using this option does not override the built-in support for handling return - * values. To customize the built-in support for handling return values, configure - * RequestMappingHandlerAdapter directly. - * @param returnValueHandlers initially an empty list + * Configure the {@link MethodReturnValueHandler}s to use in addition to the ones + * registered by default. + * @param returnValueHandlers the list of handlers; initially the default handlers */ default void addReturnValueHandlers(List returnValueHandlers) { diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java index 0e8ae148..00c53cba 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java @@ -76,6 +76,16 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter private ClassLoader classLoader; + /** + * Create a new instance with default method argument and return value resolvers. + * @return a new instance with defaults configured + */ + public static DefaultMethodEndpointAdapter withDefaults() { + DefaultMethodEndpointAdapter adapter = new DefaultMethodEndpointAdapter(); + adapter.initDefaultStrategies(); + return adapter; + } + /** * Returns the list of {@code MethodArgumentResolver}s to use. */ @@ -92,7 +102,9 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter /** * Returns the custom argument resolvers. + * @deprecated as of 4.1.0 with no replacement */ + @Deprecated(since = "4.1.0", forRemoval = true) public List getCustomMethodArgumentResolvers() { return this.customMethodArgumentResolvers; } @@ -101,7 +113,10 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter * Sets the custom handlers for method arguments. Custom handlers are ordered after * built-in ones. To override the built-in support for return value handling use * {@link #setMethodArgumentResolvers(List)}. + * @deprecated as of 4.1.0 in favor of setting a single list + * @see #withDefaults() */ + @Deprecated(since = "4.1.0", forRemoval = true) public void setCustomMethodArgumentResolvers(List customMethodArgumentResolvers) { this.customMethodArgumentResolvers = customMethodArgumentResolvers; } @@ -122,7 +137,9 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter /** * Returns the custom return value handlers. + * @deprecated as of 4.1.0 with no replacement */ + @Deprecated(since = "4.1.0", forRemoval = true) public List getCustomMethodReturnValueHandlers() { return this.customMethodReturnValueHandlers; } @@ -131,7 +148,10 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter * Sets the handlers for custom return value types. Custom handlers are ordered after * built-in ones. To override the built-in support for return value handling use * {@link #setMethodReturnValueHandlers(List)}. + * @deprecated as of 4.1.0 in favor of setting a single list + * @see #withDefaults() */ + @Deprecated(since = "4.1.0", forRemoval = true) public void setCustomMethodReturnValueHandlers(List customMethodReturnValueHandlers) { this.customMethodReturnValueHandlers = customMethodReturnValueHandlers; } diff --git a/spring-ws-core/src/test/java/org/springframework/ws/config/annotation/WsConfigurationSupportTest.java b/spring-ws-core/src/test/java/org/springframework/ws/config/annotation/WsConfigurationSupportTest.java index 3e3547e4..ffd88095 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/config/annotation/WsConfigurationSupportTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/config/annotation/WsConfigurationSupportTest.java @@ -18,82 +18,136 @@ package org.springframework.ws.config.annotation; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.ws.config.annotation.WsConfigurationSupportTest.CustomArgumentResolverConfig.MyArgumentResolver; +import org.springframework.ws.config.annotation.WsConfigurationSupportTest.CustomDefaultMethodEndpointAdapterConfig.MyDefaultMethodEndpointAdapter; +import org.springframework.ws.config.annotation.WsConfigurationSupportTest.CustomInterceptorConfig.MyInterceptor; +import org.springframework.ws.config.annotation.WsConfigurationSupportTest.CustomReturnValueHandlerConfig.MyReturnValueHandler; import org.springframework.ws.server.EndpointInterceptor; import org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter; +import org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver; +import org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler; +import org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor; import org.springframework.ws.server.endpoint.interceptor.EndpointInterceptorAdapter; import org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping; import static org.assertj.core.api.Assertions.assertThat; /** + * Tests for {@link WsConfigurationSupport}. + * * @author Arjen Poutsma + * @author Stephane Nicoll */ -public class WsConfigurationSupportTest { +class WsConfigurationSupportTest { - private ApplicationContext applicationContext; + @Test + void interceptors() { + try (ConfigurableApplicationContext applicationContext = load(CustomInterceptorConfig.class)) { + PayloadRootAnnotationMethodEndpointMapping endpointMapping = applicationContext + .getBean(PayloadRootAnnotationMethodEndpointMapping.class); + assertThat(endpointMapping.getOrder()).isEqualTo(0); + EndpointInterceptor[] interceptors = endpointMapping.getInterceptors(); + assertThat(interceptors).singleElement().isInstanceOf(MyInterceptor.class); + } + } - @BeforeEach - public void setUp() { + @Test + void argumentResolvers() { + try (ConfigurableApplicationContext applicationContext = load(CustomArgumentResolverConfig.class)) { + DefaultMethodEndpointAdapter bean = applicationContext.getBean(DefaultMethodEndpointAdapter.class); + List methodArgumentResolvers = bean.getMethodArgumentResolvers(); + assertThat(methodArgumentResolvers).hasSizeGreaterThan(1).element(0).isInstanceOf(MyArgumentResolver.class); + } + } + @Test + void returnValueHandlers() { + try (ConfigurableApplicationContext applicationContext = load(CustomReturnValueHandlerConfig.class)) { + DefaultMethodEndpointAdapter bean = applicationContext.getBean(DefaultMethodEndpointAdapter.class); + List methodReturnValueHandlers = bean.getMethodReturnValueHandlers(); + assertThat(methodReturnValueHandlers).hasSizeGreaterThan(1) + .element(0) + .isInstanceOf(MyReturnValueHandler.class); + } + } + + @Test + void defaultMethodEndpointAdapter() { + try (ConfigurableApplicationContext applicationContext = load(CustomDefaultMethodEndpointAdapterConfig.class)) { + assertThat(applicationContext.getBean(DefaultMethodEndpointAdapter.class)) + .isInstanceOf(MyDefaultMethodEndpointAdapter.class); + } + } + + private ConfigurableApplicationContext load(Class... componentClasses) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); - applicationContext.register(TestConfig.class); + applicationContext.register(componentClasses); applicationContext.refresh(); - - this.applicationContext = applicationContext; + return applicationContext; } - @Test - public void interceptors() { - - PayloadRootAnnotationMethodEndpointMapping endpointMapping = this.applicationContext - .getBean(PayloadRootAnnotationMethodEndpointMapping.class); - - assertThat(endpointMapping.getOrder()).isEqualTo(0); - - EndpointInterceptor[] interceptors = endpointMapping.getInterceptors(); - - assertThat(interceptors).hasSize(1); - assertThat(interceptors[0]).isInstanceOf(MyInterceptor.class); - } - - @Test - public void defaultMethodEndpointAdapter() { - - DefaultMethodEndpointAdapter endpointAdapter = this.applicationContext - .getBean(DefaultMethodEndpointAdapter.class); - - assertThat(endpointAdapter).isNotNull(); - assertThat(endpointAdapter).isInstanceOf(MyDefaultMethodEndpointAdapter.class); - } - - @Configuration - public static class TestConfig extends WsConfigurationSupport { + @Configuration(proxyBeanMethods = false) + static class CustomInterceptorConfig extends WsConfigurationSupport { @Override protected void addInterceptors(List interceptors) { interceptors.add(new MyInterceptor()); } + static class MyInterceptor extends EndpointInterceptorAdapter { + + } + + } + + @Configuration(proxyBeanMethods = false) + static class CustomArgumentResolverConfig extends WsConfigurationSupport { + + @Override + protected void addArgumentResolvers(List argumentResolvers) { + assertThat(argumentResolvers).isNotEmpty(); + argumentResolvers.add(0, new MyArgumentResolver()); + } + + static class MyArgumentResolver extends SourcePayloadMethodProcessor { + + } + + } + + @Configuration(proxyBeanMethods = false) + static class CustomReturnValueHandlerConfig extends WsConfigurationSupport { + + @Override + protected void addReturnValueHandlers(List returnValueHandlers) { + assertThat(returnValueHandlers).isNotEmpty(); + returnValueHandlers.add(0, new MyReturnValueHandler()); + } + + static class MyReturnValueHandler extends SourcePayloadMethodProcessor { + + } + + } + + @Configuration(proxyBeanMethods = false) + static class CustomDefaultMethodEndpointAdapterConfig extends WsConfigurationSupport { + @Bean @Override public DefaultMethodEndpointAdapter defaultMethodEndpointAdapter() { return new MyDefaultMethodEndpointAdapter(); } - } + static class MyDefaultMethodEndpointAdapter extends DefaultMethodEndpointAdapter { - public static class MyInterceptor extends EndpointInterceptorAdapter { - - } - - public static class MyDefaultMethodEndpointAdapter extends DefaultMethodEndpointAdapter { + } } diff --git a/spring-ws-core/src/test/java/org/springframework/ws/config/annotation/WsConfigurerAdapterTest.java b/spring-ws-core/src/test/java/org/springframework/ws/config/annotation/WsConfigurerAdapterTest.java index fb8e0816..9b10c4ea 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/config/annotation/WsConfigurerAdapterTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/config/annotation/WsConfigurerAdapterTest.java @@ -38,6 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Arjen Poutsma */ +@Deprecated public class WsConfigurerAdapterTest { private ApplicationContext applicationContext; @@ -72,9 +73,9 @@ public class WsConfigurerAdapterTest { DefaultMethodEndpointAdapter endpointAdapter = this.applicationContext .getBean(DefaultMethodEndpointAdapter.class); - List argumentResolvers = endpointAdapter.getCustomMethodArgumentResolvers(); + List argumentResolvers = endpointAdapter.getMethodArgumentResolvers(); - assertThat(argumentResolvers).hasSize(1); + assertThat(argumentResolvers).hasSizeGreaterThan(1); assertThat(argumentResolvers.get(0)).isInstanceOf(MyMethodArgumentResolver.class); argumentResolvers = endpointAdapter.getMethodArgumentResolvers(); @@ -88,9 +89,9 @@ public class WsConfigurerAdapterTest { DefaultMethodEndpointAdapter endpointAdapter = this.applicationContext .getBean(DefaultMethodEndpointAdapter.class); - List returnValueHandlers = endpointAdapter.getCustomMethodReturnValueHandlers(); + List returnValueHandlers = endpointAdapter.getMethodReturnValueHandlers(); - assertThat(returnValueHandlers).hasSize(1); + assertThat(returnValueHandlers).hasSizeGreaterThan(1); assertThat(returnValueHandlers.get(0)).isInstanceOf(MyReturnValueHandler.class); returnValueHandlers = endpointAdapter.getMethodReturnValueHandlers(); @@ -109,12 +110,12 @@ public class WsConfigurerAdapterTest { @Override public void addArgumentResolvers(List argumentResolvers) { - argumentResolvers.add(new MyMethodArgumentResolver()); + argumentResolvers.add(0, new MyMethodArgumentResolver()); } @Override public void addReturnValueHandlers(List returnValueHandlers) { - returnValueHandlers.add(new MyReturnValueHandler()); + returnValueHandlers.add(0, new MyReturnValueHandler()); } }