Skip to content

Spring Cloud OpenFeign Basic#

What Is Feign?#

  • Feign is a declarative web service client. It makes writing web service clients easier. To use Feign, just create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders.
  • Feign uses tools like Jersey and CXF to write java clients for REST or SOAP services. Furthermore, Feign allows you to write your own code on top of http libraries such as Apache HC. Feign connects your code to http APIs with minimal overhead and code via customizable decoders and error handling, which can be written to any text-based http API.
  • Feign works by processing annotations into a templatized request. Arguments are applied to these templates in a straightforward fashion before output. Although Feign is limited to supporting text-based APIs, it dramatically simplifies system aspects such as replaying requests. Furthermore, Feign makes it easy to unit test your conversions knowing this.
  • Feign 10.x and above are built on Java 8 and should work on Java 9, 10, and 11. For those that need JDK 6 compatibility, please use Feign 9.x
  • By default Feign will use it's client which is built based on Java Http Client and annotations to define Contract between the interface and how the underlying client should work. Feign's default contract defines the following annotations:
Annotation Interface Target Usage
@RequestLine Method Defines the HttpMethod and UriTemplate for request. Expressions, values wrapped in curly-braces {expression} are resolved using their corresponding @Param annotated parameters.
@Param Parameter Defines a template variable, whose value will be used to resolve the corresponding template Expression, by name provided as annotation value. If value is missing it will try to get the name from bytecode method parameter name (if the code was compiled with -parameters flag).
@Headers Method Type Defines a HeaderTemplate; a variation on a UriTemplate. that uses @Param annotated values to resolve the corresponding Expressions. When used on a Type, the template will be applied to every request. When used on a Method, the template will apply only to the annotated method.
@QueryMap Parameter Defines a Map of name-value pairs, or POJO, to expand into a query string.
@HeaderMap Parameter Defines a Map of name-value pairs, to expand into Http Headers
@Body Method Defines a Template, similar to a UriTemplate and HeaderTemplate, that uses @Param annotated values to resolve the corresponding Expressions.

What Is The Spring Cloud OpenFeign?#

  • Spring Cloud Openfeign provides OpenFeign integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms.
  • Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. Spring Cloud integrates Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign. Basically, the dependency spring-cloud-starter-openfeign that we use in Spring Boot applications is built based on the Feign-core with a lot of powerful features. You can look at the dependency tree below to see all dependencies wrapped in spring-cloud-starter-openfeign.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
+- org.springframework.cloud:spring-cloud-starter-openfeign:jar:3.1.0:compile
|  +- org.springframework.cloud:spring-cloud-starter:jar:3.1.0:compile
|  |  +- org.springframework.cloud:spring-cloud-context:jar:3.1.0:compile
|  |  \- org.springframework.security:spring-security-rsa:jar:1.0.10.RELEASE:compile
|  |     \- org.bouncycastle:bcpkix-jdk15on:jar:1.68:compile
|  |        \- org.bouncycastle:bcprov-jdk15on:jar:1.68:compile
|  +- org.springframework.cloud:spring-cloud-openfeign-core:jar:3.1.0:compile
|  |  +- org.springframework.boot:spring-boot-starter-aop:jar:2.6.1:compile
|  |  |  \- org.aspectj:aspectjweaver:jar:1.9.7:compile
|  |  \- io.github.openfeign.form:feign-form-spring:jar:3.8.0:compile
|  |     +- io.github.openfeign.form:feign-form:jar:3.8.0:compile
|  |     \- commons-fileupload:commons-fileupload:jar:1.4:compile
|  +- org.springframework.cloud:spring-cloud-commons:jar:3.1.0:compile
|  |  \- org.springframework.security:spring-security-crypto:jar:5.6.0:compile
|  +- io.github.openfeign:feign-core:jar:11.7:compile
|  \- io.github.openfeign:feign-slf4j:jar:11.7:compile
  • Spring Cloud OpenFeign provides the following beans by default for feign:
