spring cloud feign

优质
小牛编辑
126浏览
2023-12-01

introduction

netflix feign是一个类似retrofit进行http调用框架,Feign makes writing java http clients easier 使得编写http client代码更加简单

netflix feign

直接给出一段简单的案例

package com.lkl.netflix.feign;

import feign.*;
import feign.codec.ErrorDecoder;
import feign.codec.Decoder;
import feign.gson.GsonDecoder;

import java.io.IOException;
import java.util.List;

/**
 * Created by liaokailin on 16/5/9.
 */
public class GitHubExample {

    interface GitHub {  // 1
        @RequestLine("GET /repos/{owner}/{repo}/contributors")
        List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
    }


    public static void main(String... args) {
        Decoder decoder = new GsonDecoder();  // 2
        GitHub github = Feign.builder()
                .decoder(decoder) // 3
                .errorDecoder(new GitHubErrorDecoder(decoder))  // 4
                .logger(new Logger.ErrorLogger())  // 5
                .logLevel(Logger.Level.BASIC)  // 5
                .target(GitHub.class, "https://api.github.com");  // 6

        System.out.println("Let's fetch and print a list of the contributors to this library.");
        List<Contributor> contributors = github.contributors("netflix", "feign");  // 7
        for (Contributor contributor : contributors) {
            System.out.println(contributor.login + " (" + contributor.contributions + ")");
        }

        System.out.println("Now, let's cause an error.");
        try {
            github.contributors("netflix", "some-unknown-project");  // 8
        } catch (GitHubClientError e) {
            System.out.println(e.getMessage());
        }
    }

    static class Contributor {
        String login;
        int contributions;
    }

    static class GitHubClientError extends RuntimeException {  // 4
        private String message; // parsed from json

        @Override
        public String getMessage() {
            return message;
        }
    }

    static class GitHubErrorDecoder implements ErrorDecoder {   // 4

        final Decoder decoder;
        final ErrorDecoder defaultDecoder = new ErrorDecoder.Default();

        GitHubErrorDecoder(Decoder decoder) {
            this.decoder = decoder;
        }

        @Override
        public Exception decode(String methodKey, Response response) {
            try {
                return (Exception) decoder.decode(response, GitHubClientError.class);
            } catch (IOException fallbackToDefault) {
                return defaultDecoder.decode(methodKey, response);
            }
        }
    }


}

1.首先定义接口GitHub,使用注解RequestLine 表明contributors()方法为一个get请求,请求相对为/repos/{owner}/{repo}/contributors
2.Decoder decoder = new GsonDecoder(); 创建一个GsonDecoder解码器,表明通过Gson解析返回数据 3.decoder()方法设置解码器 4.errorDecoder()指定发生异常时的解码器,需要实现ErrorDecoder接口,覆写decode方法,通过指定的Decoder解析错误信息,这里还是使用GsonDecoder 5.logger 相关的表示配置日志系你系 6.target() 方法指定访问url以及返回的类型 7.通过创建的github对象调用contributors获取结果 8.模拟异常情况

note: feign使用起来很简单,其原理和retrofit及其类似,通过接口定义访问访问,用jdk的动态代理创建接口的实现类,在类中解析方法上的注解信息用以识别用户配置的http请求信息,然后执行请求; feign可以通过ReflectiveFeign下的newInstance()方法看到

return (T) Proxy
        .newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);

spring cloud feign

spring cloud feign通过注解的封装使用起来更加简单

package com.lkl.springcloud.feign;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * Created by liaokailin on 16/5/11.
 */
@Configuration
@EnableAutoConfiguration
@EnableFeignClients
@RestController
public class Application {

    @Autowired
    GitHub gitHub;
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @FeignClient(url = "https://api.github.com")
    interface GitHub {
        @RequestMapping(value = "/repos/{owner}/{repo}/contributors", method = RequestMethod.GET)
      ResponseEntity<List<Contributor>> contributors(@PathVariable("owner") String owner, @PathVariable("repo") String repo);
    }

    @RequestMapping("/")
    public void test(){
        ResponseEntity< List<Contributor>>  responseEntity  =   gitHub.contributors("netflix", "feign");
        List<Contributor> contributors = responseEntity.getBody();
         for (Contributor contributor : contributors) {
         System.out.println(contributor.login + " (" + contributor.contributions + ")");
         }
    }


    static class Contributor {
        String login;
        int contributions;
    }
}

注解EnableFeignClients表明需要扫描使用FeignClient注解的接口,在代码中定义了@FeignClient(url = "https://api.github.com") 表明该接口为一个feign接口 通过url指定访问路径RequestMapping表明相对路径

在代码中注入beanGitHub,直接调用其中申明的方法即可。

重点说明下FeignClient注解

该注解表示申明创建一个rest client bean,可以直接通过Autowired注入使用,如果ribbon在工程中启用,则会使用load balance进行后端请求调用,可以为FeignClient指定value表明需要访问的serviceId

feign + ribbon + eureka

在springcloud(第七篇)springcloud ribbon with eureka中讲解了ribbon+eureka,其中使用RestTemplate进行调用,也可以通过 feign调用


package com.lkl.springcloud.feign;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static org.springframework.web.bind.annotation.RequestMethod.GET;

/**
 * Created by liaokailin on 16/5/9.
 */
@SpringBootApplication
@EnableDiscoveryClient
@RestController
@RibbonClient("hello")
@EnableFeignClients
public class Application {

    @Autowired
    HelloClient client;

    @RequestMapping("/")
    public String hello() {
        return client.hello();
    }

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

    @FeignClient("simple")
    interface HelloClient {
        @RequestMapping(value = "/", method = GET)
        String hello();
    }

}

使用@FeignClient("simple")指定要访问的service id 由于hello()对应的mapping为@RequestMapping(value = "/", method = GET) 那么该方法实际调用url为 http://simple/,因此在执行

@RequestMapping("/")
    public String hello() {
        return client.hello();
    }

调用的为SimpleApplication.java

@RequestMapping("/")
    public String hello() {
        return "Hello";
    }