自定义BeanPostProcessor之Spring Cloud 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;
    }
  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值