本文節選自《瘋狂Spring Cloud微服務架構實戰》html
京東購買地址:https://item.jd.com/12256011.htmljava
噹噹網購買地址:http://product.dangdang.com/25201393.htmlgit
Spring Cloud教學視頻:https://my.oschina.net/JavaLaw/blog/1552993web
Spring Cloud電子書:https://my.oschina.net/JavaLaw/blog/1570383spring
本文要點json
Feign編碼器與解碼器架構
本小節全部的案例都是單獨使用Feign,Feign在Spring Cloud的使用將在後面章節講述,請讀者注意該細節。app
向服務發送請求的過程當中,有些狀況須要對請求的內容進行處理。例如服務端發佈的服務接收的是JSON格式參數,而客戶端使用的是對象,這種狀況就可使用編碼器,將對象轉換爲JSON字符串。ide
爲服務端編寫一個REST服務,處理POST請求,請見代碼清單5-7。spring-boot
代碼清單5-7:codes\05\5.1\rest-server\src\main\java\org\crazyit\cloud\MyController.java
/** * 參數爲JSON */ @RequestMapping(value = "/person/create", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public String createPerson(@RequestBody Person person) { System.out.println(person.getName() + "-" + person.getAge()); return "Success, Person Id: " + person.getId(); }
控制器中,發佈了一個「/person/create」的服務,須要傳入JSON格式的請求參數。在客戶端中,要調用該服務,先編寫接口,再使用註解進行修飾,請見代碼清單5-8。
代碼清單5-8:codes\05\5.2\feign-use\src\main\java\org\crazyit\feign\PersonClient.java
public interface PersonClient { @RequestLine("POST /person/create") @Headers("Content-Type: application/json") String createPerson(Person person); @Data class Person { Integer id; String name; Integer age; String message; } }
注意在客戶端的服務接口中,使用了@Headers註解,聲明請求的內容類型爲JSON,接下來再編寫運行類,如代碼清單5-9。
代碼清單5-9:codes\05\5.2\feign-use\src\main\java\org\crazyit\feign\EncoderTest.java
/** * 運行主類 * @author 楊恩雄 * */ public class EncoderTest { public static void main(String[] args) { // 獲取服務接口 PersonClient personClient = Feign.builder() .encoder(new GsonEncoder()) .target(PersonClient.class, "http://localhost:8080/"); // 建立參數的實例 Person person = new Person(); person.id = 1; person.name = "Angus"; person.age = 30; String response = personClient.createPerson(person); System.out.println(response); } }
運行類中,在建立服務接口實例時,使用了encoder方法來指定編碼器,本案例使用了Feign提供的GsonEncoder類,該類會在發送請求過程當中,將請求的對象轉換爲JSON字符串。Feign支持插件式的編碼器,若是Feign提供的編碼器沒法知足要求,還可使用自定義的編碼器,這部份內容在後面章節講述。啓動服務,運行代碼清單5-9,可看到服務已經調用成功,運行後輸出以下:
Success, Person Id: 1
編碼器是對請求的內容進行處理,解碼器則會對服務響應的內容進行處理,例如解析響應的JSON或者XML字符串,轉換爲咱們所須要的對象,在代碼中經過如下的代碼片段設置解碼器:
PersonClient personService = Feign.builder() .decoder(new GsonDecoder()) .target(PersonClient.class, "http://localhost:8080/");
在前面章節中,咱們已經使用過GsonDecoder解碼器,在此再也不做贅述。
除了支持JSON的處理外,Feign還爲XML的處理了提供編碼器與解碼器,可使用JAXBEncoder與JAXBDecoder來進行編碼與解碼。爲服務端添加發布XML的接口,請見代碼清單5-10。
代碼清單5-10:codes\05\5.1\rest-server\src\main\java\org\crazyit\cloud\MyController.java
/** * 參數與返回值均爲XML */ @RequestMapping(value = "/person/createXML", method = RequestMethod.POST, consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE) @ResponseBody public String createXMLPerson(@RequestBody Person person) { System.out.println(person.getName() + "-" + person.getId()); return "<result><message>success</message></result>"; }
在服務端發佈的服務方法中,聲明瞭傳入的參數爲XML。須要注意的是,服務端項目「rest-server」使用的是「spring-boot-starter-web」進行構建,默認狀況下不支持XML接口,調用接口時會獲得如下異常信息:
{"timestamp":1502705981406,"status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'application/xml;charset=UTF-8' not supported","path":"/person/createXML"}
爲服務端的pom.xml加入如下依賴便可解決該問題:
<dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-xml-provider</artifactId> <version>2.9.0</version> </dependency>
編寫客戶端時,先定義好服務接口以及對象,接口請見代碼清單5-11。
代碼清單5-11:codes\05\5.2\feign-use\src\main\java\org\crazyit\feign\PersonClient.java
public interface PersonClient { @RequestLine("POST /person/createXML") @Headers("Content-Type: application/xml") Result createPersonXML(Person person); @Data @XmlRootElement class Person { @XmlElement Integer id; @XmlElement String name; @XmlElement Integer age; @XmlElement String message; } @Data @XmlRootElement class Result { @XmlElement String message; } }
在接口中,定義了「Content-Type」爲XML,使用了JAXB的相關注解來修飾Person與Result。接下來,只須要調用createPersonXML方法便可請求服務,請見代碼清單5-12。
代碼清單5-12:codes\05\5.2\feign-use\src\main\java\org\crazyit\feign\XMLTest.java
package org.crazyit.feign; import org.crazyit.feign.PersonClient.Person; import org.crazyit.feign.PersonClient.Result; import feign.Feign; import feign.jaxb.JAXBContextFactory; import feign.jaxb.JAXBDecoder; import feign.jaxb.JAXBEncoder; public class XMLTest { public static void main(String[] args) { JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder().build(); // 獲取服務接口 PersonClient personClient = Feign.builder() .encoder(new JAXBEncoder(jaxbFactory)) .decoder(new JAXBDecoder(jaxbFactory)) .target(PersonClient.class, "http://localhost:8080/"); // 構建參數 Person person = new Person(); person.id = 1; person.name = "Angus"; person.age = 30; // 調用接口並返回結果 Result result = personClient.createPersonXML(person); System.out.println(result.message); } }
本小節的請求有一點特殊,請求服務時傳入參數爲XML、返回的結果也是XML,目的是爲了能使編碼與解碼一塊兒使用。開啓服務,運行代碼清單5-12,能夠看到服務端與客戶端的輸出。
根據前面的兩小節可知,Feign的插件式編碼器與解碼器,能夠對請求以及結果進行處理,若是對於一些特殊的要求,可使用自定義的編碼器與解碼器。實現自定義編碼器,須要實現Encoder接口的encode方法,而對於解碼器,則要實現Decoder接口的decode方法,例如如下的代碼片段:
package org.crazyit.feign; import java.lang.reflect.Type; import feign.RequestTemplate; import feign.codec.EncodeException; import feign.codec.Encoder; public class MyEncoder implements Encoder { public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { // 實現本身的Encode邏輯 } }
在使用時,調用Feign的API來設置編碼器或者解碼器便可,實現較爲簡單,在此再也不贅述。
本文節選自《瘋狂Spring Cloud微服務架構實戰》
Spring Cloud教學視頻:https://my.oschina.net/JavaLaw/blog/1552993
Spring Cloud電子書:https://my.oschina.net/JavaLaw/blog/1570383