(window.webpackJsonp=window.webpackJsonp||[]).push([[77],{501:function(e,t,n){"use strict";n.r(t);var a=n(56),i=Object(a.a)({},(function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[n("h1",{attrs:{id:"spring-cloud-openfeign"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#spring-cloud-openfeign"}},[e._v("#")]),e._v(" Spring Cloud OpenFeign")]),e._v(" "),n("h2",{attrs:{id:"_1-declarative-rest-client-feign"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-declarative-rest-client-feign"}},[e._v("#")]),e._v(" 1. Declarative REST Client: Feign")]),e._v(" "),n("p",[n("a",{attrs:{href:"https://github.com/OpenFeign/feign",target:"_blank",rel:"noopener noreferrer"}},[e._v("Feign"),n("OutboundLink")],1),e._v(" is a declarative web service client.\nIt makes writing web service clients easier.\nTo use Feign create an interface and annotate it.\nIt has pluggable annotation support including Feign annotations and JAX-RS annotations.\nFeign also supports pluggable encoders and decoders.\nSpring Cloud adds support for Spring MVC annotations and for using the same "),n("code",[e._v("HttpMessageConverters")]),e._v(" used by default in Spring Web.\nSpring Cloud integrates Eureka, Spring Cloud CircuitBreaker, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.")]),e._v(" "),n("h3",{attrs:{id:"_1-1-how-to-include-feign"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-1-how-to-include-feign"}},[e._v("#")]),e._v(" 1.1. How to Include Feign")]),e._v(" "),n("p",[e._v("To include Feign in your project use the starter with group "),n("code",[e._v("org.springframework.cloud")]),e._v("and artifact id "),n("code",[e._v("spring-cloud-starter-openfeign")]),e._v(". See the "),n("a",{attrs:{href:"https://projects.spring.io/spring-cloud/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Spring Cloud Project page"),n("OutboundLink")],1),e._v("for details on setting up your build system with the current Spring Cloud Release Train.")]),e._v(" "),n("p",[e._v("Example spring boot app")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("@SpringBootApplication\n@EnableFeignClients\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n}\n")])])]),n("p",[e._v("StoreClient.java")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient("stores")\npublic interface StoreClient {\n @RequestMapping(method = RequestMethod.GET, value = "/stores")\n List getStores();\n\n @RequestMapping(method = RequestMethod.GET, value = "/stores")\n Page getStores(Pageable pageable);\n\n @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")\n Store update(@PathVariable("storeId") Long storeId, Store store);\n\n @RequestMapping(method = RequestMethod.DELETE, value = "/stores/{storeId:\\\\d+}")\n void delete(@PathVariable Long storeId);\n}\n')])])]),n("p",[e._v("In the "),n("code",[e._v("@FeignClient")]),e._v(' annotation the String value ("stores" above) is an arbitrary client name, which is used to create a '),n("a",{attrs:{href:"https://github.com/spring-cloud/spring-cloud-commons/blob/main/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClient.java",target:"_blank",rel:"noopener noreferrer"}},[e._v("Spring Cloud LoadBalancer client"),n("OutboundLink")],1),e._v(".\nYou can also specify a URL using the "),n("code",[e._v("url")]),e._v(" attribute\n(absolute value or just a hostname). The name of the bean in the\napplication context is the fully qualified name of the interface.\nTo specify your own alias value you can use the "),n("code",[e._v("qualifiers")]),e._v(" value\nof the "),n("code",[e._v("@FeignClient")]),e._v(" annotation.")]),e._v(" "),n("p",[e._v('The load-balancer client above will want to discover the physical addresses\nfor the "stores" service. If your application is a Eureka client then\nit will resolve the service in the Eureka service registry. If you\ndon’t want to use Eureka, you can configure a list of servers\nin your external configuration using '),n("a",{attrs:{href:"https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#simplediscoveryclient",target:"_blank",rel:"noopener noreferrer"}},[n("code",[e._v("SimpleDiscoveryClient")]),n("OutboundLink")],1),e._v(".")]),e._v(" "),n("p",[e._v("Spring Cloud OpenFeign supports all the features available for the blocking mode of Spring Cloud LoadBalancer. You can read more about them in the "),n("a",{attrs:{href:"https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer",target:"_blank",rel:"noopener noreferrer"}},[e._v("project documentation"),n("OutboundLink")],1),e._v(".")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("To use "),n("code",[e._v("@EnableFeignClients")]),e._v(" annotation on "),n("code",[e._v("@Configuration")]),e._v("-annotated-classes, make sure to specify where the clients are located, for example:"),n("code",[e._v('@EnableFeignClients(basePackages = "com.example.clients")')]),e._v("or list them explicitly:"),n("code",[e._v("@EnableFeignClients(clients = InventoryServiceFeignClient.class)")])])])]),e._v(" "),n("tbody")]),e._v(" "),n("h3",{attrs:{id:"_1-2-overriding-feign-defaults"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-2-overriding-feign-defaults"}},[e._v("#")]),e._v(" 1.2. Overriding Feign Defaults")]),e._v(" "),n("p",[e._v("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 "),n("code",[e._v("@FeignClient")]),e._v(" annotation. Spring Cloud creates a new ensemble as an"),n("code",[e._v("ApplicationContext")]),e._v(" on demand for each named client using "),n("code",[e._v("FeignClientsConfiguration")]),e._v(". This contains (amongst other things) an "),n("code",[e._v("feign.Decoder")]),e._v(", a "),n("code",[e._v("feign.Encoder")]),e._v(", and a "),n("code",[e._v("feign.Contract")]),e._v(".\nIt is possible to override the name of that ensemble by using the "),n("code",[e._v("contextId")]),e._v("attribute of the "),n("code",[e._v("@FeignClient")]),e._v(" annotation.")]),e._v(" "),n("p",[e._v("Spring Cloud lets you take full control of the feign client by declaring additional configuration (on top of the "),n("code",[e._v("FeignClientsConfiguration")]),e._v(") using "),n("code",[e._v("@FeignClient")]),e._v(". Example:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient(name = "stores", configuration = FooConfiguration.class)\npublic interface StoreClient {\n //..\n}\n')])])]),n("p",[e._v("In this case the client is composed from the components already in "),n("code",[e._v("FeignClientsConfiguration")]),e._v(" together with any in "),n("code",[e._v("FooConfiguration")]),e._v(" (where the latter will override the former).")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[n("code",[e._v("FooConfiguration")]),e._v(" does not need to be annotated with "),n("code",[e._v("@Configuration")]),e._v(". However, if it is, then take care to exclude it from any "),n("code",[e._v("@ComponentScan")]),e._v(" that would otherwise include this configuration as it will become the default source for "),n("code",[e._v("feign.Decoder")]),e._v(", "),n("code",[e._v("feign.Encoder")]),e._v(", "),n("code",[e._v("feign.Contract")]),e._v(", etc., when specified. This can be avoided by putting it in a separate, non-overlapping package from any "),n("code",[e._v("@ComponentScan")]),e._v(" or "),n("code",[e._v("@SpringBootApplication")]),e._v(", or it can be explicitly excluded in "),n("code",[e._v("@ComponentScan")]),e._v(".")])])]),e._v(" "),n("tbody")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("Using "),n("code",[e._v("contextId")]),e._v(" attribute of the "),n("code",[e._v("@FeignClient")]),e._v(" annotation in addition to changing the name of"),n("br"),e._v("the "),n("code",[e._v("ApplicationContext")]),e._v(" ensemble, it will override the alias of the client name"),n("br"),e._v("and it will be used as part of the name of the configuration bean created for that client.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("Previously, using the "),n("code",[e._v("url")]),e._v(" attribute, did not require the "),n("code",[e._v("name")]),e._v(" attribute. Using "),n("code",[e._v("name")]),e._v(" is now required.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("Placeholders are supported in the "),n("code",[e._v("name")]),e._v(" and "),n("code",[e._v("url")]),e._v(" attributes.")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient(name = "${feign.name}", url = "${feign.url}")\npublic interface StoreClient {\n //..\n}\n')])])]),n("p",[e._v("Spring Cloud OpenFeign provides the following beans by default for feign ("),n("code",[e._v("BeanType")]),e._v(" beanName: "),n("code",[e._v("ClassName")]),e._v("):")]),e._v(" "),n("ul",[n("li",[n("p",[n("code",[e._v("Decoder")]),e._v(" feignDecoder: "),n("code",[e._v("ResponseEntityDecoder")]),e._v(" (which wraps a "),n("code",[e._v("SpringDecoder")]),e._v(")")])]),e._v(" "),n("li",[n("p",[n("code",[e._v("Encoder")]),e._v(" feignEncoder: "),n("code",[e._v("SpringEncoder")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("Logger")]),e._v(" feignLogger: "),n("code",[e._v("Slf4jLogger")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("MicrometerCapability")]),e._v(" micrometerCapability: If "),n("code",[e._v("feign-micrometer")]),e._v(" is on the classpath and "),n("code",[e._v("MeterRegistry")]),e._v(" is available")])]),e._v(" "),n("li",[n("p",[n("code",[e._v("CachingCapability")]),e._v(" cachingCapability: If "),n("code",[e._v("@EnableCaching")]),e._v(" annotation is used. Can be disabled via "),n("code",[e._v("feign.cache.enabled")]),e._v(".")])]),e._v(" "),n("li",[n("p",[n("code",[e._v("Contract")]),e._v(" feignContract: "),n("code",[e._v("SpringMvcContract")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("Feign.Builder")]),e._v(" feignBuilder: "),n("code",[e._v("FeignCircuitBreaker.Builder")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("Client")]),e._v(" feignClient: If Spring Cloud LoadBalancer is on the classpath, "),n("code",[e._v("FeignBlockingLoadBalancerClient")]),e._v(" is used.\nIf none of them is on the classpath, the default feign client is used.")])])]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[n("code",[e._v("spring-cloud-starter-openfeign")]),e._v(" supports "),n("code",[e._v("spring-cloud-starter-loadbalancer")]),e._v(". However, as is an optional dependency, you need to make sure it been added to your project if you want to use it.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("The OkHttpClient and ApacheHttpClient and ApacheHC5 feign clients can be used by setting "),n("code",[e._v("feign.okhttp.enabled")]),e._v(" or "),n("code",[e._v("feign.httpclient.enabled")]),e._v(" or "),n("code",[e._v("feign.httpclient.hc5.enabled")]),e._v(" to "),n("code",[e._v("true")]),e._v(", respectively, and having them on the classpath.\nYou can customize the HTTP client used by providing a bean of either "),n("code",[e._v("org.apache.http.impl.client.CloseableHttpClient")]),e._v(" when using Apache or "),n("code",[e._v("okhttp3.OkHttpClient")]),e._v(" when using OK HTTP or "),n("code",[e._v("org.apache.hc.client5.http.impl.classic.CloseableHttpClient")]),e._v(" when using Apache HC5.")]),e._v(" "),n("p",[e._v("Spring Cloud OpenFeign "),n("em",[e._v("does not")]),e._v(" 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:")]),e._v(" "),n("ul",[n("li",[n("p",[n("code",[e._v("Logger.Level")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("Retryer")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("ErrorDecoder")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("Request.Options")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("Collection")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("SetterFactory")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("QueryMapEncoder")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("Capability")]),e._v(" ("),n("code",[e._v("MicrometerCapability")]),e._v(" and "),n("code",[e._v("CachingCapability")]),e._v(" are provided by default)")])])]),e._v(" "),n("p",[e._v("A bean of "),n("code",[e._v("Retryer.NEVER_RETRY")]),e._v(" with the type "),n("code",[e._v("Retryer")]),e._v(" is created by default, which will disable retrying.\nNotice this retrying behavior is different from the Feign default one, where it will automatically retry IOExceptions,\ntreating them as transient network related exceptions, and any RetryableException thrown from an ErrorDecoder.")]),e._v(" "),n("p",[e._v("Creating a bean of one of those type and placing it in a "),n("code",[e._v("@FeignClient")]),e._v(" configuration (such as "),n("code",[e._v("FooConfiguration")]),e._v(" above) allows you to override each one of the beans described. Example:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@Configuration\npublic class FooConfiguration {\n @Bean\n public Contract feignContract() {\n return new feign.Contract.Default();\n }\n\n @Bean\n public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {\n return new BasicAuthRequestInterceptor("user", "password");\n }\n}\n')])])]),n("p",[e._v("This replaces the "),n("code",[e._v("SpringMvcContract")]),e._v(" with "),n("code",[e._v("feign.Contract.Default")]),e._v(" and adds a "),n("code",[e._v("RequestInterceptor")]),e._v(" to the collection of "),n("code",[e._v("RequestInterceptor")]),e._v(".")]),e._v(" "),n("p",[n("code",[e._v("@FeignClient")]),e._v(" also can be configured using configuration properties.")]),e._v(" "),n("p",[e._v("application.yml")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("feign:\n client:\n config:\n feignName:\n connectTimeout: 5000\n readTimeout: 5000\n loggerLevel: full\n errorDecoder: com.example.SimpleErrorDecoder\n retryer: com.example.SimpleRetryer\n defaultQueryParameters:\n query: queryValue\n defaultRequestHeaders:\n header: headerValue\n requestInterceptors:\n - com.example.FooRequestInterceptor\n - com.example.BarRequestInterceptor\n decode404: false\n encoder: com.example.SimpleEncoder\n decoder: com.example.SimpleDecoder\n contract: com.example.SimpleContract\n capabilities:\n - com.example.FooCapability\n - com.example.BarCapability\n queryMapEncoder: com.example.SimpleQueryMapEncoder\n metrics.enabled: false\n")])])]),n("p",[e._v("Default configurations can be specified in the "),n("code",[e._v("@EnableFeignClients")]),e._v(" attribute "),n("code",[e._v("defaultConfiguration")]),e._v(" in a similar manner as described above. The difference is that this configuration will apply to "),n("em",[e._v("all")]),e._v(" feign clients.")]),e._v(" "),n("p",[e._v("If you prefer using configuration properties to configured all "),n("code",[e._v("@FeignClient")]),e._v(", you can create configuration properties with "),n("code",[e._v("default")]),e._v(" feign name.")]),e._v(" "),n("p",[e._v("You can use "),n("code",[e._v("feign.client.config.feignName.defaultQueryParameters")]),e._v(" and "),n("code",[e._v("feign.client.config.feignName.defaultRequestHeaders")]),e._v(" to specify query parameters and headers that will be sent with every request of the client named "),n("code",[e._v("feignName")]),e._v(".")]),e._v(" "),n("p",[e._v("application.yml")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("feign:\n client:\n config:\n default:\n connectTimeout: 5000\n readTimeout: 5000\n loggerLevel: basic\n")])])]),n("p",[e._v("If we create both "),n("code",[e._v("@Configuration")]),e._v(" bean and configuration properties, configuration properties will win.\nIt will override "),n("code",[e._v("@Configuration")]),e._v(" values. But if you want to change the priority to "),n("code",[e._v("@Configuration")]),e._v(",\nyou can change "),n("code",[e._v("feign.client.default-to-properties")]),e._v(" to "),n("code",[e._v("false")]),e._v(".")]),e._v(" "),n("p",[e._v("If we want to create multiple feign clients with the same name or url\nso that they would point to the same server but each with a different custom configuration then\nwe have to use "),n("code",[e._v("contextId")]),e._v(" attribute of the "),n("code",[e._v("@FeignClient")]),e._v(" in order to avoid name\ncollision of these configuration beans.")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)\npublic interface FooClient {\n //..\n}\n')])])]),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)\npublic interface BarClient {\n //..\n}\n')])])]),n("p",[e._v("It is also possible to configure FeignClient not to inherit beans from the parent context.\nYou can do this by overriding the "),n("code",[e._v("inheritParentConfiguration()")]),e._v(" in a "),n("code",[e._v("FeignClientConfigurer")]),e._v("bean to return "),n("code",[e._v("false")]),e._v(":")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("@Configuration\npublic class CustomConfiguration{\n\n@Bean\npublic FeignClientConfigurer feignClientConfigurer() {\n return new FeignClientConfigurer() {\n\n @Override\n public boolean inheritParentConfiguration() {\n return false;\n }\n };\n\n }\n}\n")])])]),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("By default, Feign clients do not encode slash "),n("code",[e._v("/")]),e._v(" characters. You can change this behaviour, by setting the value of "),n("code",[e._v("feign.client.decodeSlash")]),e._v(" to "),n("code",[e._v("false")]),e._v(".")])])]),e._v(" "),n("tbody")]),e._v(" "),n("h4",{attrs:{id:"_1-2-1-springencoder-configuration"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-2-1-springencoder-configuration"}},[e._v("#")]),e._v(" 1.2.1. "),n("code",[e._v("SpringEncoder")]),e._v(" configuration")]),e._v(" "),n("p",[e._v("In the "),n("code",[e._v("SpringEncoder")]),e._v(" that we provide, we set "),n("code",[e._v("null")]),e._v(" charset for binary content types and "),n("code",[e._v("UTF-8")]),e._v(" for all the other ones.")]),e._v(" "),n("p",[e._v("You can modify this behaviour to derive the charset from the "),n("code",[e._v("Content-Type")]),e._v(" header charset instead by setting the value of "),n("code",[e._v("feign.encoder.charset-from-content-type")]),e._v(" to "),n("code",[e._v("true")]),e._v(".")]),e._v(" "),n("h3",{attrs:{id:"_1-3-timeout-handling"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-3-timeout-handling"}},[e._v("#")]),e._v(" 1.3. Timeout Handling")]),e._v(" "),n("p",[e._v("We can configure timeouts on both the default and the named client. OpenFeign works with two timeout parameters:")]),e._v(" "),n("ul",[n("li",[n("p",[n("code",[e._v("connectTimeout")]),e._v(" prevents blocking the caller due to the long server processing time.")])]),e._v(" "),n("li",[n("p",[n("code",[e._v("readTimeout")]),e._v(" is applied from the time of connection establishment and is triggered when returning the response takes too long.")])])]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("In case the server is not running or available a packet results in "),n("em",[e._v("connection refused")]),e._v(". The communication ends either with an error message or in a fallback. This can happen "),n("em",[e._v("before")]),e._v(" the "),n("code",[e._v("connectTimeout")]),e._v(" if it is set very low. The time taken to perform a lookup and to receive such a packet causes a significant part of this delay. It is subject to change based on the remote host that involves a DNS lookup.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("h3",{attrs:{id:"_1-4-creating-feign-clients-manually"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-4-creating-feign-clients-manually"}},[e._v("#")]),e._v(" 1.4. Creating Feign Clients Manually")]),e._v(" "),n("p",[e._v("In some cases it might be necessary to customize your Feign Clients in a way that is not\npossible using the methods above. In this case you can create Clients using the"),n("a",{attrs:{href:"https://github.com/OpenFeign/feign/#basics",target:"_blank",rel:"noopener noreferrer"}},[e._v("Feign Builder API"),n("OutboundLink")],1),e._v(". Below is an example\nwhich creates two Feign Clients with the same interface but configures each one with\na separate request interceptor.")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@Import(FeignClientsConfiguration.class)\nclass FooController {\n\n private FooClient fooClient;\n\n private FooClient adminClient;\n\n @Autowired\n public FooController(Client client, Encoder encoder, Decoder decoder, Contract contract, MicrometerCapability micrometerCapability) {\n this.fooClient = Feign.builder().client(client)\n .encoder(encoder)\n .decoder(decoder)\n .contract(contract)\n .addCapability(micrometerCapability)\n .requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))\n .target(FooClient.class, "https://PROD-SVC");\n\n this.adminClient = Feign.builder().client(client)\n .encoder(encoder)\n .decoder(decoder)\n .contract(contract)\n .addCapability(micrometerCapability)\n .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))\n .target(FooClient.class, "https://PROD-SVC");\n }\n}\n')])])]),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("In the above example "),n("code",[e._v("FeignClientsConfiguration.class")]),e._v(" is the default configuration"),n("br"),e._v("provided by Spring Cloud OpenFeign.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[n("code",[e._v("PROD-SVC")]),e._v(" is the name of the service the Clients will be making requests to.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("The Feign "),n("code",[e._v("Contract")]),e._v(" object defines what annotations and values are valid on interfaces. The"),n("br"),e._v("autowired "),n("code",[e._v("Contract")]),e._v(" bean provides supports for SpringMVC annotations, instead of"),n("br"),e._v("the default Feign native annotations.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("You can also use the "),n("code",[e._v("Builder")]),e._v("to configure FeignClient not to inherit beans from the parent context.\nYou can do this by overriding calling "),n("code",[e._v("inheritParentContext(false)")]),e._v(" on the "),n("code",[e._v("Builder")]),e._v(".")]),e._v(" "),n("h3",{attrs:{id:"_1-5-feign-spring-cloud-circuitbreaker-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-5-feign-spring-cloud-circuitbreaker-support"}},[e._v("#")]),e._v(" 1.5. Feign Spring Cloud CircuitBreaker Support")]),e._v(" "),n("p",[e._v("If Spring Cloud CircuitBreaker is on the classpath and "),n("code",[e._v("feign.circuitbreaker.enabled=true")]),e._v(", Feign will wrap all methods with a circuit breaker.")]),e._v(" "),n("p",[e._v("To disable Spring Cloud CircuitBreaker support on a per-client basis create a vanilla "),n("code",[e._v("Feign.Builder")]),e._v(' with the "prototype" scope, e.g.:')]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@Configuration\npublic class FooConfiguration {\n @Bean\n @Scope("prototype")\n public Feign.Builder feignBuilder() {\n return Feign.builder();\n }\n}\n')])])]),n("p",[e._v("The circuit breaker name follows this pattern "),n("code",[e._v("#()")]),e._v(". When calling a "),n("code",[e._v("@FeignClient")]),e._v(" with "),n("code",[e._v("FooClient")]),e._v(" interface and the called interface method that has no parameters is "),n("code",[e._v("bar")]),e._v(" then the circuit breaker name will be "),n("code",[e._v("FooClient#bar()")]),e._v(".")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("As of 2020.0.2, the circuit breaker name pattern has changed from "),n("code",[e._v("_")]),e._v("."),n("br"),e._v("Using "),n("code",[e._v("CircuitBreakerNameResolver")]),e._v(" introduced in 2020.0.4, circuit breaker names can retain the old pattern.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("Providing a bean of "),n("code",[e._v("CircuitBreakerNameResolver")]),e._v(", you can change the circuit breaker name pattern.")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@Configuration\npublic class FooConfiguration {\n @Bean\n public CircuitBreakerNameResolver circuitBreakerNameResolver() {\n return (String feignClientName, Target target, Method method) -> feignClientName + "_" + method.getName();\n }\n}\n')])])]),n("p",[e._v("To enable Spring Cloud CircuitBreaker group set the "),n("code",[e._v("feign.circuitbreaker.group.enabled")]),e._v(" property to "),n("code",[e._v("true")]),e._v(" (by default "),n("code",[e._v("false")]),e._v(").")]),e._v(" "),n("h3",{attrs:{id:"_1-6-feign-spring-cloud-circuitbreaker-fallbacks"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-6-feign-spring-cloud-circuitbreaker-fallbacks"}},[e._v("#")]),e._v(" 1.6. Feign Spring Cloud CircuitBreaker Fallbacks")]),e._v(" "),n("p",[e._v("Spring Cloud CircuitBreaker supports the notion of a fallback: a default code path that is executed when the circuit is open or there is an error. To enable fallbacks for a given "),n("code",[e._v("@FeignClient")]),e._v(" set the "),n("code",[e._v("fallback")]),e._v(" attribute to the class name that implements the fallback. You also need to declare your implementation as a Spring bean.")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class)\n protected interface TestClient {\n\n @RequestMapping(method = RequestMethod.GET, value = "/hello")\n Hello getHello();\n\n @RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")\n String getException();\n\n }\n\n @Component\n static class Fallback implements TestClient {\n\n @Override\n public Hello getHello() {\n throw new NoFallbackAvailableException("Boom!", new RuntimeException());\n }\n\n @Override\n public String getException() {\n return "Fixed response";\n }\n\n }\n')])])]),n("p",[e._v("If one needs access to the cause that made the fallback trigger, one can use the "),n("code",[e._v("fallbackFactory")]),e._v(" attribute inside "),n("code",[e._v("@FeignClient")]),e._v(".")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient(name = "testClientWithFactory", url = "http://localhost:${server.port}/",\n fallbackFactory = TestFallbackFactory.class)\n protected interface TestClientWithFactory {\n\n @RequestMapping(method = RequestMethod.GET, value = "/hello")\n Hello getHello();\n\n @RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")\n String getException();\n\n }\n\n @Component\n static class TestFallbackFactory implements FallbackFactory {\n\n @Override\n public FallbackWithFactory create(Throwable cause) {\n return new FallbackWithFactory();\n }\n\n }\n\n static class FallbackWithFactory implements TestClientWithFactory {\n\n @Override\n public Hello getHello() {\n throw new NoFallbackAvailableException("Boom!", new RuntimeException());\n }\n\n @Override\n public String getException() {\n return "Fixed response";\n }\n\n }\n')])])]),n("h3",{attrs:{id:"_1-7-feign-and-primary"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-7-feign-and-primary"}},[e._v("#")]),e._v(" 1.7. Feign and "),n("code",[e._v("@Primary")])]),e._v(" "),n("p",[e._v("When using Feign with Spring Cloud CircuitBreaker fallbacks, there are multiple beans in the "),n("code",[e._v("ApplicationContext")]),e._v(" of the same type. This will cause "),n("code",[e._v("@Autowired")]),e._v(" to not work because there isn’t exactly one bean, or one marked as primary. To work around this, Spring Cloud OpenFeign marks all Feign instances as "),n("code",[e._v("@Primary")]),e._v(", so Spring Framework will know which bean to inject. In some cases, this may not be desirable. To turn off this behavior set the "),n("code",[e._v("primary")]),e._v(" attribute of "),n("code",[e._v("@FeignClient")]),e._v(" to false.")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient(name = "hello", primary = false)\npublic interface HelloClient {\n // methods here\n}\n')])])]),n("h3",{attrs:{id:"_1-8-feign-inheritance-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-8-feign-inheritance-support"}},[e._v("#")]),e._v(" 1.8. Feign Inheritance Support")]),e._v(" "),n("p",[e._v("Feign supports boilerplate apis via single-inheritance interfaces.\nThis allows grouping common operations into convenient base interfaces.")]),e._v(" "),n("p",[e._v("UserService.java")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('public interface UserService {\n\n @RequestMapping(method = RequestMethod.GET, value ="/users/{id}")\n User getUser(@PathVariable("id") long id);\n}\n')])])]),n("p",[e._v("UserResource.java")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("@RestController\npublic class UserResource implements UserService {\n\n}\n")])])]),n("p",[e._v("UserClient.java")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('package project.user;\n\n@FeignClient("users")\npublic interface UserClient extends UserService {\n\n}\n')])])]),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[n("code",[e._v("@FeignClient")]),e._v(" interfaces should not be shared between server and client and annotating "),n("code",[e._v("@FeignClient")]),e._v(" interfaces with "),n("code",[e._v("@RequestMapping")]),e._v(" on class level is no longer supported.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("h3",{attrs:{id:"_1-9-feign-request-response-compression"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-9-feign-request-response-compression"}},[e._v("#")]),e._v(" 1.9. Feign request/response compression")]),e._v(" "),n("p",[e._v("You may consider enabling the request or response GZIP compression for your\nFeign requests. You can do this by enabling one of the properties:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("feign.compression.request.enabled=true\nfeign.compression.response.enabled=true\n")])])]),n("p",[e._v("Feign request compression gives you settings similar to what you may set for your web server:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("feign.compression.request.enabled=true\nfeign.compression.request.mime-types=text/xml,application/xml,application/json\nfeign.compression.request.min-request-size=2048\n")])])]),n("p",[e._v("These properties allow you to be selective about the compressed media types and minimum request threshold length.")]),e._v(" "),n("h3",{attrs:{id:"_1-10-feign-logging"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-10-feign-logging"}},[e._v("#")]),e._v(" 1.10. Feign logging")]),e._v(" "),n("p",[e._v("A logger is created for each Feign client created. By default the name of the logger is the full class name of the interface used to create the Feign client. Feign logging only responds to the "),n("code",[e._v("DEBUG")]),e._v(" level.")]),e._v(" "),n("p",[e._v("application.yml")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("logging.level.project.user.UserClient: DEBUG\n")])])]),n("p",[e._v("The "),n("code",[e._v("Logger.Level")]),e._v(" object that you may configure per client, tells Feign how much to log. Choices are:")]),e._v(" "),n("ul",[n("li",[n("p",[n("code",[e._v("NONE")]),e._v(", No logging ("),n("strong",[e._v("DEFAULT")]),e._v(").")])]),e._v(" "),n("li",[n("p",[n("code",[e._v("BASIC")]),e._v(", Log only the request method and URL and the response status code and execution time.")])]),e._v(" "),n("li",[n("p",[n("code",[e._v("HEADERS")]),e._v(", Log the basic information along with request and response headers.")])]),e._v(" "),n("li",[n("p",[n("code",[e._v("FULL")]),e._v(", Log the headers, body, and metadata for both requests and responses.")])])]),e._v(" "),n("p",[e._v("For example, the following would set the "),n("code",[e._v("Logger.Level")]),e._v(" to "),n("code",[e._v("FULL")]),e._v(":")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("@Configuration\npublic class FooConfiguration {\n @Bean\n Logger.Level feignLoggerLevel() {\n return Logger.Level.FULL;\n }\n}\n")])])]),n("h3",{attrs:{id:"_1-11-feign-capability-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-11-feign-capability-support"}},[e._v("#")]),e._v(" 1.11. Feign Capability support")]),e._v(" "),n("p",[e._v("The Feign capabilities expose core Feign components so that these components can be modified. For example, the capabilities can take the "),n("code",[e._v("Client")]),e._v(", "),n("em",[e._v("decorate")]),e._v(" it, and give the decorated instance back to Feign.\nThe support for metrics libraries is a good real-life example for this. See "),n("a",{attrs:{href:"#feign-metrics"}},[e._v("Feign metrics")]),e._v(".")]),e._v(" "),n("p",[e._v("Creating one or more "),n("code",[e._v("Capability")]),e._v(" beans and placing them in a "),n("code",[e._v("@FeignClient")]),e._v(" configuration lets you register them and modify the behavior of the involved client.")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("@Configuration\npublic class FooConfiguration {\n @Bean\n Capability customCapability() {\n return new CustomCapability();\n }\n}\n")])])]),n("h3",{attrs:{id:"_1-12-feign-metrics"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-12-feign-metrics"}},[e._v("#")]),e._v(" 1.12. Feign metrics")]),e._v(" "),n("p",[e._v("If all of the following conditions are true, a "),n("code",[e._v("MicrometerCapability")]),e._v(" bean is created and registered so that your Feign client publishes metrics to Micrometer:")]),e._v(" "),n("ul",[n("li",[n("p",[n("code",[e._v("feign-micrometer")]),e._v(" is on the classpath")])]),e._v(" "),n("li",[n("p",[e._v("A "),n("code",[e._v("MeterRegistry")]),e._v(" bean is available")])]),e._v(" "),n("li",[n("p",[e._v("feign metrics properties are set to "),n("code",[e._v("true")]),e._v(" (by default)")]),e._v(" "),n("ul",[n("li",[n("p",[n("code",[e._v("feign.metrics.enabled=true")]),e._v(" (for all clients)")])]),e._v(" "),n("li",[n("p",[n("code",[e._v("feign.client.config.feignName.metrics.enabled=true")]),e._v(" (for a single client)")])])])])]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("If your application already uses Micrometer, enabling metrics is as simple as putting "),n("code",[e._v("feign-micrometer")]),e._v(" onto your classpath.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("You can also disable the feature by either:")]),e._v(" "),n("ul",[n("li",[n("p",[e._v("excluding "),n("code",[e._v("feign-micrometer")]),e._v(" from your classpath")])]),e._v(" "),n("li",[n("p",[e._v("setting one of the feign metrics properties to "),n("code",[e._v("false")])]),e._v(" "),n("ul",[n("li",[n("p",[n("code",[e._v("feign.metrics.enabled=false")])])]),e._v(" "),n("li",[n("p",[n("code",[e._v("feign.client.config.feignName.metrics.enabled=false")])])])])])]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[n("code",[e._v("feign.metrics.enabled=false")]),e._v(" disables metrics support for "),n("strong",[e._v("all")]),e._v(" Feign clients regardless of the value of the client-level flags: "),n("code",[e._v("feign.client.config.feignName.metrics.enabled")]),e._v("."),n("br"),e._v("If you want to enable or disable merics per client, don’t set "),n("code",[e._v("feign.metrics.enabled")]),e._v(" and use "),n("code",[e._v("feign.client.config.feignName.metrics.enabled")]),e._v(".")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("You can also customize the "),n("code",[e._v("MicrometerCapability")]),e._v(" by registering your own bean:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("@Configuration\npublic class FooConfiguration {\n @Bean\n public MicrometerCapability micrometerCapability(MeterRegistry meterRegistry) {\n return new MicrometerCapability(meterRegistry);\n }\n}\n")])])]),n("h3",{attrs:{id:"_1-13-feign-caching"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-13-feign-caching"}},[e._v("#")]),e._v(" 1.13. Feign Caching")]),e._v(" "),n("p",[e._v("If "),n("code",[e._v("@EnableCaching")]),e._v(" annotation is used, a "),n("code",[e._v("CachingCapability")]),e._v(" bean is created and registered so that your Feign client recognizes "),n("code",[e._v("@Cache*")]),e._v(" annotations on its interface:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('public interface DemoClient {\n\n @GetMapping("/demo/{filterParam}")\n @Cacheable(cacheNames = "demo-cache", key = "#keyParam")\n String demoEndpoint(String keyParam, @PathVariable String filterParam);\n}\n')])])]),n("p",[e._v("You can also disable the feature via property "),n("code",[e._v("feign.cache.enabled=false")]),e._v(".")]),e._v(" "),n("h3",{attrs:{id:"_1-14-feign-querymap-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-14-feign-querymap-support"}},[e._v("#")]),e._v(" 1.14. Feign @QueryMap support")]),e._v(" "),n("p",[e._v("The OpenFeign "),n("code",[e._v("@QueryMap")]),e._v(" annotation provides support for POJOs to be used as\nGET parameter maps. Unfortunately, the default OpenFeign QueryMap annotation is\nincompatible with Spring because it lacks a "),n("code",[e._v("value")]),e._v(" property.")]),e._v(" "),n("p",[e._v("Spring Cloud OpenFeign provides an equivalent "),n("code",[e._v("@SpringQueryMap")]),e._v(" annotation, which\nis used to annotate a POJO or Map parameter as a query parameter map.")]),e._v(" "),n("p",[e._v("For example, the "),n("code",[e._v("Params")]),e._v(" class defines parameters "),n("code",[e._v("param1")]),e._v(" and "),n("code",[e._v("param2")]),e._v(":")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("// Params.java\npublic class Params {\n private String param1;\n private String param2;\n\n // [Getters and setters omitted for brevity]\n}\n")])])]),n("p",[e._v("The following feign client uses the "),n("code",[e._v("Params")]),e._v(" class by using the "),n("code",[e._v("@SpringQueryMap")]),e._v(" annotation:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient("demo")\npublic interface DemoTemplate {\n\n @GetMapping(path = "/demo")\n String demoEndpoint(@SpringQueryMap Params params);\n}\n')])])]),n("p",[e._v("If you need more control over the generated query parameter map, you can implement a custom "),n("code",[e._v("QueryMapEncoder")]),e._v(" bean.")]),e._v(" "),n("h3",{attrs:{id:"_1-15-hateoas-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-15-hateoas-support"}},[e._v("#")]),e._v(" 1.15. HATEOAS support")]),e._v(" "),n("p",[e._v("Spring provides some APIs to create REST representations that follow the "),n("a",{attrs:{href:"https://en.wikipedia.org/wiki/HATEOAS",target:"_blank",rel:"noopener noreferrer"}},[e._v("HATEOAS"),n("OutboundLink")],1),e._v(" principle, "),n("a",{attrs:{href:"https://spring.io/projects/spring-hateoas",target:"_blank",rel:"noopener noreferrer"}},[e._v("Spring Hateoas"),n("OutboundLink")],1),e._v(" and "),n("a",{attrs:{href:"https://spring.io/projects/spring-data-rest",target:"_blank",rel:"noopener noreferrer"}},[e._v("Spring Data REST"),n("OutboundLink")],1),e._v(".")]),e._v(" "),n("p",[e._v("If your project use the "),n("code",[e._v("org.springframework.boot:spring-boot-starter-hateoas")]),e._v(" starter\nor the "),n("code",[e._v("org.springframework.boot:spring-boot-starter-data-rest")]),e._v(" starter, Feign HATEOAS support is enabled by default.")]),e._v(" "),n("p",[e._v("When HATEOAS support is enabled, Feign clients are allowed to serialize\nand deserialize HATEOAS representation models: "),n("a",{attrs:{href:"https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/EntityModel.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("EntityModel"),n("OutboundLink")],1),e._v(", "),n("a",{attrs:{href:"https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/CollectionModel.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("CollectionModel"),n("OutboundLink")],1),e._v(" and "),n("a",{attrs:{href:"https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/PagedModel.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("PagedModel"),n("OutboundLink")],1),e._v(".")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient("demo")\npublic interface DemoTemplate {\n\n @GetMapping(path = "/stores")\n CollectionModel getStores();\n}\n')])])]),n("h3",{attrs:{id:"_1-16-spring-matrixvariable-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-16-spring-matrixvariable-support"}},[e._v("#")]),e._v(" 1.16. Spring @MatrixVariable Support")]),e._v(" "),n("p",[e._v("Spring Cloud OpenFeign provides support for the Spring "),n("code",[e._v("@MatrixVariable")]),e._v(" annotation.")]),e._v(" "),n("p",[e._v("If a map is passed as the method argument, the "),n("code",[e._v("@MatrixVariable")]),e._v(" path segment is created by joining key-value pairs from the map with a "),n("code",[e._v("=")]),e._v(".")]),e._v(" "),n("p",[e._v("If a different object is passed, either the "),n("code",[e._v("name")]),e._v(" provided in the "),n("code",[e._v("@MatrixVariable")]),e._v(" annotation (if defined) or the annotated variable name is\njoined with the provided method argument using "),n("code",[e._v("=")]),e._v(".")]),e._v(" "),n("p",[e._v("IMPORTANT")]),e._v(" "),n("p",[e._v("Even though, on the server side, Spring does not require the users to name the path segment placeholder same as the matrix variable name, since it would be too ambiguous on the client side, Spring Cloud OpenFeign requires that you add a path segment placeholder with a name matching either the "),n("code",[e._v("name")]),e._v(" provided in the "),n("code",[e._v("@MatrixVariable")]),e._v(" annotation (if defined) or the annotated variable name.")]),e._v(" "),n("p",[e._v("For example:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@GetMapping("/objects/links/{matrixVars}")\nMap> getObjects(@MatrixVariable Map> matrixVars);\n')])])]),n("p",[e._v("Note that both variable name and the path segment placeholder are called "),n("code",[e._v("matrixVars")]),e._v(".")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient("demo")\npublic interface DemoTemplate {\n\n @GetMapping(path = "/stores")\n CollectionModel getStores();\n}\n')])])]),n("h3",{attrs:{id:"_1-17-feign-collectionformat-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-17-feign-collectionformat-support"}},[e._v("#")]),e._v(" 1.17. Feign "),n("code",[e._v("CollectionFormat")]),e._v(" support")]),e._v(" "),n("p",[e._v("We support "),n("code",[e._v("feign.CollectionFormat")]),e._v(" by providing the "),n("code",[e._v("@CollectionFormat")]),e._v(" annotation.\nYou can annotate a Feign client method (or the whole class to affect all methods) with it by passing the desired "),n("code",[e._v("feign.CollectionFormat")]),e._v(" as annotation value.")]),e._v(" "),n("p",[e._v("In the following example, the "),n("code",[e._v("CSV")]),e._v(" format is used instead of the default "),n("code",[e._v("EXPLODED")]),e._v(" to process the method.")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@FeignClient(name = "demo")\nprotected interface PageableFeignClient {\n\n @CollectionFormat(feign.CollectionFormat.CSV)\n @GetMapping(path = "/page")\n ResponseEntity performRequest(Pageable page);\n\n}\n')])])]),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("Set the "),n("code",[e._v("CSV")]),e._v(" format while sending "),n("code",[e._v("Pageable")]),e._v(" as a query parameter in order for it to be encoded correctly.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("h3",{attrs:{id:"_1-18-reactive-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-18-reactive-support"}},[e._v("#")]),e._v(" 1.18. Reactive Support")]),e._v(" "),n("p",[e._v("As the "),n("a",{attrs:{href:"https://github.com/OpenFeign/feign",target:"_blank",rel:"noopener noreferrer"}},[e._v("OpenFeign project"),n("OutboundLink")],1),e._v(" does not currently support reactive clients, such as "),n("a",{attrs:{href:"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("Spring WebClient"),n("OutboundLink")],1),e._v(", neither does Spring Cloud OpenFeign.We will add support for it here as soon as it becomes available in the core project.")]),e._v(" "),n("p",[e._v("Until that is done, we recommend using "),n("a",{attrs:{href:"https://github.com/Playtika/feign-reactive",target:"_blank",rel:"noopener noreferrer"}},[e._v("feign-reactive"),n("OutboundLink")],1),e._v(" for Spring WebClient support.")]),e._v(" "),n("h4",{attrs:{id:"_1-18-1-early-initialization-errors"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-18-1-early-initialization-errors"}},[e._v("#")]),e._v(" 1.18.1. Early Initialization Errors")]),e._v(" "),n("p",[e._v("Depending on how you are using your Feign clients you may see initialization errors when starting your application.\nTo work around this problem you can use an "),n("code",[e._v("ObjectProvider")]),e._v(" when autowiring your client.")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("@Autowired\nObjectProvider testFeignClient;\n")])])]),n("h3",{attrs:{id:"_1-19-spring-data-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-19-spring-data-support"}},[e._v("#")]),e._v(" 1.19. Spring Data Support")]),e._v(" "),n("p",[e._v("You may consider enabling Jackson Modules for the support "),n("code",[e._v("org.springframework.data.domain.Page")]),e._v(" and "),n("code",[e._v("org.springframework.data.domain.Sort")]),e._v(" decoding.")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("feign.autoconfiguration.jackson.enabled=true\n")])])]),n("h3",{attrs:{id:"_1-20-spring-refreshscope-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-20-spring-refreshscope-support"}},[e._v("#")]),e._v(" 1.20. Spring "),n("code",[e._v("@RefreshScope")]),e._v(" Support")]),e._v(" "),n("p",[e._v("If Feign client refresh is enabled, each feign client is created with "),n("code",[e._v("feign.Request.Options")]),e._v(" as a refresh-scoped bean. This means properties such as "),n("code",[e._v("connectTimeout")]),e._v(" and "),n("code",[e._v("readTimeout")]),e._v(" can be refreshed against any Feign client instance through "),n("code",[e._v("POST /actuator/refresh")]),e._v(".")]),e._v(" "),n("p",[e._v("By default, refresh behavior in Feign clients is disabled. Use the following property to enable refresh behavior:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("feign.client.refresh-enabled=true\n")])])]),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("DO NOT annotate the "),n("code",[e._v("@FeignClient")]),e._v(" interface with the "),n("code",[e._v("@RefreshScope")]),e._v(" annotation.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("h3",{attrs:{id:"_1-21-oauth2-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_1-21-oauth2-support"}},[e._v("#")]),e._v(" 1.21. OAuth2 Support")]),e._v(" "),n("p",[e._v("OAuth2 support can be enabled by setting following flag:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("feign.oauth2.enabled=true\n")])])]),n("p",[e._v("When the flag is set to true, and the oauth2 client context resource details are present, a bean of class "),n("code",[e._v("OAuth2FeignRequestInterceptor")]),e._v(" is created. Before each request, the interceptor resolves the required access token and includes it as a header.\nSometimes, when load balancing is enabled for Feign clients, you may want to use load balancing for fetching access tokens, too. To do so, you should ensure that the load balancer is on the classpath (spring-cloud-starter-loadbalancer) and explicitly enable load balancing for OAuth2FeignRequestInterceptor by setting the following flag:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("feign.oauth2.load-balanced=true\n")])])]),n("h2",{attrs:{id:"_2-configuration-properties"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#_2-configuration-properties"}},[e._v("#")]),e._v(" 2. Configuration properties")]),e._v(" "),n("p",[e._v("To see the list of all Spring Cloud OpenFeign related configuration properties please check "),n("RouterLink",{attrs:{to:"/en/spring-cloud/appendix.html"}},[e._v("the Appendix page")]),e._v(".")],1)])}),[],!1,null,null,null);t.default=i.exports}}]);