From 0fc9e4ec1c944c5a6e9b8b9d26d8b3b93d1c2d64 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Thu, 31 Jul 2025 05:01:34 +0100 Subject: [PATCH] Exclude HttpServiceClient from GroupRegistry scan The detect methods in the GroupRegistry that find all interfaces with HttpExchange annotations now exclude HttpServiceClient interfaces that are instead supported by a dedicated registrar. This ensures there is no overlap between the HttpServiceClient registrar scan and the ImportHttpServices registrar scan or the scan of any other custom registrar. See gh-35244 --- .../AbstractHttpServiceRegistrar.java | 19 ++++++++++++++++++- .../service/registry/ImportHttpServices.java | 9 +++++---- ...ttpServiceClientRegistrarSupportTests.java | 17 ++++++++++------- .../BasicClient.java} | 4 ++-- .../{client => echo}/EchoClientA.java | 2 +- .../{client => echo}/EchoClientB.java | 2 +- 6 files changed, 37 insertions(+), 16 deletions(-) rename spring-web/src/test/java/org/springframework/web/service/registry/{client/DefaultClient.java => basic/BasicClient.java} (90%) rename spring-web/src/test/java/org/springframework/web/service/registry/{client => echo}/EchoClientA.java (94%) rename spring-web/src/test/java/org/springframework/web/service/registry/{client => echo}/EchoClientB.java (94%) diff --git a/spring-web/src/main/java/org/springframework/web/service/registry/AbstractHttpServiceRegistrar.java b/spring-web/src/main/java/org/springframework/web/service/registry/AbstractHttpServiceRegistrar.java index d7d9b461b0d..099e01f13fc 100644 --- a/spring-web/src/main/java/org/springframework/web/service/registry/AbstractHttpServiceRegistrar.java +++ b/spring-web/src/main/java/org/springframework/web/service/registry/AbstractHttpServiceRegistrar.java @@ -188,6 +188,12 @@ public abstract class AbstractHttpServiceRegistrar implements GroupRegistry registry, AnnotationMetadata importingClassMetadata); + /** + * Exposes the scan for HTTP Service types, looking for + * interfaces with type or method {@link HttpExchange} annotations. + * @param basePackage the packages to look under + * @return match bean definitions + */ protected Stream findHttpServices(String basePackage) { if (this.scanner == null) { Assert.state(this.environment != null, "Environment has not been set"); @@ -257,7 +263,10 @@ public abstract class AbstractHttpServiceRegistrar implements /** * Detect HTTP Service types in the given packages, looking for - * interfaces with a type and/or method {@link HttpExchange} annotation. + * interfaces with type or method {@link HttpExchange} annotations. + *

The performed scan, however, filters out any interfaces + * annotated with {@link HttpServiceClient} that are instead supported + * by {@link HttpServiceClientRegistrarSupport}. */ GroupSpec detectInBasePackages(Class... packageClasses); @@ -314,11 +323,19 @@ public abstract class AbstractHttpServiceRegistrar implements private void detectInBasePackage(String packageName) { findHttpServices(packageName) + .filter(DefaultGroupSpec::isNotHttpServiceClientAnnotated) .map(BeanDefinition::getBeanClassName) .filter(Objects::nonNull) .forEach(this::registerServiceTypeName); } + private static boolean isNotHttpServiceClientAnnotated(BeanDefinition defintion) { + if (defintion instanceof AnnotatedBeanDefinition abd) { + return !abd.getMetadata().hasAnnotation(HttpServiceClient.class.getName()); + } + return true; + } + private void registerServiceTypeName(String httpServiceTypeName) { this.registration.httpServiceTypeNames().add(httpServiceTypeName); } diff --git a/spring-web/src/main/java/org/springframework/web/service/registry/ImportHttpServices.java b/spring-web/src/main/java/org/springframework/web/service/registry/ImportHttpServices.java index 3785f6834df..4dbd7450779 100644 --- a/spring-web/src/main/java/org/springframework/web/service/registry/ImportHttpServices.java +++ b/spring-web/src/main/java/org/springframework/web/service/registry/ImportHttpServices.java @@ -76,10 +76,11 @@ public @interface ImportHttpServices { String group() default HttpServiceGroup.DEFAULT_GROUP_NAME; /** - * Detect HTTP Services in the packages of the specified classes by looking - * for interfaces with type-level or method-level - * {@link org.springframework.web.service.annotation.HttpExchange @HttpExchange} - * annotations. + * Detect HTTP Services in the packages of the specified classes, looking + * for interfaces with type or method {@link HttpExchange} annotations. + *

The performed scan, however, filters out interfaces annotated with + * {@link HttpServiceClient} that are instead supported by + * {@link HttpServiceClientRegistrarSupport}. */ Class[] basePackageClasses() default {}; diff --git a/spring-web/src/test/java/org/springframework/web/service/registry/HttpServiceClientRegistrarSupportTests.java b/spring-web/src/test/java/org/springframework/web/service/registry/HttpServiceClientRegistrarSupportTests.java index ef5b11a9524..3045eb97958 100644 --- a/spring-web/src/test/java/org/springframework/web/service/registry/HttpServiceClientRegistrarSupportTests.java +++ b/spring-web/src/test/java/org/springframework/web/service/registry/HttpServiceClientRegistrarSupportTests.java @@ -25,9 +25,9 @@ import org.junit.jupiter.api.Test; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.type.AnnotationMetadata; -import org.springframework.web.service.registry.client.DefaultClient; -import org.springframework.web.service.registry.client.EchoClientA; -import org.springframework.web.service.registry.client.EchoClientB; +import org.springframework.web.service.registry.basic.BasicClient; +import org.springframework.web.service.registry.echo.EchoClientA; +import org.springframework.web.service.registry.echo.EchoClientB; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -43,21 +43,24 @@ public class HttpServiceClientRegistrarSupportTests { @Test void register() { + + List basePackages = List.of( + BasicClient.class.getPackageName(), EchoClientA.class.getPackageName()); + HttpServiceClientRegistrarSupport registrar = new HttpServiceClientRegistrarSupport() { @Override protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata importingClassMetadata) { - findAndRegisterHttpServiceClients(groupRegistry, List.of(getClass().getPackage().getName() + ".client")); + findAndRegisterHttpServiceClients(groupRegistry, basePackages); } }; registrar.setEnvironment(new StandardEnvironment()); registrar.setResourceLoader(new PathMatchingResourcePatternResolver()); - registrar.registerHttpServices(groupRegistry, mock(AnnotationMetadata.class)); assertGroups( - TestGroup.ofListing("echo", EchoClientA.class, EchoClientB.class), - TestGroup.ofListing("default", DefaultClient.class)); + TestGroup.ofListing("default", BasicClient.class), + TestGroup.ofListing("echo", EchoClientA.class, EchoClientB.class)); } private void assertGroups(TestGroup... expectedGroups) { diff --git a/spring-web/src/test/java/org/springframework/web/service/registry/client/DefaultClient.java b/spring-web/src/test/java/org/springframework/web/service/registry/basic/BasicClient.java similarity index 90% rename from spring-web/src/test/java/org/springframework/web/service/registry/client/DefaultClient.java rename to spring-web/src/test/java/org/springframework/web/service/registry/basic/BasicClient.java index 197f950873d..6b81e0e7796 100644 --- a/spring-web/src/test/java/org/springframework/web/service/registry/client/DefaultClient.java +++ b/spring-web/src/test/java/org/springframework/web/service/registry/basic/BasicClient.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.web.service.registry.client; +package org.springframework.web.service.registry.basic; import org.springframework.web.bind.annotation.RequestParam; @@ -22,7 +22,7 @@ import org.springframework.web.service.annotation.GetExchange; import org.springframework.web.service.registry.HttpServiceClient; @HttpServiceClient -public interface DefaultClient { +public interface BasicClient { @GetExchange String handle(@RequestParam String input); diff --git a/spring-web/src/test/java/org/springframework/web/service/registry/client/EchoClientA.java b/spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoClientA.java similarity index 94% rename from spring-web/src/test/java/org/springframework/web/service/registry/client/EchoClientA.java rename to spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoClientA.java index e339f9a35d6..7ab473f5844 100644 --- a/spring-web/src/test/java/org/springframework/web/service/registry/client/EchoClientA.java +++ b/spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoClientA.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.web.service.registry.client; +package org.springframework.web.service.registry.echo; import org.springframework.web.bind.annotation.RequestParam; diff --git a/spring-web/src/test/java/org/springframework/web/service/registry/client/EchoClientB.java b/spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoClientB.java similarity index 94% rename from spring-web/src/test/java/org/springframework/web/service/registry/client/EchoClientB.java rename to spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoClientB.java index ed28b908ff9..88368f0c931 100644 --- a/spring-web/src/test/java/org/springframework/web/service/registry/client/EchoClientB.java +++ b/spring-web/src/test/java/org/springframework/web/service/registry/echo/EchoClientB.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.web.service.registry.client; +package org.springframework.web.service.registry.echo; import org.springframework.web.bind.annotation.RequestParam;