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
参考:
- 杨恩雄的视频教程