当前位置: 首页 > article >正文

自定义BeanPostProcessor之Feign组件服务间优雅调用

Feign是什么

feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service。Spring Cloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。

Feign怎么使用

@FeignClient(value = "service-name")
public interface CartFeignClient {

    @PostMapping("/cart/{productId}")
    Long addCart(@PathVariable("productId")Long productId);
}

上面是最简单的feign client的使用,声明完为feign client后,其他spring管理的类,如service就可以直接注入使用了,例如:

//这里直接注入feign client
@Autowired
private CartFeignClient cartFeignClient;

@PostMapping("/toCart/{productId}")
public ResponseEntity addCart(@PathVariable("productId") Long productId){
    Long result = cartFeignClient.addCart(productId);
    return ResponseEntity.ok(result);
}

可以看到,使用feign之后,我们调用eureka 注册的其他服务,在代码中就像各个service之间相互调用那么简单。

常规Feign服务间调用

在本地研发环境开发时候可能需要调用其他服务,例如通过service-name的方式调用,但是如果经过注册中心,并且服务有多实例的情况下会出现超时的情况。可以通过指定服务ip的方式调用。

@FeignClient(value="service-name", url = "http://127.0.0.1:2607/")

@FeignClient(value="service-name-1", url = "http://1.2.3.4:2607/")

Feign服务间调用更容易

如果引用的其他服务比较多,需要修改很多的url,这样比较浪费时间,通过重写@FeignClient的url,可进行统一处理,而不用一个一个的去修改。也可以配置研发环境,本地环境等多个环境的服务地址。
具体的实现方式是通过自定义BeanPostProcessor的方式,在bean初始化的时候动态替换。
BeanPostProcessor的原理可以看spring相关文章。

@Component
public class FeignClientsServiceNameAppendBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    private AtomicInteger atomicInteger = new AtomicInteger();
	// 需要替换的服务名称和ip集合
    public static final Map<String, String> SERVICE_MAP = new HashMap<>();

    private static String beanNameOfFeignClientFactoryBean = "org.springframework.cloud.netflix.feign.FeignClientFactoryBean";

    static {
        SERVICE_MAP.put("service-name", "http://127.0.0.1:2607");
        SERVICE_MAP.put("service-name-1", "http://1.2.3.4:8081");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String s) throws BeansException {
        return bean;
    }

    @SneakyThrows
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if (atomicInteger.getAndIncrement() == 0) {
            Class beanNameClz = Class.forName(beanNameOfFeignClientFactoryBean);

            applicationContext.getBeansOfType(beanNameClz).forEach((feignBeanName, beanOfFeignClientFactoryBean) -> {
                try {
                    setField(beanNameClz, "url", beanOfFeignClientFactoryBean);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                System.out.println(feignBeanName + "-->" + beanOfFeignClientFactoryBean);
            });
        }


        return bean;
    }

    private void setField(Class clazz,  Object obj) throws Exception {
    	// 获取FeignClientFactoryBean的name属性,找到服务名称。例如:service-name-1
        Field name = ReflectionUtils.findField(clazz, "name");
        Object nameValue = null;
        if (Objects.nonNull(name)) {
            ReflectionUtils.makeAccessible(name);
            nameValue = name.get(obj);
        }
        // 获取FeignClientFactoryBean的url字段
        Field field = ReflectionUtils.findField(clazz, "url");
        if (Objects.nonNull(field) && Objects.nonNull(nameValue)) {
            ReflectionUtils.makeAccessible(field);
            Object value = field.get(obj);
            // 找到指定配置替换url的值
            if (Objects.nonNull(value) && SERVICE_MAP.containsKey(nameValue)) {
                value = SERVICE_MAP.get(nameValue);
                ReflectionUtils.setField(field, obj, value);
            }
        }


    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

http://www.kler.cn/news/160518.html

相关文章:

  • Istio可观测性
  • MySql MVCC 详解
  • 机器的深度强化学习算法可以被诱导
  • 【13】PyQt多线程多任务管理
  • 虚拟机配置网络(这里以centos为例)
  • 操作系统的特征
  • Java集合进阶(上)
  • 0基础学java-day14
  • logback整合rabbitmq实现消息记录日志
  • 关于域名、ssl证书的一些问题
  • ThreadX开源助力Microsoft扩大应用范围:对比亚马逊AWS的策略差异
  • 在cmd下查看当前python的版本
  • vue2+typescript使用高德地图2.0版本
  • 物联网安全芯片ACL16 采用 32 位内核,片内集成多种安全密码模块 且低成本、低功耗
  • 【1】基于多设计模式下的同步异步日志系统-项目介绍
  • 5-redis高级-哨兵
  • vue3 Hooks函数使用及常用utils封装
  • LeetCode双指针:第一个错误的版本
  • Redis Reactor事件驱动模型源码
  • Linux-centos上如何配置管理NFS服务器?
  • 数据分析中的绝地反击:如何解救一个陷入困境的数据模型
  • IDEA切换Python虚拟环境
  • Vue3计算属性与监听属性和生命周期
  • Linux网卡命名规则
  • Spring Boot学习(三十三):集成kafka
  • 让关节远离疼痛,重拾健康活力
  • Java架构师系统架构设计原则应用
  • 13款趣味性不错(炫酷)的前端动画特效及源码(预览获取)分享(附源码)
  • 解决Error:You‘re using an RSA key with SHA-1, which is no longer allowed
  • 多关键字排序(java实训)