1、快速入门
1、创建一个提供者
这里就拿用户下订单举例
-
创建一个SpringCloud项目,引入以下依赖即可  -
项目创建好后,记得修改pom.xml 文件的springcloud 依赖  -
然后在启动类上添加@EnableEurekaClient 注解开启eureka服务 -
配置application.xml 文件,注册到eureka上  -
修改完后,创建一个下订单的Controller,编写一个接口用来测试 package com.tcc.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping("buyCar")
public String buyCar(){
return "劳斯莱斯-幻觉";
}
}
-
最后,启动项目,打开eureka地址查看order-a 服务是否注册成功
2、创建一个调用者(主要)
调用者需要引入OpenFeign依赖
-
创建项目和上面的步骤一样,除了创建Controller,依赖引入如下  -
创建好项目,修改好pom文件,配置好xml文件,配置好Eureka后,编写Controller -
Controller需要调用order-a 服务提供的接口,步骤如下 -
先在启动类上添加注解@EnableFeignClients // 开启feign的客户端,才可以帮助我们发起调用 -
添加好后,我们使用OpenFeign写法,创建一个文件夹,在里面创建接口,名字为UserOrderFeign package com.tcc.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient("order-a")
public interface UserOrderFeign {
@RequestMapping("buyCar")
public String buyCar();
}
-
编写好接口后,我们在Controller里面自动注入这个接口,然后调用接口里面的方法 package com.tcc.controller;
import com.tcc.feign.UserOrderFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private UserOrderFeign userOrderFeign;
@RequestMapping("doOrder")
public String doOrder(){
String doOrder = userOrderFeign.buyCar();
return doOrder;
}
}
-
编写完毕后我们访问http://localhost:8090/doOrder 调用接口,查看是否可以成功调用 
2、调用超时设置
测试:在order-a 服务提供的接口里延迟两秒再返回内容,看看user-a 是否可以正常获取
- order-a接口修改:
? 
-
修改完毕后,重启服务,然后再次调用doOrder 接口,查看结果 
结论:
当被调用的服务长时间无响应的时候,就停止访问,默认为1秒
RIbbon默认调用时长为1s,可以修改,超时调整,可以查看DefaultClientConfigImpl 类

