Clarify and document some Feign stuff
This commit is contained in:
@@ -226,7 +226,7 @@ exclude it manually, e.g. in Maven
|
||||
|
||||
The Eureka server does not have a backend store, but the service
|
||||
instances in the registry all have to send heartbeats to keep their
|
||||
resistrations up to date (so this can be done in memory). Clients also
|
||||
registrations up to date (so this can be done in memory). Clients also
|
||||
have an in-memory cache of eureka registrations (so they don't have to
|
||||
go to the registry for every single request to a service).
|
||||
|
||||
@@ -481,7 +481,7 @@ configured server list, and you can supply the configuration like this
|
||||
----
|
||||
stores:
|
||||
ribbon:
|
||||
listOfClients: example.com,google.com
|
||||
listOfServers: example.com,google.com
|
||||
----
|
||||
|
||||
[[spring-cloud-ribbon]]
|
||||
|
||||
@@ -16,17 +16,33 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
import feign.Request;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.Retryer;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import feign.ribbon.LoadBalancingTarget;
|
||||
import feign.slf4j.Slf4jLogger;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
class FeignClientFactoryBean extends FeignConfiguration implements FactoryBean<Object> {
|
||||
class FeignClientFactoryBean implements FactoryBean<Object> {
|
||||
|
||||
private boolean loadbalance;
|
||||
|
||||
@@ -34,6 +50,76 @@ class FeignClientFactoryBean extends FeignConfiguration implements FactoryBean<O
|
||||
|
||||
private String schemeName;
|
||||
|
||||
@Autowired
|
||||
private Decoder decoder;
|
||||
|
||||
@Autowired
|
||||
private Encoder encoder;
|
||||
|
||||
@Autowired
|
||||
private Logger logger;
|
||||
|
||||
@Autowired
|
||||
private Contract contract;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Logger.Level logLevel;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Retryer retryer;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ErrorDecoder errorDecoder;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Request.Options options;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Client ribbonClient;
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<RequestInterceptor> requestInterceptors;
|
||||
|
||||
protected Feign.Builder feign() {
|
||||
Feign.Builder builder = Feign.builder()
|
||||
// required values
|
||||
.logger(this.logger).encoder(this.encoder).decoder(this.decoder)
|
||||
.contract(this.contract);
|
||||
|
||||
// optional values
|
||||
if (this.logLevel != null) {
|
||||
builder.logLevel(this.logLevel);
|
||||
}
|
||||
if (this.retryer != null) {
|
||||
builder.retryer(this.retryer);
|
||||
}
|
||||
if (this.errorDecoder != null) {
|
||||
builder.errorDecoder(this.errorDecoder);
|
||||
}
|
||||
if (this.options != null) {
|
||||
builder.options(this.options);
|
||||
}
|
||||
if (this.requestInterceptors != null) {
|
||||
builder.requestInterceptors(this.requestInterceptors);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Class<T> type, String schemeName) {
|
||||
return loadBalance(feign(), type, schemeName);
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Feign.Builder builder, Class<T> type, String schemeName) {
|
||||
builder.logger(new Slf4jLogger(type)); // TODO: how to have choice here?
|
||||
if (this.ribbonClient != null) {
|
||||
return builder.client(this.ribbonClient).target(type, schemeName);
|
||||
}
|
||||
else {
|
||||
return builder.target(LoadBalancingTarget.create(type, schemeName));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject() throws Exception {
|
||||
if (!this.schemeName.startsWith("http")) {
|
||||
|
||||
@@ -41,8 +41,8 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class FeignClientScanRegistrar extends FeignConfiguration implements
|
||||
ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware {
|
||||
public class FeignClientScanRegistrar implements ImportBeanDefinitionRegistrar,
|
||||
ResourceLoaderAware, BeanClassLoaderAware {
|
||||
|
||||
// patterned after Spring Integration IntegrationComponentScanRegistrar
|
||||
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.netflix.archaius.ConfigurableEnvironmentConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import feign.*;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import feign.ribbon.LoadBalancingTarget;
|
||||
import feign.slf4j.Slf4jLogger;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
public class FeignConfiguration {
|
||||
|
||||
@Autowired
|
||||
ConfigurableEnvironmentConfiguration envConfig; // FIXME: howto enforce this?
|
||||
|
||||
@Autowired
|
||||
private Decoder decoder;
|
||||
|
||||
@Autowired
|
||||
private Encoder encoder;
|
||||
|
||||
@Autowired
|
||||
private Logger logger;
|
||||
|
||||
@Autowired
|
||||
private Contract contract;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Logger.Level logLevel;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Retryer retryer;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ErrorDecoder errorDecoder;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Request.Options options;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Client ribbonClient;
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<RequestInterceptor> requestInterceptors;
|
||||
|
||||
protected Feign.Builder feign() {
|
||||
Feign.Builder builder = Feign.builder()
|
||||
// required values
|
||||
.logger(this.logger).encoder(this.encoder).decoder(this.decoder)
|
||||
.contract(this.contract);
|
||||
|
||||
// optional values
|
||||
if (this.logLevel != null) {
|
||||
builder.logLevel(this.logLevel);
|
||||
}
|
||||
if (this.retryer != null) {
|
||||
builder.retryer(this.retryer);
|
||||
}
|
||||
if (this.errorDecoder != null) {
|
||||
builder.errorDecoder(this.errorDecoder);
|
||||
}
|
||||
if (this.options != null) {
|
||||
builder.options(this.options);
|
||||
}
|
||||
if (this.requestInterceptors != null) {
|
||||
builder.requestInterceptors(requestInterceptors);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Class<T> type, String schemeName) {
|
||||
return loadBalance(feign(), type, schemeName);
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Feign.Builder builder, Class<T> type, String schemeName) {
|
||||
builder.logger(new Slf4jLogger(type)); // TODO: how to have choice here?
|
||||
if (this.ribbonClient != null) {
|
||||
return builder.client(this.ribbonClient).target(type, schemeName);
|
||||
}
|
||||
else {
|
||||
return builder.target(LoadBalancingTarget.create(type, schemeName));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,15 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.IRule;
|
||||
import com.netflix.loadbalancer.ServerListFilter;
|
||||
|
||||
/**
|
||||
* Declarative configuration for a ribbon client. Add this annotation to any
|
||||
* <code>@Configuration</code> and then inject a {@link SpringClientFactory} to access the
|
||||
* client that is created.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@@ -35,10 +43,26 @@ import org.springframework.context.annotation.Import;
|
||||
@Documented
|
||||
public @interface RibbonClient {
|
||||
|
||||
/**
|
||||
* Synonym for name (the name of the client)
|
||||
*
|
||||
* @see #name()
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* The name of the ribbon client, uniquely identifying a set of client resources,
|
||||
* including a load balancer.
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* A custom <code>@Configuration</code> for the ribbon client. Can contain override
|
||||
* <code>@Bean</code> definition for the pieces that make up the client, for instance
|
||||
* {@link ILoadBalancer}, {@link ServerListFilter}, {@link IRule}.
|
||||
*
|
||||
* @see RibbonClientConfiguration for the defaults
|
||||
*/
|
||||
Class<?>[] configuration() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.netflix.feign;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
@@ -55,7 +52,9 @@ import com.netflix.loadbalancer.Server;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -65,7 +64,7 @@ import static org.junit.Assert.*;
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest({ "server.port=0", "spring.application.name=feignclienttest" })
|
||||
@DirtiesContext
|
||||
public class FeignClientTests extends FeignConfiguration {
|
||||
public class FeignClientTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
@@ -169,24 +168,26 @@ public class FeignClientTests extends FeignConfiguration {
|
||||
|
||||
@Test
|
||||
public void testSimpleType() {
|
||||
Hello hello = testClient.getHello();
|
||||
Hello hello = this.testClient.getHello();
|
||||
assertNotNull("hello was null", hello);
|
||||
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericType() {
|
||||
List<Hello> hellos = testClient.getHellos();
|
||||
List<Hello> hellos = this.testClient.getHellos();
|
||||
assertNotNull("hellos was null", hellos);
|
||||
assertEquals("hellos didn't match", hellos, getHelloList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestInterceptors() {
|
||||
List<String> headers = testClient.getHelloHeaders();
|
||||
List<String> headers = this.testClient.getHelloHeaders();
|
||||
assertNotNull("headers was null", headers);
|
||||
assertTrue("headers didn't contain myheader1value", headers.contains("myheader1value"));
|
||||
assertTrue("headers didn't contain myheader2value", headers.contains("myheader2value"));
|
||||
assertTrue("headers didn't contain myheader1value",
|
||||
headers.contains("myheader1value"));
|
||||
assertTrue("headers didn't contain myheader2value",
|
||||
headers.contains("myheader2value"));
|
||||
}
|
||||
|
||||
@Data
|
||||
@@ -207,7 +208,7 @@ class LocalRibbonClientConfiguration {
|
||||
@Bean
|
||||
public ILoadBalancer ribbonLoadBalancer() {
|
||||
BaseLoadBalancer balancer = new BaseLoadBalancer();
|
||||
balancer.setServersList(Arrays.asList(new Server("localhost", port)));
|
||||
balancer.setServersList(Arrays.asList(new Server("localhost", this.port)));
|
||||
return balancer;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ import static org.junit.Assert.assertNotNull;
|
||||
@IntegrationTest({ "server.port=0", "spring.application.name=springdecodertest",
|
||||
"spring.jmx.enabled=true" })
|
||||
@DirtiesContext
|
||||
public class SpringDecoderTests extends FeignConfiguration {
|
||||
public class SpringDecoderTests extends FeignClientFactoryBean {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
Reference in New Issue
Block a user