关于Feign的接口调用坑点

文章介绍了在使用Feign进行微服务调用时遇到的连接、空指针和参数获取问题,以及如何通过FallbackFactory实现服务降级。同时,讨论了在Spring启动后如何正确获取并使用Feign对象,包括@Autowired注解的使用限制和静态字段的注入问题,提出了解决方案。
摘要由CSDN通过智能技术生成
0. 前言

关于Feign的调用前面自己在学习微服务的时候没有过多留意,后面到了公司真正调用的时候,一堆问题,连都连不通,要么空指针异常,要么获取不到参数,花了大半个下午还没解决,回家后特地自己建了两个微服务进行实验,觉得要好好总结一下知识点,要不然太对不起我下午花的时间了。

1. Feign 的服务降级

业务失败后,不能直接报错,而应该返回用户一个友好提示或者默认结果,这个就是失败降级逻辑。

实现方式

①方式一:FallbackClass,无法对远程调用的异常做处理

②方式二:FallbackFactory,可以对远程调用的异常做处理,我们选择这种

  1. Feign 接口

    @FeignClient(value = "user-service",fallbackFactory = UserFeignServiceFallBackFactory.class)
    public interface UserFeignService {
        @GetMapping("all/{ids}")
        Result<List<JSONObject>> getAll(@PathVariable String ids);
    }
    
  2. 实现 FallbackFactory<T> 类

    public class UserFeignServiceFallBackFactory implements FallbackFactory<UserFeignService> {
        @Override
        public UserFeignService create(Throwable throwable) {
            return new UserFeignService() {
                @Override
                public Result<List<JSONObject>> getAll(String ids) {
                    System.out.println(throwable);
                    return null;
                }
            };
        }
    }
    
踩坑点:

不管服务降不降级,在接口上面都不能加 @RequestMapping(),要不然可能会报错

Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource

内部好像是因为在初始化加载bean时,Spring会通过 @Controller 和 @RequesMapping 来判断是否为Controller对象,所以若加了 @RequestMapping 会被Spring误判,导致bean对象创建失败

2. 从Spring获取Feign对象

在启动类配置好 @EnableFeignClients() 后,Spring在启动的时候就会自动生成 Feign接口的实例对象 ,正常情况在Spring管理的对象中进行注入调用一般没什么问题,但是我的一些骚操作,引出了一堆问题:

需求:在项目启动的时候就要调Feign进行一些数据的初始化,所以单独写一个类 InitGetData.class 用来初始化,在启动类中调用

@EnableFeignClients("cn.itcast.order") //开启Feign功能
@MapperScan("cn.itcast.order") //扫描包,配置接口实现类
@SpringBootApplication
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
        
        InitGetData initGetData = new InitGetData();
        initGetData.init();
    }
}

在 SpringApplication.run(OrderApplication.class, args) 后面再进行调用,确保Spring服务启动后能注册进 nacos 中来实现Feign连接

我之前 InitGetData.class 写法为:

public class InitGetData {

    @Autowired
    private UserFeignService userFeignService;

    public void init() {
        String ids = "柳岩,文二狗,郑爽爽";
        Result<List<JSONObject>> result = userFeignService.getAll(ids);
        List<JSONObject> jsObjects = result.getData();
        System.out.println(jsObjects);
    }
}

当时想的是,UserFeignService的实例对象在Spring初始化的时候就生成了,所以能自动注入进去
然而报错了

Exception in thread "main" java.lang.NullPointerException

空指针,因为 InitGetData 实例对象不受Spring管理,而 @Autowired 是在Spring初始化的时候就把所有bean对象生成了并对管理的实例对象进行自动注入,后面不会再监听注入,所以后面调用对象报空指针,因为不受Spring管理,对象没有注入进去

方法1,让Spring来管理该对象,Spring初始化的时候进行注入
@Component
public class InitGetData {

    @Autowired
    private UserFeignService userFeignService;

    public void init() {
        String ids = "柳岩,文二狗,郑爽爽";
        Result<List<JSONObject>> result = userFeignService.getAll(ids);
        List<JSONObject> jsObjects = result.getData();
        System.out.println(jsObjects);
    }
}
@EnableFeignClients("cn.itcast.order") //开启Feign功能
@MapperScan("cn.itcast.order") //扫描包,配置接口实现类
@SpringBootApplication
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
        // SpringUtils.getBean - 获取Spring容器中的实例对象
        InitGetData initGetData = SpringUtils.getBean(InitGetData.class);
        initGetData.init();
    }
    
}
拓展:

若想声明为 static 类型来进行自动注入,则写法应为

	private static UserFeignService userFeignService;
    @Autowired
    private void setUserFeignService(UserFeignService userFeignService) {
        this.userFeignService = userFeignService;
    }

要不然可能会报错,大致原因应该是Spring构造实例对象的时候三级缓存,实例化和属性赋值要分开完成,如果都在实例化这一步进行,会造成bean对象创建失败,具体原因还不是很清楚,因为普通对象之间注入可以正常运行,但是static类型直接注入我报错了

方法2,从Spring容器中拿到实例手动注入
public class InitGetData {

    private static UserFeignService userFeignService;

    public void init() {
        String ids = "柳岩,文二狗,郑爽爽";
        Result<List<JSONObject>> result = getUserFeignService().getAll(ids);
        List<JSONObject> jsObjects = result.getData();
        System.out.println(jsObjects);
    }

    private static UserFeignService getUserFeignService() {
        if (userFeignService == null) {
            synchronized (InitGetData.class) {
                if (userFeignService == null) {
                    userFeignService = SpringUtils.getBean(UserFeignService.class);
                }
                return userFeignService;
            }
        }
        return userFeignService;
    }

}
@EnableFeignClients("cn.itcast.order") //开启Feign功能
@MapperScan("cn.itcast.order") //扫描包,配置接口实现类
@SpringBootApplication
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
        
        InitGetData initGetData = new InitGetData();
        initGetData.init();
    }
}
拓展:

手动去容器拿实例声明出对象,可以用单例模式-懒汉式(静态内部类)

	private static class LazyLoadUserFeignService {
        public final static UserFeignService userFeignService = SpringUtils.getBean(UserFeignService.class);
    }
    public static UserFeignService getUserFeignService() {
        return LazyLoadUserFeignService.userFeignService;
    }
3. 小结

想复盘下午遇到的问题,结果牺牲了一晚上下班的时间,写了这么久,但还是值得的,至少没白白浪费时间,又把相关知识梳理了一遍,犯错的根本原因还是把原理忘记了,这以后就是低级错误了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值