Browse Source

Add ProxyFactoryCustomizer

Allows manipulating the `ProxyFactory` before the proxy is created

See gh-36225

Signed-off-by: Đặng Minh Dũng <dungdm93@live.com>
pull/36273/head
Đặng Minh Dũng 2 months ago committed by rstoyanchev
parent
commit
f2d3da3f32
  1. 40
      spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceProxyFactory.java
  2. 14
      spring-web/src/test/java/org/springframework/web/service/invoker/HttpServiceProxyFactoryTests.java

40
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 @Nullable StringValueResolver embeddedValueResolver;
private final List<ProxyFactoryCustomizer> proxyCustomizers;
private HttpServiceProxyFactory( private HttpServiceProxyFactory(
HttpExchangeAdapter exchangeAdapter, List<HttpServiceArgumentResolver> argumentResolvers, HttpExchangeAdapter exchangeAdapter, List<HttpServiceArgumentResolver> argumentResolvers,
List<HttpRequestValues.Processor> requestValuesProcessor, List<HttpRequestValues.Processor> requestValuesProcessor,
@Nullable StringValueResolver embeddedValueResolver) { @Nullable StringValueResolver embeddedValueResolver,
List<ProxyFactoryCustomizer> proxyCustomizers) {
this.exchangeAdapter = exchangeAdapter; this.exchangeAdapter = exchangeAdapter;
this.argumentResolvers = argumentResolvers; this.argumentResolvers = argumentResolvers;
this.requestValuesProcessor = new CompositeHttpRequestValuesProcessor(requestValuesProcessor); this.requestValuesProcessor = new CompositeHttpRequestValuesProcessor(requestValuesProcessor);
this.embeddedValueResolver = embeddedValueResolver; this.embeddedValueResolver = embeddedValueResolver;
this.proxyCustomizers = proxyCustomizers;
} }
@ -84,7 +87,6 @@ public final class HttpServiceProxyFactory {
* @return the created proxy * @return the created proxy
*/ */
public <S> S createClient(Class<S> serviceType) { public <S> S createClient(Class<S> serviceType) {
List<HttpServiceMethod> httpServiceMethods = List<HttpServiceMethod> httpServiceMethods =
MethodIntrospector.selectMethods(serviceType, this::isExchangeMethod).stream() MethodIntrospector.selectMethods(serviceType, this::isExchangeMethod).stream()
.map(method -> createHttpServiceMethod(serviceType, method)) .map(method -> createHttpServiceMethod(serviceType, method))
@ -97,6 +99,9 @@ public final class HttpServiceProxyFactory {
private <S> S getProxy(Class<S> serviceType, List<HttpServiceMethod> httpServiceMethods) { private <S> S getProxy(Class<S> serviceType, List<HttpServiceMethod> httpServiceMethods) {
MethodInterceptor interceptor = new HttpServiceMethodInterceptor(httpServiceMethods); MethodInterceptor interceptor = new HttpServiceMethodInterceptor(httpServiceMethods);
ProxyFactory factory = new ProxyFactory(serviceType, interceptor); ProxyFactory factory = new ProxyFactory(serviceType, interceptor);
for (var customizer : this.proxyCustomizers) {
customizer.customize(factory, serviceType);
}
return (S) factory.getProxy(serviceType.getClassLoader()); return (S) factory.getProxy(serviceType.getClassLoader());
} }
@ -147,6 +152,8 @@ public final class HttpServiceProxyFactory {
private @Nullable StringValueResolver embeddedValueResolver; private @Nullable StringValueResolver embeddedValueResolver;
private final List<ProxyFactoryCustomizer> proxyCustomizers = new ArrayList<>();
private Builder() { private Builder() {
} }
@ -216,6 +223,19 @@ public final class HttpServiceProxyFactory {
return this; 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. * Build the {@link HttpServiceProxyFactory} instance.
*/ */
@ -225,7 +245,7 @@ public final class HttpServiceProxyFactory {
return new HttpServiceProxyFactory( return new HttpServiceProxyFactory(
adapterToUse, initArgumentResolvers(), this.requestValuesProcessors, adapterToUse, initArgumentResolvers(), this.requestValuesProcessors,
this.embeddedValueResolver); this.embeddedValueResolver, this.proxyCustomizers);
} }
@SuppressWarnings({"DataFlowIssue", "NullAway"}) @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);
}
} }

14
spring-web/src/test/java/org/springframework/web/service/invoker/HttpServiceProxyFactoryTests.java

@ -16,9 +16,9 @@
package org.springframework.web.service.invoker; package org.springframework.web.service.invoker;
import org.aopalliance.intercept.MethodInterceptor;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ParameterizedTypeReference;
import org.springframework.web.service.annotation.GetExchange; 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 { private interface Service {
@GetExchange @GetExchange

Loading…
Cancel
Save