在实际开发中,对于服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以我们通常会针对各个微服务自行封装一些客户端类来包装这些依赖服务的调用,Spring Cloud Feign 在此基础上做了进一步的封装,由他来帮助我们定义和实现依赖服务接口的定义,我们只需要创建一个接口并用注解的方式来配置他,即可完成对服务提供方的接口绑定,简化了在使用 Spring Cloud Ribbon 时自行封装服务调用客户端的开发量。

快速入门

  • 首先创建一个 Spring Cloud 的基础工程,并增加 spring-cloud-starter-eureka 依赖 和 spring-cloud-starter-feign 依赖,示例代码如下:

    <?xml
    version="1.0"
    encoding="UTF-8"?>

    <project
    xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.lixue</groupId>

    <artifactId>eureka-feign-consumer</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>jar</packaging>

    <name>eureka-feign-consumer</name>

    <description>Demo project for Spring Boot</description>

    <parent>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-parent</artifactId>

    <version>1.5.6.RELEASE</version>

    <relativePath/>
    <!-- lookup parent from repository -->

    </parent>

    <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

    <java.version>1.8</java.version>

    <spring-cloud.version>Dalston.SR3</spring-cloud.version>

    </properties>

    <dependencies>

    <dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-eureka</artifactId>

    </dependency>

    <dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-feign</artifactId>

    </dependency>

    <dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-test</artifactId>

    <scope>test</scope>

    </dependency>

    </dependencies>

    <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>

    <build>

    <plugins>

    <plugin>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-maven-plugin</artifactId>

    </plugin>

    </plugins>

    </build>

    </project>

  • 创建应用主类 EurekaFeignConsumerApplication 并通过 @EnableFeignClients 注解开启 Spring Cloud Feign 功能,使用 @EnableDiscoveryClient 注解开启 Eureka的服务发现,代码如下:

    @EnableFeignClients

    @EnableDiscoveryClient

    @SpringBootApplication

    public class EurekaFeignConsumerApplication {

    public static
    void
    main(String[] args) {

    SpringApplication.run(EurekaFeignConsumerApplication.class, args);

    }

    }

  • 定义 HelloWorldService 接口,通过 @FeignClient 注解指定服务名来绑定服务,然后再使用Spring MVC 的注解来绑定具体该服务提供的 REST 接口,代码如下:

    @FeignClient ("ORG.LIXUE.HELLOWORLD")

    public interface HelloWorldService {

    @RequestMapping ("/hi")

    String hi();

    }

  • 在需要调用服务的位置,使用 @Autowired
    直接注入
    HelloWorldService 实例,并使用该接口实例调用方法 hi 来完成 /hi 接口的调用,示例代码如下:

    @RestController

    public class FeignConsumerController {

    @Autowired

    HelloWorldService helloWorldService;

    @RequestMapping ("/hi")

    public String hi() {

    return helloWorldService.hi();

    }

    }

  • 在 application.yml 中需要指定服务注册中心,并定义自身的服务名和端口,示例如下:

    server:

    port: 9200

    eureka:

    client:

    service-url:

    defaultZone: http://eurekaserver2:9002/eureka,http://eurekaserver1:9001/eureka

    spring:

    application:

    name: eureka-feign-consumer

  • 启动服务注册中心以及二个ORG.LIXUE.HELLOWORLD服务,然后启动 eureka-feign-consumer ,此时发送几次 GET 请求到 http://localhost:9200/hi ,可以正确的返回 Hello World hi 9100 或者 Hello World hi 9101 ,可以看到Feign实现的消费者,依然是利用 Ribbon 维护了针对 ORG.LIXUE.HELLOWORLD服务列表信息,并且通过轮询实现了客户端负载均衡。

参数绑定

在入门示例中我们实现的是一个不带参数的
REST
服务绑定,然而现实系统中的各种业务接口要比他复杂很多,我们会在HTTP的各个位置传入各种不同类型的参数,并且在返回请求响应的时候也可能是一个复杂对象结构,因此我们需要使用
Feign
来对不同形式的参数进行绑定

  • @RequestParam 注解:常用来处理简单类型的绑定,通过Request.getParameter() 获取的String可直接转换为简单类型的情况( String--> 简单类型的转换操作由ConversionService配置的转换器来完成);因为使用request.getParameter()方式获取参数,所以可以处理get 方式中queryString的值,也可以处理post方式中 body data的值;用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容,提交方式GET、POST;该注解有两个属性: value、required; value用来指定要传入值的id名称,required用来指示参数是否必须绑定

    @RequestMapping (value = "/hi1", method = RequestMethod.GET)

    public String hi(@RequestParam ("name") String name) {

    return
    "Hello World hi " + port + " name " + name;

    }

  • @CookieValue
    注解:可以把Request header中关于cookie的值绑定到方法的参数上

    @RequestMapping("/displayHeaderInfo.do")

    public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie)  {

    //...

    }

  • @RequestBody 注解:用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上

    @RequestMapping (value = "/hi2", method = RequestMethod.POST)

    public String hi(@RequestBody User user) {

    return
    "Hello World hi " + port + "\tUser=" + user;

    }

    注意:User 类必须有默认构造函数

  • @RequestHeader 注解:可以把Request请求header部分的值绑定到方法的参数上

    @RequestMapping("/displayHeaderInfo.do")

    public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,

    @RequestHeader("Keep-Alive") long keepAlive)  {

    //...

    }

  • @PathVariable 注解:当使用@RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的paramId可通过 @Pathvariable
    注解绑定它传过来的值到方法的参数上,示例代码如下:

    @Controller

    @RequestMapping("/owners/{ownerId}")

    public class RelativePathUriTemplateController {

    @RequestMapping("/pets/{petId}")

    public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {

    // implementation omitted

    }

    }

