Spring-Cloud-Feign-03

1、快速入门

1、创建一个提供者

这里就拿用户下订单举例

  1. 创建一个SpringCloud项目,引入以下依赖即可

    在这里插入图片描述

  2. 项目创建好后,记得修改pom.xml文件的springcloud依赖

    在这里插入图片描述

  3. 然后在启动类上添加@EnableEurekaClient注解开启eureka服务

  4. 配置application.xml文件,注册到eureka上

    在这里插入图片描述

  5. 修改完后,创建一个下订单的Controller,编写一个接口用来测试

    package com.tcc.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author 宇辰
     * @date 2022/9/27-8:59
     **/
    @RestController
    public class TestController {
    
        @RequestMapping("buyCar")
        public String buyCar(){
            return "劳斯莱斯-幻觉";
        }
    }
    
  6. 最后,启动项目,打开eureka地址查看order-a服务是否注册成功

2、创建一个调用者(主要)

调用者需要引入OpenFeign依赖

  1. 创建项目和上面的步骤一样,除了创建Controller,依赖引入如下

    在这里插入图片描述

  2. 创建好项目,修改好pom文件,配置好xml文件,配置好Eureka后,编写Controller

  3. Controller需要调用order-a服务提供的接口,步骤如下

  4. 先在启动类上添加注解@EnableFeignClients // 开启feign的客户端,才可以帮助我们发起调用

  5. 添加好后,我们使用OpenFeign写法,创建一个文件夹,在里面创建接口,名字为UserOrderFeign

    package com.tcc.feign;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @author 宇辰
     * @date 2022/10/3-11:32
     **/
    @FeignClient("order-a") // value 为被调用的服务的应用名称
    public interface UserOrderFeign {
    
        /**
         * 需要调用哪个接口,就写它的方法签名
         * 方法签名就是包含一个方法的所有属性
         * @return
         */
        @RequestMapping("buyCar")
        public String buyCar();
    }
    
  6. 编写好接口后,我们在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;
    
    /**
     * @author 宇辰
     * @date 2022/10/3-11:29
     **/
    @RestController
    public class TestController {
    
        // 注入写好的Feign接口
        @Autowired
        private UserOrderFeign userOrderFeign;
    
        @RequestMapping("doOrder")
        public String doOrder(){
            String doOrder = userOrderFeign.buyCar();
            return doOrder;
        }
    
    }
    
  7. 编写完毕后我们访问http://localhost:8090/doOrder调用接口,查看是否可以成功调用

    在这里插入图片描述

2、调用超时设置

测试:在order-a服务提供的接口里延迟两秒再返回内容,看看user-a是否可以正常获取

  1. order-a接口修改:

在这里插入图片描述

  1. 修改完毕后,重启服务,然后再次调用doOrder接口,查看结果

    在这里插入图片描述

结论:

当被调用的服务长时间无响应的时候,就停止访问,默认为1秒

RIbbon默认调用时长为1s,可以修改,超时调整,可以查看DefaultClientConfigImpl

在这里插入图片描述

ribbon:
  ReadTimeout: 5000 # 修改调用时长为5s
  ConnecTimeout: 5000 # 修改连接时长为5s

3、OpenFeign调用参数处理

Feign传参确保消费者和提供者的参数列表一致,包括返回值,方法签名要一致

  1. 通过URL传参,GET请求,参数列表使用@PathVariable("")
  2. 如果是GET请求,每个基本参数必须加@RequestParam("")
  3. 如果是POST请求,而且是对象集合等参数,必须加@Requestbody或者@RequestParam