ribbon:
ReadTimeout: 5000
ConnecTimeout: 5000
3、OpenFeign调用参数处理
Feign传参确保消费者和提供者的参数列表一致,包括返回值,方法签名要一致
- 通过URL传参,GET请求,参数列表使用
@PathVariable("") - 如果是GET请求,每个基本参数必须加
@RequestParam("") - 如果是POST请求,而且是对象集合等参数,必须加
@Requestbody或者@RequestParam
举例:
-
先在order-a和user-a 两个服务编写User 类 package com.tcc.entity;
import lombok.*;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User {
private String name;
private Integer age;
}
-
然后在order-a 服务编写好接口 package com.tcc.controller;
import com.tcc.entity.User;
import org.springframework.web.bind.annotation.*;
@RestController
public class ParamController {
@GetMapping("testUrl/{name}/and{age}")
public String testUrl(@PathVariable("name") String name, @PathVariable("age") Integer age){
return "姓名:"+name+",年龄:"+age;
}
@GetMapping("onParam")
public String onParam(@RequestParam("name") String name){
return "姓名:" + name;
}
@PostMapping("onObject")
public String oneObject(@RequestBody User user){
return "姓名:"+user.getName()+",年龄:"+user.getAge();
}
@PostMapping("onObjectAndOnParam")
public String onObjectAndOnParam(@RequestBody User user, @RequestParam("sex") String sex){
return "姓名:"+user.getName()+",年龄:"+user.getAge()+",性别:" + sex;
}
}
-
在user-a 服务的Feign中引入需要调用的方法签名 package com.tcc.feign;
import com.tcc.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@FeignClient("order-a")
public interface UserOrderFeign {
@RequestMapping("buyCar")
public String buyCar();
@GetMapping("testUrl/{name}/and{age}")
public String testUrl(@PathVariable("name") String name, @PathVariable("age") Integer age);
@GetMapping("onParam")
public String onParam(@RequestParam("name") String name);
@PostMapping("onObject")
public String oneObject(@RequestBody User user);
@PostMapping("onObjectAndOnParam")
public String onObjectAndOnParam(@RequestBody User user, @RequestParam("sex") String sex);
}
-
在Controller中远程调用服务进行测试 @GetMapping("testParam")
public String testParam(){
String s1 = userOrderFeign.testUrl("张三", 20);
System.out.println(s1);
String s2 = userOrderFeign.onParam("李四");
System.out.println(s2);
User user = User.builder()
.name("王五")
.age(21)
.build();
String s3 = userOrderFeign.oneObject(user);
System.out.println(s3);
user = User.builder()
.name("赵六")
.age(22)
.build();
String s4 = userOrderFeign.onObjectAndOnParam(user, "男");
System.out.println(s4);
return "ok";
}
-
访问http://localhost:8090/testParam 地址,查看是否调用成功  -
结果 
传递事件日期参数的坑
传过去的日期参数,会相差12个小时
-
先在order-a 服务编写接口,参数为Date类型。 @GetMapping("oneDate")
public Date oneDate(@RequestParam("date") Date date){
return date;
}
-
在UserOrderFeign 接口中写好方法签名 @GetMapping("oneDate")
public Date oneDate(@RequestParam("date") Date date);
-
然后在user-a 服务中,编写方法,远程调用服务 @GetMapping("oneDate")
public Date oneDate(){
Date d = new Date();
System.out.println(d);
Date date = userOrderFeign.oneDate(d);
System.out.println(date);
return date;
}
-
访问接口,查看控制台打印,可以发现相差12小时 
解决办法
-
可以把日期先转为字符串类型,再传过去 -
可以使用jdk8提供的日期类型 LocalDate now = LocalDate.now();
LocalDateTime now1 = LocalDateTime.now();
-
最好把日期类型放到对象里传入过去
4、手写Feign核心功能
通过打断点,可以发现,我们注入的UserOrderFeign 是被代理过的

- 接口是不能做事情的
- 如果想做事,必须要有对象
- 那么这个接口肯定是被创建处代理对象的
- 动态代理:1.jdk(java interface 接口 $proxy) 2.cglib(subClass 子类)
- jdk动态代理,只要是代理对象调用的方法,必须走
java.lang.reflect.InvocationHandler#invoke 方法
开始手写
-
我们先在启动类上把RestTemplet 类给放到Bean中,并添加@LoadBalanced 注解,让Ribbon托管 @Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
-
在测试类中,使用jdk的动态代理,代理UserOrderFeign 接口,并重写方法 package com.tcc;
import com.tcc.feign.UserOrderFeign;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@SpringBootTest
class UserAApplicationTests {
@Autowired
private RestTemplate restTemplate;
@Test
void contextLoads() {
UserOrderFeign o = (UserOrderFeign) Proxy.newProxyInstance(UserOrderFeign.class.getClassLoader(), new Class[]{UserOrderFeign.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> aClass = method.getDeclaringClass();
FeignClient aClassAnnotation = aClass.getAnnotation(FeignClient.class);
String orderService = aClassAnnotation.value();
RequestMapping annotation = method.getAnnotation(RequestMapping.class);
String[] paths = annotation.value();
String path = paths[0];
String url = "http://" + orderService + "/" + path;
String forObject = restTemplate.getForObject(url, String.class);
return forObject;
}
});
String s = o.buyCar();
System.out.println(s);
}
}
5、配置日志
-
在启动类上把Feign 的Level 类注入到Bean中 @Bean
public Logger.Level level(){
return Logger.Level.FULL;
}
-
在配置文件中进行配置 logging:
level:
com.tcc.feign.UserOrderFeign: debug
-
访问接口进行测试 
|