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