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
This commit is contained in:
Stéphane Nicoll
2025-04-10 15:22:36 +02:00
parent ef00c3e2db
commit 72d79d19db
5 changed files with 143 additions and 68 deletions

View File

@@ -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:
* <ul>
* <li>{@link #addArgumentResolvers(List)} for adding custom argument resolvers.
* <li>{@link #addReturnValueHandlers(List)} for adding custom return value handlers.
* <li>{@link #addArgumentResolvers(List)} for configuring the argument resolvers.
* <li>{@link #addReturnValueHandlers(List)} for configuring the return value
* handlers.
* </ul>
*/
@Bean
public DefaultMethodEndpointAdapter defaultMethodEndpointAdapter() {
List<MethodArgumentResolver> argumentResolvers = new ArrayList<>();
DefaultMethodEndpointAdapter adapter = DefaultMethodEndpointAdapter.withDefaults();
LinkedList<MethodArgumentResolver> argumentResolvers = new LinkedList<>(adapter.getMethodArgumentResolvers());
addArgumentResolvers(argumentResolvers);
adapter.setMethodArgumentResolvers(argumentResolvers);
List<MethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
LinkedList<MethodReturnValueHandler> 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<MethodArgumentResolver> 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<MethodReturnValueHandler> returnValueHandlers) {
}

View File

@@ -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<MethodArgumentResolver> argumentResolvers) {
}
/**
* Add handlers to support custom controller method return value types.
* <p>
* 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<MethodReturnValueHandler> returnValueHandlers) {

View File

@@ -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<MethodArgumentResolver> 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<MethodArgumentResolver> 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<MethodReturnValueHandler> 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<MethodReturnValueHandler> customMethodReturnValueHandlers) {
this.customMethodReturnValueHandlers = customMethodReturnValueHandlers;
}

View File

@@ -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<MethodArgumentResolver> 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<MethodReturnValueHandler> 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<EndpointInterceptor> interceptors) {
interceptors.add(new MyInterceptor());
}
static class MyInterceptor extends EndpointInterceptorAdapter {
}
}
@Configuration(proxyBeanMethods = false)
static class CustomArgumentResolverConfig extends WsConfigurationSupport {
@Override
protected void addArgumentResolvers(List<MethodArgumentResolver> 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<MethodReturnValueHandler> 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 {
}
}

View File

@@ -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<MethodArgumentResolver> argumentResolvers = endpointAdapter.getCustomMethodArgumentResolvers();
List<MethodArgumentResolver> 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<MethodReturnValueHandler> returnValueHandlers = endpointAdapter.getCustomMethodReturnValueHandlers();
List<MethodReturnValueHandler> 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<MethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyMethodArgumentResolver());
argumentResolvers.add(0, new MyMethodArgumentResolver());
}
@Override
public void addReturnValueHandlers(List<MethodReturnValueHandler> returnValueHandlers) {
returnValueHandlers.add(new MyReturnValueHandler());
returnValueHandlers.add(0, new MyReturnValueHandler());
}
}