diff --git a/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceProxyFactory.java b/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceProxyFactory.java index 330456f500e..a77d70202ad 100644 --- a/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceProxyFactory.java +++ b/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceProxyFactory.java @@ -63,16 +63,19 @@ public final class HttpServiceProxyFactory { private final @Nullable StringValueResolver embeddedValueResolver; + private final List proxyCustomizers; private HttpServiceProxyFactory( HttpExchangeAdapter exchangeAdapter, List argumentResolvers, List requestValuesProcessor, - @Nullable StringValueResolver embeddedValueResolver) { + @Nullable StringValueResolver embeddedValueResolver, + List proxyCustomizers) { this.exchangeAdapter = exchangeAdapter; this.argumentResolvers = argumentResolvers; this.requestValuesProcessor = new CompositeHttpRequestValuesProcessor(requestValuesProcessor); this.embeddedValueResolver = embeddedValueResolver; + this.proxyCustomizers = proxyCustomizers; } @@ -84,7 +87,6 @@ public final class HttpServiceProxyFactory { * @return the created proxy */ public S createClient(Class serviceType) { - List httpServiceMethods = MethodIntrospector.selectMethods(serviceType, this::isExchangeMethod).stream() .map(method -> createHttpServiceMethod(serviceType, method)) @@ -97,6 +99,9 @@ public final class HttpServiceProxyFactory { private S getProxy(Class serviceType, List httpServiceMethods) { MethodInterceptor interceptor = new HttpServiceMethodInterceptor(httpServiceMethods); ProxyFactory factory = new ProxyFactory(serviceType, interceptor); + for (var customizer : this.proxyCustomizers) { + customizer.customize(factory, serviceType); + } return (S) factory.getProxy(serviceType.getClassLoader()); } @@ -147,6 +152,8 @@ public final class HttpServiceProxyFactory { private @Nullable StringValueResolver embeddedValueResolver; + private final List proxyCustomizers = new ArrayList<>(); + private Builder() { } @@ -216,6 +223,19 @@ public final class HttpServiceProxyFactory { return this; } + /** + * Register an {@link ProxyFactoryCustomizer} that can + * manipulating the {@link ProxyFactory} before creating proxy. + * + * @param customizer the customizer to add + * @return this same builder instance + * @since 7.1 + */ + public Builder proxyCustomizer(ProxyFactoryCustomizer customizer) { + this.proxyCustomizers.add(customizer); + return this; + } + /** * Build the {@link HttpServiceProxyFactory} instance. */ @@ -225,7 +245,7 @@ public final class HttpServiceProxyFactory { return new HttpServiceProxyFactory( adapterToUse, initArgumentResolvers(), this.requestValuesProcessors, - this.embeddedValueResolver); + this.embeddedValueResolver, this.proxyCustomizers); } @SuppressWarnings({"DataFlowIssue", "NullAway"}) @@ -313,4 +333,18 @@ public final class HttpServiceProxyFactory { } } + /** + * Callback interface used during HttpService proxy creation. Allows manipulating the {@link ProxyFactory} creating the + * HttpService. + * + * @author Dung Dang Minh + */ + public interface ProxyFactoryCustomizer { + /** + * Manipulates the {@link ProxyFactory}, e.g. add further interceptors to it. + * + * @param factory will never be {@literal null}. + */ + void customize(ProxyFactory factory, Class serviceType); + } } diff --git a/spring-web/src/test/java/org/springframework/web/service/invoker/HttpServiceProxyFactoryTests.java b/spring-web/src/test/java/org/springframework/web/service/invoker/HttpServiceProxyFactoryTests.java index 167698514ad..e28a57fb625 100644 --- a/spring-web/src/test/java/org/springframework/web/service/invoker/HttpServiceProxyFactoryTests.java +++ b/spring-web/src/test/java/org/springframework/web/service/invoker/HttpServiceProxyFactoryTests.java @@ -16,9 +16,9 @@ package org.springframework.web.service.invoker; +import org.aopalliance.intercept.MethodInterceptor; import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; - import org.springframework.core.ParameterizedTypeReference; import org.springframework.web.service.annotation.GetExchange; @@ -44,6 +44,18 @@ public class HttpServiceProxyFactoryTests { } + @Test + void proxyFactoryCustomizer() { + var mi = (MethodInterceptor) invocation -> "intercepted"; + var factory = HttpServiceProxyFactory.builderFor(mock(HttpExchangeAdapter.class)) + .proxyCustomizer((fact, serviceType) -> { + fact.addAdvice(0, mi); + }).build(); + + var service = factory.createClient(Service.class); + assertThat(service.execute()).isEqualTo("intercepted"); + } + private interface Service { @GetExchange