举例:

  1. 先在order-a和user-a两个服务编写User

    package com.tcc.entity;
    
    import lombok.*;
    
    /**
     * @author 宇辰
     * @date 2022/10/3-19:45
     **/
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public class User {
        private String name;
        private Integer age;
    }
    
  2. 然后在order-a服务编写好接口

    package com.tcc.controller;
    
    import com.tcc.entity.User;
    import org.springframework.web.bind.annotation.*;
    
    /**
     * @author 宇辰
     * @date 2022/10/3-19:40
     **/
    @RestController
    public class ParamController {
    
        /**
         * 路径中传参    使用PathVariable注解
         * @param name
         * @param age
         * @return
         */
        @GetMapping("testUrl/{name}/and{age}")
        public String testUrl(@PathVariable("name") String name, @PathVariable("age") Integer age){
            return "姓名:"+name+",年龄:"+age;
        }
    
        /**
         * Get请求传参,使用@RequestParam("name")  后面需要加上value
         * @param name
         * @return
         */
        @GetMapping("onParam")
        public String onParam(@RequestParam("name") String name){
            return "姓名:" + name;
        }
    
        /**
         * Post请求,传入一个对象,使用@RequestBody
         * @param user
         * @return
         */
        @PostMapping("onObject")
        public String oneObject(@RequestBody User user){
            return "姓名:"+user.getName()+",年龄:"+user.getAge();
        }
    
        /**
         * Post请求,传入一个对象,一个基本参数 对象使用;@RequestBody   基本参数使用:@RequestParam("sex") 后面需要加上value
         * @param user
         * @param sex
         * @return
         */
        @PostMapping("onObjectAndOnParam")
        public String onObjectAndOnParam(@RequestBody User user, @RequestParam("sex") String sex){
            return "姓名:"+user.getName()+",年龄:"+user.getAge()+",性别:" + sex;
        }
    }
    
  3. user-a服务的Feign中引入需要调用的方法签名

    package com.tcc.feign;
    
    import com.tcc.entity.User;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.*;
    
    /**
     * @author 宇辰
     * @date 2022/10/3-11:32
     **/
    // value 为服务的应用名称
    @FeignClient("order-a")
    public interface UserOrderFeign {
    
        /**
         * 需要调用哪个接口,就写它的方法签名
         * 方法签名就是包含一个方法的所有属性
         * @return
         */
        @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);
    }
    
  4. 在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";
    }
    
  5. 访问http://localhost:8090/testParam地址,查看是否调用成功

    在这里插入图片描述

  6. 结果

    在这里插入图片描述

传递事件日期参数的坑

传过去的日期参数,会相差12个小时

  1. 先在order-a服务编写接口,参数为Date类型。

    @GetMapping("oneDate")
    public Date oneDate(@RequestParam("date") Date date){
        return date;
    }
    
  2. UserOrderFeign接口中写好方法签名

    @GetMapping("oneDate")
    public Date oneDate(@RequestParam("date") Date date);
    
  3. 然后在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;
    }
    
  4. 访问接口,查看控制台打印,可以发现相差12小时

    在这里插入图片描述

解决办法
  1. 可以把日期先转为字符串类型,再传过去

  2. 可以使用jdk8提供的日期类型

    LocalDate now = LocalDate.now(); // 没有问题
    LocalDateTime now1 = LocalDateTime.now(); // 会丢失s
    
  3. 最好把日期类型放到对象里传入过去

4、手写Feign核心功能

通过打断点,可以发现,我们注入的UserOrderFeign是被代理过的

在这里插入图片描述

  • 接口是不能做事情的
  • 如果想做事,必须要有对象
  • 那么这个接口肯定是被创建处代理对象的
  • 动态代理:1.jdk(java interface 接口 $proxy) 2.cglib(subClass 子类)
  • jdk动态代理,只要是代理对象调用的方法,必须走java.lang.reflect.InvocationHandler#invoke方法

开始手写

  1. 我们先在启动类上把RestTemplet类给放到Bean中,并添加@LoadBalanced注解,让Ribbon托管

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    
  2. 在测试类中,使用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 {
    
        // 自动注入被Ribbon托管的RestTemplet
        @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 {
                    // 想要远程调用就必须获得服务名+接口地址,然后拼接:http://服务名/接口地址 最后用RestTemplet调用接口即可
    
                    // 通过方法获得类名,通过类名获得类名上的注解,再通过注解获取里面的值(服务名)
                    Class<?> aClass = method.getDeclaringClass();
                    FeignClient aClassAnnotation = aClass.getAnnotation(FeignClient.class);
                    String orderService = aClassAnnotation.value(); // order-a
    
                    // 通过方法获得注解,通过注解获得里面的值(接口地址)
                    RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                    String[] paths = annotation.value();
                    String path = paths[0]; // buyCar
    
                    // 拼接   http://order-a/buyCay
                    String url = "http://" + orderService + "/" + path;
                    String forObject = restTemplate.getForObject(url, String.class);
                    return forObject; // 劳斯莱斯-幻觉
                }
            });
    
            // 调用方法 走代理对象的invoke方法
            String s = o.buyCar();
            System.out.println(s); // 劳斯莱斯-幻觉
    
        }
    
    }
    

5、配置日志

  1. 在启动类上把FeignLevel类注入到Bean中

    @Bean
    public Logger.Level level(){
        return Logger.Level.FULL;
    }
    
  2. 在配置文件中进行配置

    logging:
      level:
        com.tcc.feign.UserOrderFeign: debug # 打印这个接口下的日志
    
  3. 访问接口进行测试

    在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值