Spring Cloud應用(三)---feign使用

hurricane_li發表於2018-05-04

feign相對於httpclient與ribbon,在使用風格上更加傾向於物件導向,使用如下:

使用feign需要引入的jar包,使用pom:

		<dependency>
			<groupId>com.netflix.feign</groupId>
			<artifactId>feign-core</artifactId>
			<version>8.18.0</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>com.netflix.feign</groupId>
			<artifactId>feign-gson</artifactId>
			<version>8.18.0</version>
			<scope>runtime</scope>
		</dependency>

feign-gson模組是為了以json的形式與伺服器互動,使用其中的編碼器與解碼器。

當請求引數為普通字串時:

		PersonClient target = Feign.builder()
		.decoder(new GsonDecoder())
		.encoder(new GsonEncoder())
		.target(PersonClient.class, "http://localhost:8000");
		
//		引數為字串
		Person personById = target.getPersonById(12);
		System.out.println(personById);

請求引數為物件時,用json編碼後傳輸:

//		引數為物件,會由解碼器與Headers註解完成將物件以json格式傳遞給伺服器
		Person person = new Person();
		person.setId(22);
		person.setName("hu");
		person.setDesc("desc");
		Person personByEntity = target.getPersonByEntity(person);
		System.out.println(personByEntity);

對應的PersonClient為:

package com.hurricane.learn.feign.client;

import com.hurricane.learn.feign.entity.Person;

import feign.Headers;
import feign.Param;
import feign.RequestLine;

public interface PersonClient {
	
	/**
	 * 將伺服器返回的json轉成物件,需要解碼器GsonDecoder
	 * 從客戶端向伺服器傳遞普通引數,格式如下
	 * @param id
	 * @return
	 */
	@RequestLine("GET /person/{id}")
	Person getPersonById(@Param("id") int id);
	
	
	/**
	 * 從客戶端向伺服器傳遞物件引數,需要編碼器GsonEncoder
	 * 並且要注意新增header註解,如下,
	 * 對應的伺服器端的服務介面處定義應為
	 * public Person getPersonByEntity(@RequestBody Person p,HttpServletRequest request)
	 * 注意使用RequestBody註解
	 * @param p
	 * @return
	 */
	@RequestLine("GET /person/entity")
	@Headers("Content-Type: application/json")
	Person getPersonByEntity(Person p);
	
	//Headers標籤不可省略
	@RequestLine("GET /person/entity/xml")
	@Headers("Content-Type: application/xml")
	Person getPersonByEntityXml(Person p);

}

在伺服器端,接收引數為簡單型別,使用@PathVariable或@RequestParam即可進行獲取,對於接收的是json時,應使用@RequestBody,這樣spring會自動將json轉換為物件(當然,接收json型別引數時,使用@RequestParam也是可以接收到的)。

feign客戶端使用的伺服器端的介面為:

	@RequestMapping("/{id}")
	public Person getPerson(@PathVariable("id") int id,HttpServletRequest request) {
		Person person = new Person();
		person.setId(id);
		person.setName("hurricane");
		person.setDesc(request.getRequestURI().toString());
		return person;
	}
	
	@RequestMapping("/entity")
	public Person getPersonByEntity(@RequestBody Person p,HttpServletRequest request) {
		p.setName(p.getName()+"---from server");
		return p;
	}

除了使用json作為資料傳輸的格式,feign還支援xml(不止json、xml,因為feign提供了編碼器與解碼器,可以任意自定義傳輸格式,只要與伺服器端的接收匹配即可)。

在使用xml作為傳輸格式時,應注意引入類似於feign-gson的模組feign-jaxb,

		<dependency>
			<groupId>com.netflix.feign</groupId>
			<artifactId>feign-jaxb</artifactId>
			<version>8.18.0</version>
			<scope>runtime</scope>
		</dependency>

feign-jaxb中包含了對xml的編碼器與解碼器,feign的客戶端的定義為:

		JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder().build();
		PersonClient target = Feign.builder()
		.encoder(new JAXBEncoder(jaxbFactory))
		.decoder(new JAXBDecoder(jaxbFactory))
		.target(PersonClient.class, "http://localhost:8000");
		
		Person person = new Person();
		person.setId(22);
		person.setName("hurricane");
		person.setDesc("desc");
		Person personByEntityXml = target.getPersonByEntityXml(person);
		System.out.println(personByEntityXml);

對應的PersonClient為:

	//Headers標籤不可省略
	@RequestLine("GET /person/entity/xml")
	@Headers("Content-Type: application/xml")
	Person getPersonByEntityXml(Person p);

為了配合xml的編碼器應該再Person的實體類上加入@XmlRootElement註解,如下:

@XmlRootElement
public class Person {

還有一個註解@XmlElement,可以選擇性使用,對於Person的每個屬性,要麼應有getter方法,要麼應在屬性上加入@XmlElement註解,才能讓編碼器將該屬性傳遞到伺服器端,此外@XmlElement(屬性上的)註解不應與getter方法同時存在,同時存在會拋異常說存在重複的屬性(@XmlElement放在getter方法上沒問題,但是沒必要)。若Person的一個屬性既沒有@XmlElement註解又沒有getter方法,則該屬性值無法被傳遞到伺服器端。

在伺服器端,為了能讓springboot的應用能夠接受xml形式的引數,應該加入依賴的jar包,pom為:

		<dependency>
			<groupId>com.fasterxml.jackson.jaxrs</groupId>
			<artifactId>jackson-jaxrs-xml-provider</artifactId>
			<version>2.9.5</version>
		</dependency>

對應的服務端介面為:

	@RequestMapping(value="/entity/xml",consumes = MediaType.APPLICATION_XML_VALUE, 
			produces = MediaType.APPLICATION_XML_VALUE)
	public String getPersonByEntityXml(@RequestBody Person p,HttpServletRequest request) throws IOException {
		System.out.println("xml請求成功");
		System.out.println(p);
		p.setName(p.getName()+"---from server");
		return "<person><id>22</id><name>hurricane---from server</name><desc>descc</desc></person>";
	}

consumes引數即使不指定,也能夠正常接收到值,但是為了伺服器的穩定性,應該對請求引數的格式做相應的限定,produces屬性必須指定,來讓springboot將Person實體類轉換為xml,返回給客戶端。

可以看到上面的伺服器介面中返回的是一個字串,這是因為spring預設轉換成的相應的xml是:

<Person><id>22</id><name>hurricane---from server</name><desc>descc</desc></Person>

注意Person標籤被大寫了,這使得feign客戶端解析失敗,為了能夠正常的接收伺服器端的xml格式的資料,才返回的字串,在實際使用中可以通過自定義的解碼器來解析,但是常規操作應該也存在,暫時還不清楚。

feign的設計使得feign使用具有很大的靈活性,客戶端、註解解析器、編碼器、解碼器都支援可插拔,如下:

		PersonMyClient target = Feign.builder()
				.client(new MyFeignClient())	//客戶端自定義
				.contract(new MyContract())		//註解解析器自定義
				.decoder(new GsonDecoder())		//解碼器自定義
				.encoder(new GsonEncoder())		//編碼器自定義
				.target(PersonMyClient.class, "http://localhost:8000");

 

具體可以參考楊恩雄的部落格:https://my.oschina.net/JavaLaw/blog?sort=time&p=5&temp=1525411026413

 

 

 

參考:

 

  • 楊恩雄的視訊教程

 

相關文章