首先简单解释一下什么是声明式实现?
要做一件事, 需要知道三个要素,where, what, how。即在哪里( where)用什么办法(how)做什么(what)。什么时候做(when)我们纳入how的范畴。
1)编程式实现: 每一个要素(where,what,how)都需要用具体代码实现来表示。传统的方式一般都是编程式实现,业务开发者需要关心每一处逻辑
2)声明式实现: 只需要声明在哪里(where )做什么(what),而无需关心如何实现(how)。Spring的AOP就是一种声明式实现,比如网站检查是否登录,开发页面逻辑的时候,只需要通过AOP配置声明加载页面(where)需要做检查用户是否登录(what),而无需关心如何检查用户是否登录(how)。如何检查这个逻辑由AOP机制去实现, 而AOP的登录检查实现机制与正在开发页面的逻辑本身是无关的。
在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。Feign就是Spring Cloud提供的一种声明式REST客户端。可以通过Feign访问调用远端微服务提供的REST接口。现在我们就用Feign来调用SERVICE-HELLOWORLD暴露的REST接口,以获取到“Hello World”信息。在使用Feign时,Spring Cloud集成了Ribbon和Eureka来提供HTTP客户端的负载均衡。
下面我们就采用Feign的方式来调用Hello World服务集群。
1. 创建Maven工程,加入spring-cloud-starter-openfeign依赖
1 | <dependency> |
完整的pom文件如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.cwalzy</groupId>
<artifactId>springcloud.helloworld.feign.client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud.helloworld.feign.client</name>
<description>Demo Feigh client application</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<!-- <artifactId>spring-cloud-starter-feign</artifactId> -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- 使用Apache HttpClient替换Feign原生httpclient https://blog.csdn.net/u010277958/article/details/88730889-->
<!-- Http Client 支持 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- Apache Http Client 对 Feign 支持 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
<!-- <dependency>
<groupId>com.netflix.feign</groupId>
<groupId>com.netflix.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign-httpclient.version}</version>
</dependency> -->
<!-- OKHttp 对 Feign 支持 -->
<!-- <dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</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>
2. 创建启动类,需呀加上@EnableFeignClients注解以使用Feign, 使用@EnableDiscoveryClient开启服务自动发现
1 | package org.cwalzy.springcloudeureka; |
3. 添加配置文件application.yml, 使用端口8902, 名字定义为service-feign, 并注册到eureka服务中心
1 | eureka: |
4. 定义Feign:一个用@FeignClient注解的接口类
@FeignClient用于通知Feign组件对该接口进行代理(不需要编写接口实现),使用者可直接通过@Autowired注入; 该接口通过value定义了需要调用的SERVICE-HELLOWORLD服务(通过服务中心自动发现机制会定位具体URL); @RequestMapping定义了Feign需要访问的SERVICE-HELLOWORLD服务的URL(本例中为根“/”)
1 | package springcloud.helloworld.feign.service; |
Spring Cloud应用在启动时,Feign会扫描标有@FeignClient注解的接口,生成代理,并注册到Spring容器中。生成代理时Feign会为每个接口方法创建一个RequetTemplate对象,该对象封装了HTTP请求需要的全部信息,请求参数名、请求方法等信息都是在这个过程中确定的,Feign的模板化就体现在这里
5. 定义一个WebController。
注入之前通过@FeignClient定义生成的bean,
sayHello()映射到 http://localhost:8902/hello , 在这里,我修改了Hello World服务的映射,将根“/”, 修改成了“/hello”。
1 | package springcloud.helloworld.feign.service; |
6. 启动Feign应用, 访问 http://localhost:8902/hello
多次刷新,可以看到和前一章Ribbon里面的应用一样, 两个Hello World服务的输出交替出现。说明通过Feign访问服务, Spring Cloud已经缺省使用了Ribbon负载均衡。


7. 在Feign中使用Apache HTTP Client
Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址gwai会保持一个长连接,即利用HTTP的persistence connection 。我们可以用Apache的HTTP Client替换Feign原始的http client, 从而获取连接池、超时时间等与性能息息相关的控制能力。Spring Cloud从Brixtion.SR5版本开始支持这种替换,首先在项目中声明Apache HTTP Client和feign-httpclient依赖:1
2
3
4
5
6
7
8
9
10<!-- 使用Apache HttpClient替换Feign原生httpclient https://blog.csdn.net/u010277958/article/details/88730889 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- Apache Http Client 对 Feign 支持 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
然后在application.properties中添加:
1 | feign.httpclient.enabled=true |
8. Feign的Encoder、Decoder和ErrorDecoder
Feign将方法签名中方法参数对象序列化为请求参数放到HTTP请求中的过程,是由编码器(Encoder)完成的。同理,将HTTP响应数据反序列化为Java对象是由解码器(Decoder)完成的。默认情况下,Feign会将标有@RequestParam注解的参数转换成字符串添加到URL中,将没有注解的参数通过Jackson转换成json放到请求体中。注意,如果在@RequetMapping中的method将请求方式指定为GET,那么所有未标注解的参数将会被忽略,例如:
1 | @RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) |
此时因为声明的是GET请求没有请求体,所以obj参数就会被忽略。
在Spring Cloud环境下,Feign的Encoder只会用来编码没有添加注解的参数。如果你自定义了Encoder, 那么只有在编码obj参数时才会调用你的Encoder。对于Decoder, 默认会委托给SpringMVC中的MappingJackson2HttpMessageConverter类进行解码。只有当状态码不在200 ~ 300之间时ErrorDecoder才会被调用。ErrorDecoder的作用是可以根据HTTP响应信息返回一个异常,该异常可以在调用Feign接口的地方被捕获到。我们目前就通过ErrorDecoder来使Feign接口抛出业务异常以供调用者处理。
以上7~8完全摘自 http://blog.csdn.net/neosmith/article/details/52449921
本文转载自 https://www.cnblogs.com/chry/p/7266916.html
原文基于 spring boot 1.5.3.RELEASE 版本
而本文中有一些改动 是基于 spring boot 2.1.5.RELEASE 版本