Spring Cloud 声明式REST客户端 OpenFeign -- 2. 覆盖Feign缺省值

关于Spring Cloud Feign,一个核心概念是命名客户端(named client)。每个feign client可以被理解成是一整套组件的一部分,这套组件一块工作,按需跟远程服务器发生联系,这整套组件有一个名字,就是应用开发人员通过@FeignClient所指定的名字。Spring Cloud根据FeignClientsConfiguration配置,针对每一个命名的feign客户端,将它的这整套组件创建为一个ApplicationContext实例存在。这里面包含了一个feign.Decoder,一个feign.Encoder和一个feign.Contract。并且使用注解@FeignClient的属性contextId可以覆盖这整套组件的名称。

通过使用@FeignClient注解属性configuration增加额外的配置,Spring Cloud允许你完全控制feign客户端。例子如下 :


// 这里 FooConfiguration 会叠加在缺省 FeignClientsConfiguration 之上
// 对 feign client stores 生效
@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
    //..
}

该例子中的feign客户端会结合考虑FeignClientsConfiguration配置和自定义配置FooConfiguration最终生成,并且FooConfiguration配置中的定义优先级高。

注意 : FooConfiguration不必要使用注解@Configuration。不过如果FooConfiguration使用了注解@Configuration,则注意把它从@ComponentScan中排除掉,要不然该配置会被@ComponentScan包含进来,这样它就会变成feign.Decoder,feign.Encoder,feign.Contract等组件的缺省来源。想避免FooConfiguration@ComponentScan看到,可以将它放到一个独立的,跟任何@ComponentScan/@SpringBootApplication可见包都不重叠的包中。或者将它明确地从@ComponentScan注解中排除。

注意 : serviceId属性现在已经不建议使用了,推荐使用name

注意 : @FeignClient注解属性contextId可以用于修改整个套组ApplicationContext的名字,它会覆盖客户端名称的别名,也会被用作该feign客户端配置bean名称的一部分。

注意 : 以前使用url属性,不需要name属性。现在必须要使用name属性。

@FeignClientname/url属性值可以使用占位符,如下所示:


@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
    //..
}

Spring Cloud Netflixfeign客户端缺省提供以下bean组件:

BeanType beanName ClassName
Decoder feignDecoder ResponseEntityDecoder (包了一个SpringDecoder)
Encoder feignEncoder SpringEncoder
Logger feignLogger Slf4jLogger
Contract feignContract SpringMvcContract
Feign.Builder feignBuilder HystrixFeign.Builder
Client feignClient 如果启用了Ribbon会是LoadBalancerFeignClient,否则会使用缺省的client

OkHttpClient或者ApacheHttpClient 放在classpath中,并且相应设置feign.okhttp.enabled 或者feign.httpclient.enabledtrue, 那就会使用相应的这些HTTP客户端。想定制HTTP客户端的话,使用ApacheHttpClient时,你可以提供一个类型为ClosableHttpClientbean;使用OK HTTP的话,提供一个类型为OkHttpClientbean

缺省情况下,Spring Cloud Netflix没有给feign提供以下bean组件,但是它仍然会从应用上下文中查找这些bean组件用于创建feign客户端 (这里其实给开发人员提供了定制feign客户端的机会):

  • Logger.Level
  • Retryer
  • ErrorDecoder
  • Request.Options
  • Collection
  • SetterFactory

@FeignClient配置(比如上面提到的配置FooConfiguration)中定义这里描述的任何一种类型的bean,你就可以覆盖框架缺省提供的这种类型的bean。比如 :

@Configuration
public class FooConfiguration {
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }

    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("user", "password");
    }
}

通过该例子,开发人员指定使用feign.Contract.Default而不再使用Spring Cloud缺省的SpringMvcContract。并且开发人员往RequestInterceptor集合中增加了一个自定义的RequestInterceptor:BasicAuthRequestInterceptor

@FeignClient也可以通过配置文件配置 , 如下例子所示 :

application.yml配置文件内容 :

feign:
  client:
    config:
      feignName:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        errorDecoder: com.example.SimpleErrorDecoder
        retryer: com.example.SimpleRetryer
        requestInterceptors:
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        decode404: false
        encoder: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        contract: com.example.SimpleContract

通过注解@EnableFeignClients的属性defaultConfiguration可以为所有feign客户端提供缺省配置,配置方式跟上面提到的方式类似。区别是缺省属性是面向所有feign客户端的。

如果你喜欢使用配置文件配置所有@FeignClient,你可以使用缺省配置属性,也就是feign客户端名称为default,例子如下所示 :

application.yml配置文件内容 :

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic

如果你既定义了@Configuration配置类bean又使用了配置属性,配置属性会被优先使用,它会覆盖@Configuration配置类bean中的值。但是如果你想指定使用@Configuration,可以设置feign.client.default-to-propertiesfalse

如果你想在RequestInterceptor中使用ThreadLocal绑定变量,你需要将Hystrix的线程隔离策略设置为SEMAPHORE或者在Feign中禁用Hystrix

application.yml`配置文件内容 :


# 禁用Feign中的Hystrix:
feign:
  hystrix:
    enabled: false

# 设置线程隔离级别为SEMAPHORE:
hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: SEMAPHORE

如果你想创建多个同名或者url相同的feign客户端以确保它们指向同一个服务器但是又要它们使用不同的配置,那么我们必须使用不同的contextId以避免这些配置bean的名字冲突。例子如下所示 :

@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {
    //..
}
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
public interface BarClient {
    //..
}

你可能感兴趣的:(Spring,cloud)