首先簡單解釋一下什麼是聲明式實現?css
要作一件事, 須要知道三個要素,where, what, how。即在哪裏( where)用什麼辦法(how)作什麼(what)。何時作(when)咱們歸入how的範疇。java
1)編程式實現: 每個要素(where,what,how)都須要用具體代碼實現來表示。傳統的方式通常都是編程式實現,業務開發者須要關心每一處邏輯web
2)聲明式實現: 只須要聲明在哪裏(where )作什麼(what),而無需關心如何實現(how)。Spring的AOP就是一種聲明式實現,好比網站檢查是否登陸,開發頁面邏輯的時候,只須要經過AOP配置聲明加載頁面(where)須要作檢查用戶是否登陸(what),而無需關心如何檢查用戶是否登陸(how)。如何檢查這個邏輯由AOP機制去實現, 而AOP的登陸檢查實現機制與正在開發頁面的邏輯自己是無關的。spring
在Spring Cloud Netflix棧中,各個微服務都是以HTTP接口的形式暴露自身服務的,所以在調用遠程服務時就必須使用HTTP客戶端。Feign就是Spring Cloud提供的一種聲明式REST客戶端。能夠經過Feign訪問調用遠端微服務提供的REST接口。如今咱們就用Feign來調用SERVICE-HELLOWORLD暴露的REST接口,以獲取到「Hello World」信息。在使用Feign時,Spring Cloud集成了Ribbon和Eureka來提供HTTP客戶端的負載均衡。apache
下面咱們就採用Feign的方式來調用Hello World服務集羣。編程
1. 建立Maven工程,加入spring-cloud-starter-feign依賴json
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
完整的pom文件以下:app
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 <groupId>com.chry</groupId> 7 <artifactId>springcloud.helloworld.feign.client</artifactId> 8 <version>0.0.1-SNAPSHOT</version> 9 <packaging>jar</packaging> 10 <name>springcloud.helloworld.feign.client</name> 11 <description>Demo Feigh client application</description> 12 13 <parent> 14 <groupId>org.springframework.boot</groupId> 15 <artifactId>spring-boot-starter-parent</artifactId> 16 <version>1.5.3.RELEASE</version> 17 <relativePath/> <!-- lookup parent from repository --> 18 </parent> 19 20 <properties> 21 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 22 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 23 <java.version>1.8</java.version> 24 </properties> 25 26 <dependencies> 27 <dependency> 28 <groupId>org.springframework.cloud</groupId> 29 <artifactId>spring-cloud-starter-eureka</artifactId> 30 </dependency> 31 <dependency> 32 <groupId>org.springframework.cloud</groupId> 33 <artifactId>spring-cloud-starter-feign</artifactId> 34 </dependency> 35 <dependency> 36 <groupId>org.springframework.boot</groupId> 37 <artifactId>spring-boot-starter-web</artifactId> 38 </dependency> 39 40 <dependency> 41 <groupId>org.springframework.boot</groupId> 42 <artifactId>spring-boot-starter-test</artifactId> 43 <scope>test</scope> 44 </dependency> 45 </dependencies> 46 47 <dependencyManagement> 48 <dependencies> 49 <dependency> 50 <groupId>org.springframework.cloud</groupId> 51 <artifactId>spring-cloud-dependencies</artifactId> 52 <version>Dalston.RC1</version> 53 <type>pom</type> 54 <scope>import</scope> 55 </dependency> 56 </dependencies> 57 </dependencyManagement> 58 59 <build> 60 <plugins> 61 <plugin> 62 <groupId>org.springframework.boot</groupId> 63 <artifactId>spring-boot-maven-plugin</artifactId> 64 </plugin> 65 </plugins> 66 </build> 67 68 <repositories> 69 <repository> 70 <id>spring-milestones</id> 71 <name>Spring Milestones</name> 72 <url>https://repo.spring.io/milestone</url> 73 <snapshots> 74 <enabled>false</enabled> 75 </snapshots> 76 </repository> 77 </repositories> 78 </project>
2. 建立啓動類,需呀加上@EnableFeignClients註解以使用Feign, 使用@EnableDiscoveryClient開啓服務自動發現負載均衡
1 package springcloud.helloworld.feign.service; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 import org.springframework.cloud.netflix.feign.EnableFeignClients; 7 8 @SpringBootApplication 9 @EnableDiscoveryClient 10 @EnableFeignClients 11 public class ServiceFeignApplication { 12 public static void main(String[] args) { 13 SpringApplication.run(ServiceFeignApplication.class, args); 14 } 15 }
3. 添加配置文件application.yml, 使用端口8902, 名字定義爲service-feign, 並註冊到eureka服務中心maven
1 eureka: 2 client: 3 serviceUrl: 4 defaultZone: http://localhost:8761/eureka/ 5 server: 6 port: 8902 7 spring: 8 application: 9 name: service-feign
4. 定義Feign:一個用@FeignClient註解的接口類,
@FeignClient
用於通知Feign組件對該接口進行代理(不須要編寫接口實現),使用者可直接經過@Autowired
注入; 該接口經過value定義了須要調用的SERVICE-HELLOWORLD服務(經過服務中心自動發現機制會定位具體URL); @RequestMapping定義了Feign須要訪問的SERVICE-HELLOWORLD服務的URL(本例中爲根「/」)
1 package springcloud.helloworld.feign.service; 2 3 import org.springframework.cloud.netflix.feign.FeignClient; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RequestMethod; 6 7 @FeignClient(value = "SERVICE-HELLOWORLD") 8 public interface HelloWorldService { 9 @RequestMapping(value = "/",method = RequestMethod.GET) 10 String sayHello(); 11 }
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; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RequestMethod; 6 import org.springframework.web.bind.annotation.RestController; 7 8 @RestController 9 public class WebController { 10 @Autowired HelloWorldService helloWorldFeignService; 11 @RequestMapping(value = "/hello",method = RequestMethod.GET) 12 public String sayHello(){ 13 return helloWorldFeignService.sayHello(); 14 } 15 }
6. 啓動Feign應用, 訪問http://localhost:8902/hello, 屢次刷新,能夠看到和前一章Ribbon裏面的應用同樣, 兩個Hello World服務的輸出交替出現。說明經過Feign訪問服務, Spring Cloud已經缺省使用了Ribbon負載均衡。
6. 在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 <!-- 使用Apache HttpClient替換Feign原生httpclient --> 2 <dependency> 3 <groupId>org.apache.httpcomponents</groupId> 4 <artifactId>httpclient</artifactId> 5 </dependency> 6 <dependency> 7 <groupId>com.netflix.feign</groupId> 8 <artifactId>feign-httpclient</artifactId> 9 <version>${feign-httpclient}</version> 10 </dependency>
而後在中添加:application.properties
feign.httpclient.enabled=true
7. Feign的Encoder、Decoder和ErrorDecoder
Feign將方法簽名中方法參數對象序列化爲請求參數放到HTTP請求中的過程,是由編碼器(Encoder)完成的。同理,將HTTP響應數據反序列化爲Java對象是由解碼器(Decoder)完成的。默認狀況下,Feign會將標有@RequestParam
註解的參數轉換成字符串添加到URL中,將沒有註解的參數經過Jackson轉換成json放到請求體中。注意,若是在@RequetMapping
中的method
將請求方式指定爲POST
,那麼全部未標註解的參數將會被忽略,例如:
@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);
此時由於聲明的是GET請求沒有請求體,因此obj
參數就會被忽略。
在Spring Cloud環境下,Feign的Encoder只會用來編碼沒有添加註解的參數。若是你自定義了Encoder, 那麼只有在編碼obj
參數時纔會調用你的Encoder。對於Decoder, 默認會委託給SpringMVC中的MappingJackson2HttpMessageConverter
類進行解碼。只有當狀態碼不在200 ~ 300之間時ErrorDecoder纔會被調用。ErrorDecoder的做用是能夠根據HTTP響應信息返回一個異常,該異常能夠在調用Feign接口的地方被捕獲到。咱們目前就經過ErrorDecoder來使Feign接口拋出業務異常以供調用者處理。