BeanType BeanName ClassName
Decoder feignDecoder SpringDecoder
Encoder feignEncoder SpringEncoder
Logger feignLogger Slf4jLogger
MicrometerCapability micrometerCapability If feign-micrometer is on the classpath and MeterRegistry is available
CachingCapability cachingCapability If @EnableCaching annotation is used. Can be disabled via feign.cache.enabled.
Contract feignContract SpringMvcContract
Feign.Builder feignBuilder FeignCircuitBreaker.Builder
Client feignClient If Spring Cloud LoadBalancer is on the classpath, FeignBlockingLoadBalancerClient is used. If none of them is on the classpath, the default feign client is used.
  • So with Contract is SpringMvcContract, so now you can use Spring MVC annotaions to define the client interfaces. Let's see some common annotations in the table below.
Annotation Path
@RequestMapping import org.springframework.web.bind.annotation.RequestMapping;
@PathVariable import org.springframework.web.bind.annotation.PathVariable;
@RequestParam import org.springframework.web.bind.annotation.RequestParam;
@RequestBody import org.springframework.web.bind.annotation.RequestBody;
  • Let's take an example:
AdapterServiceApi.java
1
2
3
4
5
6
public interface AdapterServiceApi {

        // define api path, method, input path variable and type of response
        @RequestMapping(method = RequestMethod.GET, value = "blog/posts/{id}")
        String getPostById(@PathVariable(value = "id") String id);
}

Spring Cloud OpenFeign Dependencies#

  • To add Spring Cloud OpenFeign dependencies, you have to add Spring Cloud dependencies first as mentioned in Spring Cloud Introduction. So you have to add this dependency first for Spring Cloud
pom.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<properties>
    <spring.cloud-version>2021.0.0</spring.cloud-version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud-version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  • Then you can add this dependency below for Spring Cloud OpenFeign.
pom.xml
1
2
3
4
5
<dependency>  
   <groupId>org.springframework.cloud</groupId>  
   <artifactId>spring-cloud-starter-openfeign</artifactId>  
   <version>2.2.6.RELEASE</version>  
</dependency>

Spring Cloud OpenFeign Default Using#

Enable Spring Cloud OpenFeign#

  • Add annotation @EnableFeignClients into the main class as below to enable FeignClient.
BffApplication.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.application.bff;  

import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.cloud.openfeign.EnableFeignClients;  

@EnableFeignClients  
@SpringBootApplication  
public class BffApplication {  

   public static void main(String[] args) {  
      SpringApplication.run(BffApplication.class, args);  
   }  

}

Create FeignClient#

  • Now we will create an adapter interface to configure OpenFeign with target api as below:
AdapterServiceApi.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.application.bff.adapters;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

// name of this feign client adapter and the base url which is defined in `application.yml`
@FeignClient(name = "adapter.service.api", url = "${adapter.service.url.vn}")
public interface AdapterServiceApi {

        // define api path, method, input path variable and type of response
        @RequestMapping(method = RequestMethod.GET, value = "blog/posts/{id}")
        String getPostById(@PathVariable(value = "id") String id);
}
  • The application.yml will contain the target URL of REST Server that we want to call to.
application.yml
1
2
3
4
5
6
7
#service run at port 9090
server:
  port: 9090

adapter:
  service:
    url: http://localhost:8080

Using OpenFeign In Code#

  • Now, you created the FeignClient interface. So you can inject for any service classes by @Autowired and methods for using.
AdapterServiceImpl.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package com.application.bff.service;

import com.application.bff.adapters.AdapterServiceApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AdapterServiceImpl implements AdapterService{

    @Autowired
    private AdapterServiceApi adapterServiceApi;

    @Override
    public String getPostById(String id) {
        return adapterServiceApi.getPostById(id);
    }
}

Testing#

  • Now, Let's start target service application(Service A) and your application that contain the OpenFeign configuration above (Service B). Then when you can an api of Service B then this api will call to an api of Service A and you should see the result from the Service A.

  • Let's call an api of Service A to create an data.

 #zoom

  • Then let's call an api of Service B to get the data that has just created on Service A

 #zoom

  • As you can see the Service B can call to api of Service A to get data successfully, so it mean the configuration for OpenFeign is correct.

See Also#

References#