继承特性

在Spring
Cloud
Feign 中,提供了继承特性来帮助我们构建相应的服务客户端不安定接口和服务控制器,进一步减少编码量,示例如下:

  • 创建一个基础的 Maven 工程,命名为
    service-contract,由于需要使用到 Spring MVC 的注解,因此在 pom.xml 中引入 spring-boot-starter-web 依赖,具体内容如下:

    <?xml
    version="1.0"
    encoding="UTF-8"?>

    <project
    xmlns="http://maven.apache.org/POM/4.0.0"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.lixue</groupId>

    <artifactId>service-contract</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>jar</packaging>

    <name>service-contract</name>

    <description>Demo project for Spring Boot</description>

    <parent>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-parent</artifactId>

    <version>1.5.6.RELEASE</version>

    <relativePath/>
    <!-- lookup parent from repository -->

    </parent>

    <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

    <java.version>1.8</java.version>

    <spring-cloud.version>Dalston.SR3</spring-cloud.version>

    </properties>

    <dependencies>

    <dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-web</artifactId>

    </dependency>

    </dependencies>

    </project>

  • 在项目中创建服务需要使用的实体类和服务接口,代码如下:

    public class User {

    private String name;

    private
    int age;

    // setter

    getter
    方法

    @Override

    public String toString() {

    return
    "name=" + name + "\tage=" + age;

    }

    }

    @RequestMapping("/hello")

    public interface HelloWorldService {

    @RequestMapping (value = "/hi", method = RequestMethod.GET)

    String hi();

    @RequestMapping (value = "/hi1", method = RequestMethod.GET)

    String hi(@RequestParam ("name") String name);

    @RequestMapping (value = "/hi2", method = RequestMethod.POST)

    String hi(@RequestBody User user);

    }

  • 在服务的具体实现项目中增加 service-contract 的依赖,并且实现服务需要继承上面的 HelloWorldService 接口,并重写接口方法实现具体的服务实现

    @RestController

    public class HelloWorldController implements HelloWorldService {

    @Value ("${server.port}")

    int port;

    @Override

    public String hi() {

    return
    "hi port " + port;

    }

    @Override

    public String hi(String name) {

    return
    "hi port " + port + " name " + name;

    }

    @Override

    public String hi(@RequestBody User user) {

    return
    "hi port " + port + " user " + user;

    }

    }

  • 在具体的消费项目中增加 service-contract 依赖增加接口
    HelloWorldServiceProxy ,继承
    service-contract 项目中的
    HelloWorldService
    并使用 @FeignClient 注解来声明调用服务名称

    @FeignClient ("ORG.LIXUE.HELLOWORLD")

    public interface HelloWorldServiceProxy extends HelloWorldService {

    }

  • 在需要调用服务的位置,使用 @Autowired
    直接注入
    HelloWorldServiceProxy 实例,并使用该接口实例调用方法 hi 来完成 /hi 接口的调用,示例代码如下:

    @RestController

    public class FeignConsumerController {

    @Autowired

    HelloWorldService helloWorldService;

    @RequestMapping ("/hi")

    public String hi() {

    User user = new
    User();

    user.setName("liyong");

    user.setAge(3434);

    StringBuilder stringBuilder = new
    StringBuilder();

    stringBuilder.append("hi=" + helloWorldServiceProxy.hi()).append("<br/>");

    stringBuilder.append("hi1=" + helloWorldServiceProxy.hi("lixue")).append("<br/>");

    stringBuilder.append("hi2=" + helloWorldServiceProxy.hi(user)).append("<br/>");

    return stringBuilder.toString();

    }

    }

  • 在 application.yml 中需要指定服务注册中心,并定义自身的服务名和端口,示例如下:

    server:

    port: 9200

    eureka:

    client:

    service-url:

    defaultZone: http://eurekaserver2:9002/eureka,http://eurekaserver1:9001/eureka

    spring:

    application:

    name: eureka-feign-consumer

  • 启动服务注册中心以及二个ORG.LIXUE.HELLOWORLD服务,然后启动 eureka-feign-consumer ,此时发送几次 GET 请求到 http://localhost:9200/hi ,可以正确的返回 Hello World hi 9100 或者 Hello World hi 9101 ,可以看到Feign实现的消费者,依然是利用 Ribbon 维护了针对 ORG.LIXUE.HELLOWORLD服务列表信息,并且通过轮询实现了客户端负载均衡。

