第一个Feign程序
编码器:如果服务端只接受JSON字符串格式/XML,那么我们客户端使用的是对象。在这种情况下,我们可以使用编码器将对象转换成JSON字符串/XML。
解码器:将服务端的响应结果对象,转换为客户端的对象。这个时候就需要用到解码器。
1. Feign被集成到SpringCloud Netflix模块,当Eureka,Ribbon集成的时候呢,Feign就具备了负载均衡的功能。Feign本身使用就很简便,再加上与SpringCloud的整合,将很大程度上降低我们开发的工作量。
2. 它是Github上面的一个开源项目,目的是为了简化WebService客户端的开发,在使用Feign的时候 可以使用注解来修饰接口,被修饰的接口就具有了访问webservice的能力,这些注解呢 可以使用Feign的自带注解,也可以支持使用第三方的注解。Feign还支持插件式的编码器和解码器,使用者可以通过这些特性,对请求和响应进行封装和解析。
Feign会更加的面向对象,下面我们使用Feign来对比一下CXF。在这之前我们需要准备对外提供接口。
如果没有接口等项目的朋友,可以参照前几章的“SpringCloud 中使用 Ribbon(默认轮询规则 + 自定义规则)”搭建(只搭建服务器与服务提供者即可)
这里我们一次把依赖CXF、Feign的依赖全部引入:pom.xml
org.apache.cxf cxf-core 3.1.0 org.apache.cxf cxf-rt-rs-client 3.1.0 io.github.openfeign feign-core 9.5.0 io.github.openfeign feign-gson 9.5.0 io.github.openfeign feign-jaxb 9.5.0 javax.xml.bind jaxb-api 2.2.8
下面我们编写一个CXF客户端,进行测试:CxfClient.java
public class CxfClient { public static void main(String[] args) throws IOException { // 创建 WebClient WebClient client = WebClient.create("http://localhost:9090/getPoliceById/123"); // 获取响应 Response response = client.get(); // 获取响应内容 InputStream is = (InputStream) response.getEntity(); String content = IOUtils.readStringFromStream(is); // 打印结果 System.out.println("请求结果:" + content); } }
乱码的问题不用管,我们就看 WebClient.create 方法中的url,url多的话,维护起来也麻烦,而且还需要对流进行操作。。。接下来,我们编写一个Feign程序,与CXF进行一下对比
在文章的一开始,提到了 Feign 程序会更加的面向对象,所以我们先创建一个实体类,用来接收结果对象:Police.java
public class Police { private String id;// 警察编号,用来保存用户输入的参数 private String url;// 处理请求的服务器url private String message;// 提示信息 public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
其次,我们创建一个接口类:FeignService.java
public interface FeignService { @RequestLine("GET /getPolice") public String getPolice(); @RequestLine("GET /getPolice") public Police getPoliceObj(); @RequestLine("GET /getPoliceById/{id}") public String getPoliceById(@Param("id") String id); /** * 发送JSON对象,返回字符串 * @param police * @return */ @RequestLine("POST /createPolice") @Headers("Content-Type: application/json")// 这里要指定请求类型 public String createPolice(Police police); /** * 发送XML对象,返回字符串 * @return */ @RequestLine("POST /createXmlPolice") @Headers("Content-Type: application/xml")// 这里要指定请求类型 public Police createXmlPolice(Police police); }
最后,我们再创建一个测试类,查看结果:TestMain.java
public static void main(String[] args) { /** * 根据ID获取派出警察,返回JSON字符串 */ FeignService client_1 = Feign.builder().target(FeignService.class, "http://localhost:9090"); String policeStr = client_1.getPolice(); System.out.println("返回JSON字符串:"+policeStr); String policeByIdStr = client_1.getPoliceById("123"); System.out.println("根据ID获取派出警察,返回JSON字符串:"+policeByIdStr); /** * 返回警察对象 */ FeignService client_2 = Feign.builder().decoder(new GsonDecoder()).target(FeignService.class, "http://localhost:9090"); Police police = client_2.getPoliceObj(); System.out.println("返回警察对象:"); System.out.println(" url:"+police.getUrl()); System.out.println(" message:"+police.getMessage()); /** * 发送JSON对象,返回字符串 */ FeignService client_3 = Feign.builder().encoder(new GsonEncoder()).target(FeignService.class, "http://localhost:9090"); Police police_json = new Police(); police_json.setMessage("你好"); String createResult = client_3.createPolice(police_json); System.out.println("发送JSON格式参数,返回字符串:"+createResult); /** * 发送XML对象,返回字符串 * 使用XML进行发送与接收时,实体类需要使用注解: * 类上注解:@XmlRootElement * 字段注解:@XmlElement * get方法使用注解:@XmlTransient */ JAXBContextFactory encoder = new JAXBContextFactory.Builder().build(); FeignService client_4 = Feign.builder() .encoder(new JAXBEncoder(encoder)) .decoder(new JAXBDecoder(encoder)) .target(FeignService.class, "http://localhost:9090"); Police police_xml = new Police(); police_xml.setMessage("你好"); Police policeResult = client_4.createXmlPolice(police_xml); System.out.println("发送XML格式参数,返回字符串:"+policeResult.getMessage()); }
但是如果向接口传入xml格式值,那接口应该怎么定义呢?
// 接收xml,返回xml @RequestMapping(value="/createXmlPolice", method=RequestMethod.POST, produces=MediaType.APPLICATION_XML_VALUE, consumes=MediaType.APPLICATION_XML_VALUE) public String createXmlPolice(@RequestBody Police police){ return ""; } "+police.getMessage()+"
OK,这样子就可以传递xml格式的参数了。。。那么下面贴一下以上main方法中的所有执行结果
额外再说一下,Feign的客服端 也是可插拔的,,下面教大家如何实现自定义客户端,并访问
首先需要自定义一个客户端,需要实现Feign的Client接口:MyClient.java
public class MyClient implements Client { @Override public Response execute(Request request, Options options) throws IOException { try { // 创建一个默认的客户端 CloseableHttpClient httpClient = HttpClients.createDefault(); // 获取调用的HTTP方法 final String method = request.method(); // 创建一个HttpClient的Request HttpRequestBase httpRequest = new HttpRequestBase() { @Override public String getMethod() { return method; } }; // 设置请求地址 httpRequest.setURI(new URI(request.url())); // 执行请求,获取响应 HttpResponse httpResponse = httpClient.execute(httpRequest); // 获取响应的主体内容 byte[] body = EntityUtils.toByteArray(httpResponse.getEntity()); // 将HttpClient的响应对象转换为Feign的Response Response feignResponse = Response.builder() .body(body) .headers(new HashMap>()) .status(httpResponse.getStatusLine().getStatusCode()) .build(); return feignResponse; } catch (Exception e) { e.printStackTrace(); } return null; } }
编写一个测试main方法:
/** * 使用可插拔式的自定义client客户端,进行请求访问 */ FeignService client_5 = Feign.builder().client(new MyClient()).target(FeignService.class, "http://localhost:9090"); String policeStr = client_5.getPolice(); System.out.println("返回JSON字符串:"+policeStr);
下面贴上运行结果