Allow overriding default feign beans.
Similar to @RibbonClient fixes gh-288
This commit is contained in:
@@ -767,6 +767,67 @@ don't want to use Eureka, you can simply configure a list of servers
|
||||
in your external configuration (see
|
||||
<<spring-cloud-ribbon-without-eureka,above for example>>).
|
||||
|
||||
[[spring-cloud-feign-overriding-defaults]]
|
||||
=== Overriding Feign Defaults
|
||||
|
||||
A central concept in Spring Cloud's Feign support is that of the named client. Each feign client is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer using the `@FeignClient` annotation. Spring Cloud creates a new ensemble as an
|
||||
`ApplicationContext` on demand for each named client using `FeignClientsConfiguration`. This contains (amongst other things) an `feign.Decoder`, a `feign.Encoder`, and a `feign.Contract`.
|
||||
|
||||
Spring Cloud lets you take full control of the feign client by declaring additional configuration (on top of the `FeignClientsConfiguration`) using `@FeignClient`. Example:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@FeignClient(name = "stores", configuration = FooConfiguration.class)
|
||||
public interface StoreClient {
|
||||
//..
|
||||
}
|
||||
----
|
||||
|
||||
In this case the client is composed from the components already in `FeignClientsConfiguration` together with any in `FooConfiguration` (where the latter will override the former).
|
||||
|
||||
WARNING: The `FooConfiguration` has to be `@Configuration` but take care that it is not in a `@ComponentScan` for the main application context, otherwise it will be shared by all the `@FeignClient`s. If you use `@ComponentScan` (or `@SpringBootApplication`) you need to take steps to avoid it being included (for instance put it in a separate, non-overlapping package, or specify the packages to scan explicitly in the `@ComponentScan`).
|
||||
|
||||
NOTE: The `serviceId` attribute is now deprecated in favor of the `name` attribute.
|
||||
|
||||
WARNING: Previously, using the `url` attribute, did not require the `name` attribute. Using `name` is now required.
|
||||
|
||||
Spring Cloud Netflix provides the following beans by default for feign (`BeanType` beanName: `ClassName`):
|
||||
|
||||
* `Decoder` feignDecoder: `ResponseEntityDecoder` (which wraps a `SpringDecoder`)
|
||||
* `Encoder` feignEncoder: `SpringEncoder`
|
||||
* `Logger` feignLogger: `Slf4jLogger`
|
||||
* `Contract` feignContract: `SpringMvcContract`
|
||||
|
||||
Spring Cloud Netflix _does not_ provide the following beans by default for feign, but still looks up beans of these types from the application context to create the feign client:
|
||||
|
||||
* `Logger.Level`
|
||||
* `Retryer`
|
||||
* `ErrorDecoder`
|
||||
* `Request.Options`
|
||||
* `Collection<RequestInterceptor>`
|
||||
|
||||
Creating a bean of one of those type and placing it in a `@FeignClient` configuration (such as `FooConfiguration` above) allows you to override each one of the beans described. Example:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Configuration
|
||||
public class FooConfiguration {
|
||||
@Bean
|
||||
public Contract feignContractg() {
|
||||
return new feign.Contract.Default();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
|
||||
return new BasicAuthRequestInterceptor("user", "password");
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
This replaces the `SpringMvcContract` with `feign.Contract.Default` and adds a `RequestInterceptor` to the collection of `RequestInterceptor`.
|
||||
|
||||
Default configurations can be specified in the `@EnableFeignClients` attribute `defaultConfiguration` in a similar manner as described above. The difference is that this configuration will apply to _all_ feign clients.
|
||||
|
||||
[[spring-cloud-feign-inheritance]]
|
||||
=== Feign Inheritance Support
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ import org.springframework.context.annotation.Import;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import({ FeignClientsConfiguration.class, FeignClientsRegistrar.class })
|
||||
@Import(FeignClientsRegistrar.class)
|
||||
public @interface EnableFeignClients {
|
||||
|
||||
/**
|
||||
@@ -71,4 +71,18 @@ public @interface EnableFeignClients {
|
||||
*/
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
|
||||
/**
|
||||
* A custom <code>@Configuration</code> for all feign clients. Can contain override
|
||||
* <code>@Bean</code> definition for the pieces that make up the client, for instance
|
||||
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
|
||||
*
|
||||
* @see FeignClientsConfiguration for the defaults
|
||||
*/
|
||||
Class<?>[] defaultConfiguration() default {};
|
||||
|
||||
/**
|
||||
* List of classes annotated with @FeignClient. If not empty, disables classpath scanning.
|
||||
* @return
|
||||
*/
|
||||
Class<?>[] clients() default {};
|
||||
}
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import feign.Feign;
|
||||
|
||||
@@ -32,12 +33,20 @@ import feign.Feign;
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(Feign.class)
|
||||
@AutoConfigureAfter(ArchaiusAutoConfiguration.class)
|
||||
@Import(FeignClientsConfiguration.class)
|
||||
public class FeignAutoConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<FeignClientSpecification> configurations = new ArrayList<>();
|
||||
|
||||
@Bean
|
||||
public HasFeatures feignFeature() {
|
||||
return HasFeatures.namedFeature("Feign", Feign.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FeignClientFactory feignClientFactory() {
|
||||
FeignClientFactory factory = new FeignClientFactory();
|
||||
factory.setConfigurations(this.configurations);
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* Annotation for interfaces declaring that a REST client with that interface should be
|
||||
* created (e.g. for autowiring into another component). If ribbon is available it will be
|
||||
@@ -39,17 +41,30 @@ public @interface FeignClient {
|
||||
* The serviceId with optional protocol prefix. Synonym for {@link #serviceId()
|
||||
* serviceId}. Either serviceId or url must be specified but not both.
|
||||
*/
|
||||
@AliasFor("name")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* The serviceId with optional protocol prefix. Synonym for {@link #value() value}.
|
||||
* Either serviceId or url must be specified but not both.
|
||||
*/
|
||||
@Deprecated
|
||||
String serviceId() default "";
|
||||
|
||||
@AliasFor("value")
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* An absolute URL or resolvable hostname (the protocol is optional).
|
||||
*/
|
||||
String url() default "";
|
||||
|
||||
/**
|
||||
* A custom <code>@Configuration</code> for the feign client. Can contain override
|
||||
* <code>@Bean</code> definition for the pieces that make up the client, for instance
|
||||
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
|
||||
*
|
||||
* @see FeignClientsConfiguration for the defaults
|
||||
*/
|
||||
Class<?>[] configuration() default {};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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
|
||||
*
|
||||
* http://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.cloud.netflix.feign;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
|
||||
/**
|
||||
* A factory that creates client, load balancer and client configuration instances. It
|
||||
* creates a Spring ApplicationContext per client name, and extracts the beans that it
|
||||
* needs from there.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class FeignClientFactory implements DisposableBean, ApplicationContextAware {
|
||||
|
||||
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
|
||||
|
||||
private Map<String, FeignClientSpecification> configurations = new ConcurrentHashMap<>();
|
||||
|
||||
private ApplicationContext parent;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext parent) throws BeansException {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void setConfigurations(List<FeignClientSpecification> configurations) {
|
||||
for (FeignClientSpecification client : configurations) {
|
||||
this.configurations.put(client.getName(), client);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
Collection<AnnotationConfigApplicationContext> values = this.contexts.values();
|
||||
this.contexts.clear();
|
||||
for (AnnotationConfigApplicationContext context : values) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotationConfigApplicationContext getContext(String name) {
|
||||
if (!this.contexts.containsKey(name)) {
|
||||
synchronized (this.contexts) {
|
||||
if (!this.contexts.containsKey(name)) {
|
||||
this.contexts.put(name, createContext(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.contexts.get(name);
|
||||
}
|
||||
|
||||
private AnnotationConfigApplicationContext createContext(String name) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
if (this.configurations.containsKey(name)) {
|
||||
for (Class<?> configuration : this.configurations.get(name)
|
||||
.getConfiguration()) {
|
||||
context.register(configuration);
|
||||
}
|
||||
}
|
||||
for (Entry<String, FeignClientSpecification> entry : this.configurations
|
||||
.entrySet()) {
|
||||
if (entry.getKey().startsWith("default.")) {
|
||||
for (Class<?> configuration : entry.getValue().getConfiguration()) {
|
||||
context.register(configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
context.register(PropertyPlaceholderAutoConfiguration.class,
|
||||
FeignClientsConfiguration.class);
|
||||
context.getEnvironment().getPropertySources()
|
||||
.addFirst(new MapPropertySource("feign",
|
||||
Collections.<String, Object> singletonMap(
|
||||
"feign.client.name", name)));
|
||||
if (this.parent != null) {
|
||||
// Uses Environment from parent as well as beans
|
||||
context.setParent(this.parent);
|
||||
}
|
||||
context.refresh();
|
||||
return context;
|
||||
}
|
||||
|
||||
public <C> C getInstance(String name, Class<C> type) {
|
||||
AnnotationConfigApplicationContext context = getContext(name);
|
||||
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0) {
|
||||
return context.getBean(type);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import lombok.EqualsAndHashCode;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -59,10 +58,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, A
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (StringUtils.hasText(this.name)) {
|
||||
Assert.state(!StringUtils.hasText(this.url),
|
||||
"Either value or url can be specified, but not both");
|
||||
}
|
||||
Assert.hasText(this.name, "Name must be set");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,30 +66,30 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, A
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
protected Feign.Builder feign() {
|
||||
protected Feign.Builder feign(FeignClientFactory factory) {
|
||||
// @formatter:off
|
||||
Feign.Builder builder = Feign.builder()
|
||||
// required values
|
||||
.logger(get(Logger.class))
|
||||
.encoder(get(Encoder.class))
|
||||
.decoder(get(Decoder.class))
|
||||
.contract(get(Contract.class));
|
||||
.logger(get(factory, Logger.class))
|
||||
.encoder(get(factory, Encoder.class))
|
||||
.decoder(get(factory, Decoder.class))
|
||||
.contract(get(factory, Contract.class));
|
||||
// @formatter:on
|
||||
|
||||
// optional values
|
||||
Logger.Level level = getOptional(Logger.Level.class);
|
||||
Logger.Level level = getOptional(factory, Logger.Level.class);
|
||||
if (level != null) {
|
||||
builder.logLevel(level);
|
||||
}
|
||||
Retryer retryer = getOptional(Retryer.class);
|
||||
Retryer retryer = getOptional(factory, Retryer.class);
|
||||
if (retryer != null) {
|
||||
builder.retryer(retryer);
|
||||
}
|
||||
ErrorDecoder errorDecoder = getOptional(ErrorDecoder.class);
|
||||
ErrorDecoder errorDecoder = getOptional(factory, ErrorDecoder.class);
|
||||
if (errorDecoder != null) {
|
||||
builder.errorDecoder(errorDecoder);
|
||||
}
|
||||
Request.Options options = getOptional(Request.Options.class);
|
||||
Request.Options options = getOptional(factory, Request.Options.class);
|
||||
if (options != null) {
|
||||
builder.options(options);
|
||||
}
|
||||
@@ -105,24 +101,23 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, A
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected <T> T get(Class<T> type) {
|
||||
return this.context.getBean(type);
|
||||
}
|
||||
|
||||
protected <T> T getOptional(Class<T> type) {
|
||||
try {
|
||||
return this.context.getBean(type);
|
||||
} catch (NoSuchBeanDefinitionException e) {
|
||||
//ignore
|
||||
protected <T> T get(FeignClientFactory factory, Class<T> type) {
|
||||
T instance = factory.getInstance(this.name, type);
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException("No bean found of type " + type + " for " + this.name);
|
||||
}
|
||||
return null;
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Feign.Builder builder, Class<T> type, String schemeName) {
|
||||
protected <T> T getOptional(FeignClientFactory factory, Class<T> type) {
|
||||
return factory.getInstance(this.name, type);
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Feign.Builder builder, FeignClientFactory factory, Class<T> type, String url) {
|
||||
builder.logger(new Slf4jLogger(type)); // TODO: how to have choice here?
|
||||
Client client = getOptional(Client.class);
|
||||
Client client = getOptional(factory, Client.class);
|
||||
if (client != null) {
|
||||
return builder.client(client).target(type, schemeName);
|
||||
return builder.client(client).target(type, url);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-ribbon?");
|
||||
@@ -130,16 +125,21 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, A
|
||||
|
||||
@Override
|
||||
public Object getObject() throws Exception {
|
||||
if (StringUtils.hasText(this.name) && !this.name.startsWith("http")) {
|
||||
this.name = "http://" + this.name;
|
||||
}
|
||||
if (StringUtils.hasText(this.name)) {
|
||||
return loadBalance(feign(), this.type, this.name);
|
||||
FeignClientFactory factory = context.getBean(FeignClientFactory.class);
|
||||
|
||||
if (!StringUtils.hasText(this.url)) {
|
||||
String url;
|
||||
if (!this.name.startsWith("http")) {
|
||||
url = "http://" + this.name;
|
||||
} else {
|
||||
url = this.name;
|
||||
}
|
||||
return loadBalance(feign(factory), factory, this.type, url);
|
||||
}
|
||||
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
|
||||
this.url = "http://" + this.url;
|
||||
}
|
||||
return feign().target(this.type, this.url);
|
||||
return feign(factory).target(this.type, this.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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
|
||||
*
|
||||
* http://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.cloud.netflix.feign;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FeignClientSpecification {
|
||||
|
||||
private String name;
|
||||
|
||||
private Class<?>[] configuration;
|
||||
|
||||
}
|
||||
@@ -47,21 +47,25 @@ public class FeignClientsConfiguration {
|
||||
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Decoder feignDecoder() {
|
||||
return new ResponseEntityDecoder(new SpringDecoder(messageConverters));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Encoder feignEncoder() {
|
||||
return new SpringEncoder(messageConverters);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Logger feignLogger() {
|
||||
return new Slf4jLogger();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Contract feignContract() {
|
||||
return new SpringMvcContract();
|
||||
}
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -36,7 +39,12 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.ClassMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AbstractClassTestingTypeFilter;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -49,6 +57,7 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
ResourceLoaderAware, BeanClassLoaderAware {
|
||||
|
||||
// patterned after Spring Integration IntegrationComponentScanRegistrar
|
||||
// and RibbonClientsConfigurationRegistgrar
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@@ -68,15 +77,59 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
|
||||
public void registerBeanDefinitions(AnnotationMetadata metadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
registerDefaultConfiguration(metadata, registry);
|
||||
registerFeignClients(metadata, registry);
|
||||
}
|
||||
|
||||
Set<String> basePackages = getBasePackages(importingClassMetadata);
|
||||
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
|
||||
Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(
|
||||
EnableFeignClients.class.getName(), true);
|
||||
|
||||
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
|
||||
String name;
|
||||
if (metadata.hasEnclosingClass()) {
|
||||
name = "default." + metadata.getEnclosingClassName();
|
||||
} else {
|
||||
name = "default." + metadata.getClassName();
|
||||
}
|
||||
registerClientConfiguration(registry, name,
|
||||
defaultAttrs.get("defaultConfiguration"));
|
||||
}
|
||||
}
|
||||
|
||||
public void registerFeignClients(AnnotationMetadata metadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
ClassPathScanningCandidateComponentProvider scanner = getScanner();
|
||||
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
|
||||
scanner.setResourceLoader(this.resourceLoader);
|
||||
|
||||
Set<String> basePackages;
|
||||
|
||||
Map<String, Object> attrs = metadata.getAnnotationAttributes(
|
||||
EnableFeignClients.class.getName());
|
||||
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
|
||||
if (attrs != null && attrs.containsKey("clients")) {
|
||||
final Class<?>[] clients = (Class<?>[]) attrs.get("clients");
|
||||
final Set<String> clientClasses = new HashSet<>();
|
||||
basePackages = new HashSet<>();
|
||||
for (Class<?> clazz : clients) {
|
||||
basePackages.add(ClassUtils.getPackageName(clazz));
|
||||
clientClasses.add(clazz.getCanonicalName());
|
||||
}
|
||||
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
|
||||
@Override
|
||||
protected boolean match(ClassMetadata metadata) {
|
||||
String cleaned = metadata.getClassName().replaceAll("\\$", ".");
|
||||
return clientClasses.contains(cleaned);
|
||||
}
|
||||
};
|
||||
scanner.addIncludeFilter(new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
|
||||
} else {
|
||||
scanner.addIncludeFilter(annotationTypeFilter);
|
||||
basePackages = getBasePackages(metadata);
|
||||
}
|
||||
|
||||
for (String basePackage : basePackages) {
|
||||
Set<BeanDefinition> candidateComponents = scanner
|
||||
.findCandidateComponents(basePackage);
|
||||
@@ -88,18 +141,21 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
Assert.isTrue(annotationMetadata.isInterface(),
|
||||
"@FeignClient can only be specified on an interface");
|
||||
|
||||
BeanDefinitionHolder holder = createBeanDefinition(annotationMetadata);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
|
||||
Map<String, Object> attributes = annotationMetadata
|
||||
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
|
||||
|
||||
String name = getClientName(attributes);
|
||||
registerClientConfiguration(registry, name, attributes.get("configuration"));
|
||||
|
||||
registerFeignClient(registry, annotationMetadata, attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BeanDefinitionHolder createBeanDefinition(
|
||||
AnnotationMetadata annotationMetadata) {
|
||||
Map<String, Object> attributes = annotationMetadata
|
||||
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
|
||||
|
||||
private void registerFeignClient( BeanDefinitionRegistry registry,
|
||||
AnnotationMetadata annotationMetadata,
|
||||
Map<String, Object> attributes) {
|
||||
String className = annotationMetadata.getClassName();
|
||||
BeanDefinitionBuilder definition = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(FeignClientFactoryBean.class);
|
||||
@@ -111,7 +167,8 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
|
||||
String beanName = StringUtils.uncapitalize(className.substring(className
|
||||
.lastIndexOf(".") + 1));
|
||||
return new BeanDefinitionHolder(definition.getBeanDefinition(), beanName);
|
||||
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition.getBeanDefinition(), beanName);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
|
||||
}
|
||||
|
||||
private void validate(Map<String, Object> attributes) {
|
||||
@@ -123,6 +180,9 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
|
||||
private String getServiceId(Map<String, Object> attributes) {
|
||||
String name = (String) attributes.get("serviceId");
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = (String) attributes.get("name");
|
||||
}
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = (String) attributes.get("value");
|
||||
}
|
||||
@@ -153,10 +213,10 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
if (beanDefinition.getMetadata().isInterface()
|
||||
&& beanDefinition.getMetadata().getInterfaceNames().length == 1
|
||||
&& Annotation.class.getName().equals(
|
||||
beanDefinition.getMetadata().getInterfaceNames()[0])) {
|
||||
beanDefinition.getMetadata().getInterfaceNames()[0])) {
|
||||
try {
|
||||
Class<?> target = ClassUtils.forName(beanDefinition
|
||||
.getMetadata().getClassName(),
|
||||
.getMetadata().getClassName(),
|
||||
FeignClientsRegistrar.this.classLoader);
|
||||
return !target.isAnnotation();
|
||||
}
|
||||
@@ -200,4 +260,68 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
return basePackages;
|
||||
}
|
||||
|
||||
private String getClientName(Map<String, Object> client) {
|
||||
if (client == null) {
|
||||
return null;
|
||||
}
|
||||
String value = (String) client.get("value");
|
||||
if (!StringUtils.hasText(value)) {
|
||||
value = (String) client.get("name");
|
||||
}
|
||||
if (!StringUtils.hasText(value)) {
|
||||
value = (String) client.get("serviceId");
|
||||
}
|
||||
if (StringUtils.hasText(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
throw new IllegalStateException(
|
||||
"Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName());
|
||||
}
|
||||
|
||||
private void registerClientConfiguration(BeanDefinitionRegistry registry,
|
||||
Object name, Object configuration) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(FeignClientSpecification.class);
|
||||
builder.addConstructorArgValue(name);
|
||||
builder.addConstructorArgValue(configuration);
|
||||
registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
|
||||
builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to create a {@link TypeFilter} that matches if all the delegates match.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class AllTypeFilter implements TypeFilter {
|
||||
|
||||
private final List<TypeFilter> delegates;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AllTypeFilter} to match if all the given delegates match.
|
||||
*
|
||||
* @param delegates must not be {@literal null}.
|
||||
*/
|
||||
public AllTypeFilter(List<TypeFilter> delegates) {
|
||||
|
||||
Assert.notNull(delegates);
|
||||
this.delegates = delegates;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.type.filter.TypeFilter#match(org.springframework.core.type.classreading.MetadataReader, org.springframework.core.type.classreading.MetadataReaderFactory)
|
||||
*/
|
||||
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
|
||||
|
||||
for (TypeFilter filter : delegates) {
|
||||
if (!filter.match(metadataReader, metadataReaderFactory)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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
|
||||
*
|
||||
* http://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.cloud.netflix.feign;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Logger;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.slf4j.Slf4jLogger;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringEncoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringMvcContract;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = EnableFeignClientsTests.PlainConfiguration.class)
|
||||
@DirtiesContext
|
||||
public class EnableFeignClientsTests {
|
||||
|
||||
@Autowired
|
||||
private FeignClientFactory factory;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void decoderDefaultCorrect() {
|
||||
ResponseEntityDecoder.class.cast(this.factory.getInstance("foo", Decoder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encoderDefaultCorrect() {
|
||||
SpringEncoder.class.cast(this.factory.getInstance("foo", Encoder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loggerDefaultCorrect() {
|
||||
Slf4jLogger.class.cast(this.factory.getInstance("foo", Logger.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contractDefaultCorrect() {
|
||||
SpringMvcContract.class.cast(this.factory.getInstance("foo", Contract.class));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, FeignAutoConfiguration.class })
|
||||
protected static class PlainConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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
|
||||
*
|
||||
* http://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.cloud.netflix.feign;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class FeignClientFactoryTests {
|
||||
|
||||
@Test
|
||||
public void testChildContexts() {
|
||||
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
|
||||
parent.refresh();
|
||||
FeignClientFactory factory = new FeignClientFactory();
|
||||
factory.setApplicationContext(parent);
|
||||
factory.setConfigurations(Arrays.asList(getSpec("foo", FooConfig.class),
|
||||
getSpec("bar", BarConfig.class)));
|
||||
|
||||
Foo foo = factory.getInstance("foo", Foo.class);
|
||||
assertThat("foo was null", foo, is(notNullValue()));
|
||||
|
||||
Bar bar = factory.getInstance("bar", Bar.class);
|
||||
assertThat("bar was null", bar, is(notNullValue()));
|
||||
|
||||
Bar foobar = factory.getInstance("foo", Bar.class);
|
||||
assertThat("bar was not null", foobar, is(nullValue()));
|
||||
}
|
||||
|
||||
private FeignClientSpecification getSpec(String name, Class<?> configClass) {
|
||||
return new FeignClientSpecification(name, new Class[]{configClass});
|
||||
}
|
||||
|
||||
static class FooConfig {
|
||||
@Bean
|
||||
Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
}
|
||||
static class Foo{}
|
||||
|
||||
static class BarConfig {
|
||||
@Bean
|
||||
Bar bar() {
|
||||
return new Bar();
|
||||
}
|
||||
}
|
||||
static class Bar{}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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
|
||||
*
|
||||
* http://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.cloud.netflix.feign;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Logger;
|
||||
import feign.Request;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.Retryer;
|
||||
import feign.auth.BasicAuthRequestInterceptor;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import feign.slf4j.Slf4jLogger;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringEncoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringMvcContract;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = FeignClientOverrideDefaultsTests.TestConfiguration.class)
|
||||
@DirtiesContext
|
||||
public class FeignClientOverrideDefaultsTests {
|
||||
|
||||
@Autowired
|
||||
private FeignClientFactory factory;
|
||||
|
||||
@Test
|
||||
public void overrideDecoder() {
|
||||
Decoder.Default.class.cast(factory.getInstance("foo", Decoder.class));
|
||||
ResponseEntityDecoder.class.cast(factory.getInstance("bar", Decoder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideEncoder() {
|
||||
Encoder.Default.class.cast(factory.getInstance("foo", Encoder.class));
|
||||
SpringEncoder.class.cast(factory.getInstance("bar", Encoder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideLogger() {
|
||||
Logger.JavaLogger.class.cast(factory.getInstance("foo", Logger.class));
|
||||
Slf4jLogger.class.cast(factory.getInstance("bar", Logger.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideContract() {
|
||||
Contract.Default.class.cast(factory.getInstance("foo", Contract.class));
|
||||
SpringMvcContract.class.cast(factory.getInstance("bar", Contract.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideLoggerLevel() {
|
||||
assertNull(factory.getInstance("foo", Logger.Level.class));
|
||||
assertEquals(Logger.Level.HEADERS, factory.getInstance("bar", Logger.Level.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideRetryer() {
|
||||
assertNull(factory.getInstance("foo", Retryer.class));
|
||||
Retryer.Default.class.cast(factory.getInstance("bar", Retryer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideErrorDecoder() {
|
||||
assertNull(factory.getInstance("foo", ErrorDecoder.class));
|
||||
ErrorDecoder.Default.class.cast(factory.getInstance("bar", ErrorDecoder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideRequestOptions() {
|
||||
assertNull(factory.getInstance("foo", Request.Options.class));
|
||||
Request.Options options = factory.getInstance("bar", Request.Options.class);
|
||||
assertEquals(1, options.connectTimeoutMillis());
|
||||
assertEquals(1, options.readTimeoutMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addRequestInterceptor() {
|
||||
assertNull(factory.getInstance("foo", RequestInterceptor.class));
|
||||
BasicAuthRequestInterceptor.class.cast(factory.getInstance("bar", RequestInterceptor.class));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableFeignClients(clients = {FooClient.class, BarClient.class})
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, FeignAutoConfiguration.class})
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
@FeignClient(value = "foo", configuration = FooConfiguration.class)
|
||||
interface FooClient{
|
||||
@RequestMapping("/")
|
||||
String get();
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class FooConfiguration {
|
||||
@Bean
|
||||
public Decoder feignDecoder() {
|
||||
return new Decoder.Default();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Encoder feignEncoder() {
|
||||
return new Encoder.Default();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Logger feignLogger() {
|
||||
return new Logger.JavaLogger();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Contract feignContract() {
|
||||
return new Contract.Default();
|
||||
}
|
||||
}
|
||||
|
||||
@FeignClient(value = "bar", configuration = BarConfiguration.class)
|
||||
interface BarClient{
|
||||
@RequestMapping("/")
|
||||
String get();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class BarConfiguration {
|
||||
@Bean
|
||||
Logger.Level feignLevel() {
|
||||
return Logger.Level.HEADERS;
|
||||
}
|
||||
|
||||
@Bean
|
||||
Retryer feignRetryer() {
|
||||
return new Retryer.Default();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ErrorDecoder feignErrorDecoder() {
|
||||
return new ErrorDecoder.Default();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Request.Options feignRequestOptions() {
|
||||
return new Request.Options(1, 1);
|
||||
}
|
||||
|
||||
@Bean
|
||||
RequestInterceptor feignRequestInterceptor() {
|
||||
return new BasicAuthRequestInterceptor("user", "pass");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import lombok.NoArgsConstructor;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
@@ -54,11 +55,18 @@ import static org.junit.Assert.assertNotNull;
|
||||
@DirtiesContext
|
||||
public class SpringDecoderTests extends FeignClientFactoryBean {
|
||||
|
||||
@Autowired
|
||||
FeignClientFactory factory;
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
public SpringDecoderTests() {
|
||||
setName("test");
|
||||
}
|
||||
|
||||
public TestClient testClient() {
|
||||
return feign().target(TestClient.class, "http://localhost:" + this.port);
|
||||
return feign(factory).target(TestClient.class, "http://localhost:" + this.port);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -75,7 +75,7 @@ public class Demo {
|
||||
|
||||
}
|
||||
|
||||
@EnableFeignClients
|
||||
@EnableFeignClients(clients = InvoiceClient.class)
|
||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
@ComponentScan("org.springframework.cloud.netflix.feign.encoding.app")
|
||||
@EnableAutoConfiguration
|
||||
|
||||
@@ -72,7 +72,7 @@ public class FeignAcceptEncodingTest {
|
||||
|
||||
}
|
||||
|
||||
@EnableFeignClients
|
||||
@EnableFeignClients(clients = InvoiceClient.class)
|
||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
@ComponentScan("org.springframework.cloud.netflix.feign.encoding.app")
|
||||
@EnableAutoConfiguration
|
||||
|
||||
@@ -75,7 +75,7 @@ public class FeignContentEncodingTest {
|
||||
|
||||
}
|
||||
|
||||
@EnableFeignClients
|
||||
@EnableFeignClients(clients = InvoiceClient.class)
|
||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
@ComponentScan("org.springframework.cloud.netflix.feign.encoding.app")
|
||||
@EnableAutoConfiguration
|
||||
|
||||
@@ -20,9 +20,11 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
@@ -46,7 +48,8 @@ public class FeignClientValidationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableFeignClients()
|
||||
@Import(FeignAutoConfiguration.class)
|
||||
@EnableFeignClients(clients = BadConfiguration.Client.class)
|
||||
protected static class BadConfiguration {
|
||||
|
||||
@FeignClient("foo_bar")
|
||||
|
||||
@@ -83,7 +83,7 @@ public class FeignRibbonClientRetryTests {
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients
|
||||
@EnableFeignClients(clients = TestClient.class)
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
public static class Application {
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ public class FeignClientTests {
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients
|
||||
@EnableFeignClients(clients = {TestClientServiceId.class, TestClient.class})
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
protected static class Application {
|
||||
|
||||
|
||||
@@ -18,11 +18,13 @@ package org.springframework.cloud.netflix.feign.valid;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
@@ -42,10 +44,11 @@ public class FeignClientValidationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableFeignClients
|
||||
@Import(FeignAutoConfiguration.class)
|
||||
@EnableFeignClients(clients = GoodUrlConfiguration.Client.class)
|
||||
protected static class GoodUrlConfiguration {
|
||||
|
||||
@FeignClient(url="http://example.com")
|
||||
@FeignClient(name="example", url="http://example.com")
|
||||
interface Client {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/")
|
||||
@Deprecated
|
||||
@@ -65,7 +68,8 @@ public class FeignClientValidationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableFeignClients
|
||||
@Import(FeignAutoConfiguration.class)
|
||||
@EnableFeignClients(clients = GoodServiceIdConfiguration.Client.class)
|
||||
protected static class GoodServiceIdConfiguration {
|
||||
|
||||
@FeignClient("foo")
|
||||
|
||||
@@ -100,7 +100,7 @@ public class FeignHttpClientTests {
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients
|
||||
@EnableFeignClients(clients = {TestClient.class, UserClient.class})
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
protected static class Application implements UserService {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user