笔记:Spring Cloud Feign 声明式服务调用的更多相关文章

  1. Spring Cloud Feign 声明式服务调用

    目录 一.Feign是什么? 二.Feign的快速搭建 三.Feign的几种姿态 参数绑定 继承特性 四.其他配置 Ribbon 配置 Hystrix 配置 一.Feign是什么? ​ 通过对前面Sp ...

  2. Spring Cloud Feign声明式服务调用(转载)+遇到的问题

    转载:原文 总结: 1.pom添加依赖 2.application中填写正确的eureka配置 3.启动项中增加注解 @EnableFeignClients 4.填写正确的调用接口 通过原文使用Fei ...

  3. Spring Cloud 2-Feign 声明式服务调用(三)

    Spring Cloud Feign  1. pom.xml 2. application.yml 3. Application.java 4. Client.java 简化RestTemplate调 ...

  4. spring cloud 系列第4篇 —— feign 声明式服务调用 (F版本)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.feign 简介 在上一个用例中,我们使用ribbon+restTem ...

  5. Feign声明式服务调用

    Feign是一种声明式.模板化的HTTP客户端(仅在Application Client中使用).声明式调用是指,就像调用本地方法一样调用远程方法,无需感知操作远程http请求. Spring Clo ...

  6. SpringCloud微服务实战二:Spring Cloud Ribbon 负载均衡 + Spring Cloud Feign 声明式调用

    1.Spring Cloud Ribbon的作用 Ribbon是Netflix开发的一个负载均衡组件,它在服务体系中起着重要作用,Pivotal将其整合成为Spring Cloud Ribbon,与其 ...

  7. Spring Cloud07: Feign 声明式接口调用

    一.什么是Feign Feign也是去实现负载均衡,但是它的使用要比Ribbon更加简化,它实际上是基于Ribbon进行了封装,让我们可以通过调用接口的方式实现负载均衡.Feign和Ribbon都是由 ...

  8. SpringCloud实战-Feign声明式服务调用

    在前面的文章中可以发现当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率 ...

  9. Spring Cloud Feign 优雅的服务调用

    Fegin 是由NetFlix开发的声明式.模板化HTTP客户端,可用于SpringCloud 的服务调用.提供了一套更优雅.便捷的HTTP调用API,并且SpringCloud整合了Fegin.Eu ...

随机推荐

  1. 我的Java设计模式-策略模式

    今天给大家说说田忌赛马的故事.如有雷同,纯属巧合!话说在战国时期,群雄割据,硝烟四起,茶余饭后还是少不了娱乐活动的,其中赛马是最火爆的.一天,孙膑看到田忌像个死鸡似的就知道肯定赛马又输给了齐威王,立马 ...

  2. Android内核解读-应用的安装过程

    前言 我们知道,在android手机上安装一个apk很简单,只要打开apk文件,默认就会弹出安装界面,然后点击确定,经过若干秒后,apk就安装成功了,可是你知道apk的安装过程是什么吗?你知道andr ...

  3. php学习笔记之一维数组

    数组是指可以存放多个数据的数据类型. PHP中数组是一组关键字(key)和值(values)的集合,值可以是任何一种数据类型, 一维数组的创建方式: $arr=array(2,5,6); $arr=a ...

  4. Java中的List转换成JSON报错(四)

    1.错误描述 Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/colle ...

  5. hdu2242 考研路茫茫——空调教室

    弱联通 #include<iostream> #include<cstdio> #include<cstring> #include<map> #inc ...

  6. My SQL 登录命令,创建表与删除表

    一.连接MYSQL. 格式: mysql -h主机地址 -u用户名 -p用户密码 1.例1:连接到本机上的MYSQL. 首先在打开DOS窗口,然后进入目录 mysqlbin,再键入命令mysql -u ...

  7. JustMock .NET单元测试利器(三)用JustMock测试你的应用程序

    用JustMock测试你的应用程序 本主题将指导您通过几个简单的步骤来使用Telerik®JustMock轻松测试您的应用程序.您将理解一个简单的原理,称为Arrange / Act / Assert ...

  8. JNDI在server.xml中的配置(全局和局部的)

    总结: 全局就是在数据源server.xml中配置,然后通过和项目名相同的xml来进行映射.对所有的项目都起作用.那个项目需要就在对应的tomcat下配置一个与项目名相同的xml映射文件. 局部的就是 ...

  9. 关于html5 data-*自定义属性相关注意点和踩过的坑

    在HTML5中添加了data-*的方式来自定义属性,所谓data-*实际上上就是data-前缀加上自定义的属性名,命名可以用驼峰命名方式,但取值是必需全部使用小写(后面会说),使用这样的结构可以进行数 ...

  10. 第一个bug

    话不多说自己遇到的第一个小程序bug 需要渲染渲染多重元素,这个没什么.but当你要获取这个大样式的id进行各种操作时,你需要每一个子节点都加上data-=""属性这样就很麻烦了, ...