Add `@HttpServiceClient` scanning auto-configuration
Refactor `HttpServiceClientAutoConfiguration` and `ReactiveHttpServiceClientAutoConfiguration` to support scanning for `@HttpServiceClient` annotated interfaces. Closes gh-46782
This commit is contained in:
parent
11c5a8c404
commit
7a8b337b1c
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.http.client.autoconfigure.service;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional @Conditional} that matches when one or more HTTP Service bean has
|
||||
* been registered.
|
||||
*
|
||||
* @author Phillip Webb@
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnMissingHttpServiceProxyBeanCondition.class)
|
||||
public @interface ConditionalOnMissingHttpServiceProxyBean {
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.http.client.autoconfigure.service;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.HierarchicalBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.ConfigurationCondition;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for any HTTP Service proxy bean.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ConditionalOnJava
|
||||
*/
|
||||
class OnMissingHttpServiceProxyBeanCondition extends SpringBootCondition implements ConfigurationCondition {
|
||||
|
||||
static final String HTTP_SERVICE_GROUP_NAME_ATTRIBUTE = "httpServiceGroupName";
|
||||
|
||||
@Override
|
||||
public ConfigurationPhase getConfigurationPhase() {
|
||||
return ConfigurationPhase.REGISTER_BEAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition(ConditionalOnMissingHttpServiceProxyBean.class);
|
||||
BeanFactory beanFactory = context.getBeanFactory();
|
||||
while (beanFactory != null) {
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory configurableListableBeanFactory
|
||||
&& hasHttpServiceProxyBeanDefinition(configurableListableBeanFactory)) {
|
||||
return ConditionOutcome.noMatch(message.foundExactly("HTTP Service proxy bean"));
|
||||
}
|
||||
beanFactory = (beanFactory instanceof HierarchicalBeanFactory hierarchicalBeanFactory)
|
||||
? hierarchicalBeanFactory.getParentBeanFactory() : null;
|
||||
}
|
||||
return ConditionOutcome.match(message.didNotFind("").items("HTTP Service proxy beans"));
|
||||
}
|
||||
|
||||
private boolean hasHttpServiceProxyBeanDefinition(ConfigurableListableBeanFactory beanFactory) {
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
|
||||
if (beanDefinition.hasAttribute(HTTP_SERVICE_GROUP_NAME_ATTRIBUTE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common support code for HTTP Service Clients.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.http.client.autoconfigure.service;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.http.client.autoconfigure.service;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.service.annotation.GetExchange;
|
||||
import org.springframework.web.service.registry.AbstractHttpServiceRegistrar;
|
||||
import org.springframework.web.service.registry.ImportHttpServices;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for
|
||||
* {@link ConditionalOnMissingHttpServiceProxyBean @ConditionalOnMissingHttpServiceProxyBean}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ConditionalOnMissingHttpServiceProxyBeanTests {
|
||||
|
||||
@Test
|
||||
void attributeNameMatchesSpringFramework() {
|
||||
assertThat(OnMissingHttpServiceProxyBeanCondition.HTTP_SERVICE_GROUP_NAME_ATTRIBUTE).isEqualTo(
|
||||
ReflectionTestUtils.getField(AbstractHttpServiceRegistrar.class, "HTTP_SERVICE_GROUP_NAME_ATTRIBUTE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOutcomeWhenNoHttpServiceProxyMatches() {
|
||||
new ApplicationContextRunner().withUserConfiguration(TestConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasBean("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOutcomeWhenHasHttpServiceProxyDoesNotMatch() {
|
||||
new ApplicationContextRunner()
|
||||
.withUserConfiguration(HttpServiceProxyConfiguration.class, TestConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasSingleBean(TestHttpService.class).doesNotHaveBean("test"));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ImportHttpServices(TestHttpService.class)
|
||||
static class HttpServiceProxyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingHttpServiceProxyBean
|
||||
String test() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface TestHttpService {
|
||||
|
||||
@GetExchange("/test")
|
||||
String test();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -16,25 +16,14 @@
|
|||
|
||||
package org.springframework.boot.restclient.autoconfigure.service;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
|
||||
import org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration;
|
||||
import org.springframework.boot.http.client.autoconfigure.HttpClientProperties;
|
||||
import org.springframework.boot.restclient.RestClientCustomizer;
|
||||
import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.web.client.ApiVersionFormatter;
|
||||
import org.springframework.web.client.ApiVersionInserter;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.client.support.RestClientAdapter;
|
||||
import org.springframework.web.service.registry.HttpServiceProxyRegistry;
|
||||
import org.springframework.web.service.registry.ImportHttpServices;
|
||||
|
||||
/**
|
||||
|
@ -50,39 +39,9 @@ import org.springframework.web.service.registry.ImportHttpServices;
|
|||
*/
|
||||
@AutoConfiguration(after = { HttpClientAutoConfiguration.class, RestClientAutoConfiguration.class })
|
||||
@ConditionalOnClass(RestClientAdapter.class)
|
||||
@ConditionalOnBean(HttpServiceProxyRegistry.class)
|
||||
@Conditional(NotReactiveWebApplicationCondition.class)
|
||||
@EnableConfigurationProperties(HttpClientServiceProperties.class)
|
||||
public final class HttpServiceClientAutoConfiguration implements BeanClassLoaderAware {
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
HttpServiceClientAutoConfiguration() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestClientPropertiesHttpServiceGroupConfigurer restClientPropertiesHttpServiceGroupConfigurer(
|
||||
ObjectProvider<SslBundles> sslBundles, ObjectProvider<HttpClientProperties> httpClientProperties,
|
||||
HttpClientServiceProperties serviceProperties,
|
||||
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientFactoryBuilder,
|
||||
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings,
|
||||
ObjectProvider<ApiVersionInserter> apiVersionInserter,
|
||||
ObjectProvider<ApiVersionFormatter> apiVersionFormatter) {
|
||||
return new RestClientPropertiesHttpServiceGroupConfigurer(this.beanClassLoader, sslBundles,
|
||||
httpClientProperties.getIfAvailable(), serviceProperties, clientFactoryBuilder,
|
||||
clientHttpRequestFactorySettings, apiVersionInserter, apiVersionFormatter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestClientCustomizerHttpServiceGroupConfigurer restClientCustomizerHttpServiceGroupConfigurer(
|
||||
ObjectProvider<RestClientCustomizer> customizers) {
|
||||
return new RestClientCustomizerHttpServiceGroupConfigurer(customizers);
|
||||
}
|
||||
@Import({ ImportHttpServiceClientsConfiguration.class, RestClientHttpServiceClientConfiguration.class })
|
||||
public final class HttpServiceClientAutoConfiguration {
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.restclient.autoconfigure.service;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
|
||||
import org.springframework.boot.http.client.autoconfigure.service.ConditionalOnMissingHttpServiceProxyBean;
|
||||
import org.springframework.boot.restclient.autoconfigure.service.ImportHttpServiceClientsConfiguration.ImportHttpServiceClients;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.web.service.registry.AbstractClientHttpServiceRegistrar;
|
||||
import org.springframework.web.service.registry.HttpServiceClient;
|
||||
|
||||
/**
|
||||
* {@link Configuration @Configuration} to import {@link ImportHttpServiceClients} when no
|
||||
* user-defined HTTP service client beans are found.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingHttpServiceProxyBean
|
||||
@Import(ImportHttpServiceClients.class)
|
||||
class ImportHttpServiceClientsConfiguration {
|
||||
|
||||
/**
|
||||
* {@link AbstractClientHttpServiceRegistrar} to import
|
||||
* {@link HttpServiceClient @HttpServiceClient} annotated classes from
|
||||
* {@link AutoConfigurationPackages}.
|
||||
*/
|
||||
static class ImportHttpServiceClients extends AbstractClientHttpServiceRegistrar {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
ImportHttpServiceClients(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata importingClassMetadata) {
|
||||
if (AutoConfigurationPackages.has(this.beanFactory)) {
|
||||
findAndRegisterHttpServiceClients(registry, AutoConfigurationPackages.get(this.beanFactory));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.restclient.autoconfigure.service;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
|
||||
import org.springframework.boot.http.client.autoconfigure.HttpClientProperties;
|
||||
import org.springframework.boot.restclient.RestClientCustomizer;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.ApiVersionFormatter;
|
||||
import org.springframework.web.client.ApiVersionInserter;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer;
|
||||
import org.springframework.web.service.registry.HttpServiceProxyRegistry;
|
||||
|
||||
/**
|
||||
* {@link Configuration @Configuration} to register
|
||||
* {@link RestClientHttpServiceGroupConfigurer} beans to support HTTP service clients
|
||||
* backed by a {@link RestClient}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(HttpServiceProxyRegistry.class)
|
||||
class RestClientHttpServiceClientConfiguration implements BeanClassLoaderAware {
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestClientPropertiesHttpServiceGroupConfigurer restClientPropertiesHttpServiceGroupConfigurer(
|
||||
ObjectProvider<SslBundles> sslBundles, ObjectProvider<HttpClientProperties> httpClientProperties,
|
||||
HttpClientServiceProperties serviceProperties,
|
||||
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientFactoryBuilder,
|
||||
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings,
|
||||
ObjectProvider<ApiVersionInserter> apiVersionInserter,
|
||||
ObjectProvider<ApiVersionFormatter> apiVersionFormatter) {
|
||||
return new RestClientPropertiesHttpServiceGroupConfigurer(this.beanClassLoader, sslBundles,
|
||||
httpClientProperties.getIfAvailable(), serviceProperties, clientFactoryBuilder,
|
||||
clientHttpRequestFactorySettings, apiVersionInserter, apiVersionFormatter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestClientCustomizerHttpServiceGroupConfigurer restClientCustomizerHttpServiceGroupConfigurer(
|
||||
ObjectProvider<RestClientCustomizer> customizers) {
|
||||
return new RestClientCustomizerHttpServiceGroupConfigurer(customizers);
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.assertj.core.extractor.Extractors;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
|
||||
|
@ -34,6 +35,7 @@ import org.springframework.boot.http.client.HttpRedirects;
|
|||
import org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration;
|
||||
import org.springframework.boot.restclient.RestClientCustomizer;
|
||||
import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
|
||||
import org.springframework.boot.restclient.autoconfigure.service.scan.TestHttpServiceClient;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -155,6 +157,18 @@ class HttpServiceClientAutoConfigurationTests {
|
|||
.run((context) -> assertThat(context).doesNotHaveBean(HttpServiceProxyRegistry.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerHttpServiceAnnotatedInterfacesInPackages() {
|
||||
this.contextRunner.withUserConfiguration(ScanConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasSingleBean(TestHttpServiceClient.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasImportAnnotationDoesNotRegisterHttpServiceAnnotatedInterfacesInPackages() {
|
||||
this.contextRunner.withUserConfiguration(ScanConfiguration.class, HttpClientConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(TestHttpServiceClient.class));
|
||||
}
|
||||
|
||||
private HttpClient getJdkHttpClient(Object proxy) {
|
||||
return (HttpClient) Extractors.byName("clientRequestFactory.httpClient").apply(getRestClient(proxy));
|
||||
}
|
||||
|
@ -237,6 +251,12 @@ class HttpServiceClientAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigurationPackage(basePackageClasses = TestHttpServiceClient.class)
|
||||
static class ScanConfiguration {
|
||||
|
||||
}
|
||||
|
||||
interface TestClientOne {
|
||||
|
||||
@GetExchange("/hello")
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.restclient.autoconfigure.service.scan;
|
||||
|
||||
import org.springframework.web.service.annotation.GetExchange;
|
||||
import org.springframework.web.service.registry.HttpServiceClient;
|
||||
|
||||
/**
|
||||
* Test HTTP service used with scanning.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@HttpServiceClient("test")
|
||||
public interface TestHttpServiceClient {
|
||||
|
||||
@GetExchange("/hello")
|
||||
String hello();
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.webclient.autoconfigure.service;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
|
||||
import org.springframework.boot.http.client.autoconfigure.service.ConditionalOnMissingHttpServiceProxyBean;
|
||||
import org.springframework.boot.webclient.autoconfigure.service.ImportHttpServiceClientsConfiguration.ImportHttpServiceClients;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.web.service.registry.AbstractClientHttpServiceRegistrar;
|
||||
import org.springframework.web.service.registry.HttpServiceClient;
|
||||
|
||||
/**
|
||||
* {@link Configuration @Configuration} to import {@link ImportHttpServiceClients} when no
|
||||
* user-defined HTTP service client beans are found.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingHttpServiceProxyBean
|
||||
@Import(ImportHttpServiceClients.class)
|
||||
class ImportHttpServiceClientsConfiguration {
|
||||
|
||||
/**
|
||||
* {@link AbstractClientHttpServiceRegistrar} to import
|
||||
* {@link HttpServiceClient @HttpServiceClient} annotated classes from
|
||||
* {@link AutoConfigurationPackages}.
|
||||
*/
|
||||
static class ImportHttpServiceClients extends AbstractClientHttpServiceRegistrar {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
ImportHttpServiceClients(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata importingClassMetadata) {
|
||||
if (AutoConfigurationPackages.has(this.beanFactory)) {
|
||||
findAndRegisterHttpServiceClients(registry, AutoConfigurationPackages.get(this.beanFactory));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -16,24 +16,13 @@
|
|||
|
||||
package org.springframework.boot.webclient.autoconfigure.service;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.http.client.autoconfigure.reactive.ClientHttpConnectorAutoConfiguration;
|
||||
import org.springframework.boot.http.client.autoconfigure.reactive.HttpReactiveClientProperties;
|
||||
import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder;
|
||||
import org.springframework.boot.http.client.reactive.ClientHttpConnectorSettings;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.boot.webclient.WebClientCustomizer;
|
||||
import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.client.ApiVersionFormatter;
|
||||
import org.springframework.web.client.ApiVersionInserter;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
|
||||
import org.springframework.web.service.registry.HttpServiceProxyRegistry;
|
||||
import org.springframework.web.service.registry.ImportHttpServices;
|
||||
|
||||
/**
|
||||
|
@ -49,38 +38,8 @@ import org.springframework.web.service.registry.ImportHttpServices;
|
|||
*/
|
||||
@AutoConfiguration(after = { ClientHttpConnectorAutoConfiguration.class, WebClientAutoConfiguration.class })
|
||||
@ConditionalOnClass(WebClientAdapter.class)
|
||||
@ConditionalOnBean(HttpServiceProxyRegistry.class)
|
||||
@EnableConfigurationProperties(ReactiveHttpClientServiceProperties.class)
|
||||
public final class ReactiveHttpServiceClientAutoConfiguration implements BeanClassLoaderAware {
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
ReactiveHttpServiceClientAutoConfiguration() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebClientPropertiesHttpServiceGroupConfigurer webClientPropertiesHttpServiceGroupConfigurer(
|
||||
ObjectProvider<SslBundles> sslBundles, HttpReactiveClientProperties httpReactiveClientProperties,
|
||||
ReactiveHttpClientServiceProperties serviceProperties,
|
||||
ObjectProvider<ClientHttpConnectorBuilder<?>> clientConnectorBuilder,
|
||||
ObjectProvider<ClientHttpConnectorSettings> clientConnectorSettings,
|
||||
ObjectProvider<ApiVersionInserter> apiVersionInserter,
|
||||
ObjectProvider<ApiVersionFormatter> apiVersionFormatter) {
|
||||
return new WebClientPropertiesHttpServiceGroupConfigurer(this.beanClassLoader, sslBundles,
|
||||
httpReactiveClientProperties, serviceProperties, clientConnectorBuilder, clientConnectorSettings,
|
||||
apiVersionInserter, apiVersionFormatter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebClientCustomizerHttpServiceGroupConfigurer webClientCustomizerHttpServiceGroupConfigurer(
|
||||
ObjectProvider<WebClientCustomizer> customizers) {
|
||||
return new WebClientCustomizerHttpServiceGroupConfigurer(customizers);
|
||||
}
|
||||
@Import({ ImportHttpServiceClientsConfiguration.class, WebClientHttpServiceClientConfiguration.class })
|
||||
public final class ReactiveHttpServiceClientAutoConfiguration {
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.webclient.autoconfigure.service;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.http.client.autoconfigure.reactive.HttpReactiveClientProperties;
|
||||
import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder;
|
||||
import org.springframework.boot.http.client.reactive.ClientHttpConnectorSettings;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.boot.webclient.WebClientCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.ApiVersionFormatter;
|
||||
import org.springframework.web.client.ApiVersionInserter;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupConfigurer;
|
||||
import org.springframework.web.service.registry.HttpServiceProxyRegistry;
|
||||
|
||||
/**
|
||||
* {@link Configuration @Configuration} to register
|
||||
* {@link WebClientHttpServiceGroupConfigurer} beans to support HTTP service clients
|
||||
* backed by a {@link WebClient}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(HttpServiceProxyRegistry.class)
|
||||
final class WebClientHttpServiceClientConfiguration implements BeanClassLoaderAware {
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebClientPropertiesHttpServiceGroupConfigurer webClientPropertiesHttpServiceGroupConfigurer(
|
||||
ObjectProvider<SslBundles> sslBundles, HttpReactiveClientProperties httpReactiveClientProperties,
|
||||
ReactiveHttpClientServiceProperties serviceProperties,
|
||||
ObjectProvider<ClientHttpConnectorBuilder<?>> clientConnectorBuilder,
|
||||
ObjectProvider<ClientHttpConnectorSettings> clientConnectorSettings,
|
||||
ObjectProvider<ApiVersionInserter> apiVersionInserter,
|
||||
ObjectProvider<ApiVersionFormatter> apiVersionFormatter) {
|
||||
return new WebClientPropertiesHttpServiceGroupConfigurer(this.beanClassLoader, sslBundles,
|
||||
httpReactiveClientProperties, serviceProperties, clientConnectorBuilder, clientConnectorSettings,
|
||||
apiVersionInserter, apiVersionFormatter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebClientCustomizerHttpServiceGroupConfigurer webClientCustomizerHttpServiceGroupConfigurer(
|
||||
ObjectProvider<WebClientCustomizer> customizers) {
|
||||
return new WebClientCustomizerHttpServiceGroupConfigurer(customizers);
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.assertj.core.extractor.Extractors;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.http.client.HttpRedirects;
|
||||
import org.springframework.boot.http.client.autoconfigure.reactive.ClientHttpConnectorAutoConfiguration;
|
||||
|
@ -35,6 +36,7 @@ import org.springframework.boot.http.client.reactive.ClientHttpConnectorSettings
|
|||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.boot.webclient.WebClientCustomizer;
|
||||
import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration;
|
||||
import org.springframework.boot.webclient.autoconfigure.service.scan.TestHttpServiceClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
@ -137,6 +139,18 @@ class ReactiveHttpServiceClientAutoConfigurationTests {
|
|||
.run((context) -> assertThat(context).doesNotHaveBean(HttpServiceProxyRegistry.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerHttpServiceAnnotatedInterfacesInPackages() {
|
||||
this.contextRunner.withUserConfiguration(ScanConfiguration.class)
|
||||
.run((context) -> assertThat(context).hasSingleBean(TestHttpServiceClient.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasImportAnnotationDoesNotRegisterHttpServiceAnnotatedInterfacesInPackages() {
|
||||
this.contextRunner.withUserConfiguration(ScanConfiguration.class, HttpClientConfiguration.class)
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(TestHttpServiceClient.class));
|
||||
}
|
||||
|
||||
private HttpClient getJdkHttpClient(Object proxy) {
|
||||
return (HttpClient) Extractors.byName("builder.connector.httpClient").apply(getWebClient(proxy));
|
||||
}
|
||||
|
@ -206,6 +220,12 @@ class ReactiveHttpServiceClientAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigurationPackage(basePackageClasses = TestHttpServiceClient.class)
|
||||
static class ScanConfiguration {
|
||||
|
||||
}
|
||||
|
||||
interface TestClientOne {
|
||||
|
||||
@GetExchange("/hello")
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.webclient.autoconfigure.service.scan;
|
||||
|
||||
import org.springframework.web.service.annotation.GetExchange;
|
||||
import org.springframework.web.service.registry.HttpServiceClient;
|
||||
|
||||
/**
|
||||
* Test HTTP service used with scanning.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@HttpServiceClient("test")
|
||||
public interface TestHttpServiceClient {
|
||||
|
||||
@GetExchange("/hello")
|
||||
String hello();
|
||||
|
||||
}
|
Loading…
Reference in New Issue