你不知道的事—SringCloud的feign的继承特性

  • A+

前言

说起SpringChoud的feign大家用过的都说好。Feign是Netflix开发的声明式、模板化的HTTP客户端。对于我们微服务来说,微服务之间的api调用,使用feign来说是再方便不过的。本文先介绍一下,传统的feign的调用写法方式,再介绍我们的重点feign的继承特性。
feign的继承特性有很多的好处,可以进行参数和方法的统一管理,一次修改,feign和具体的controller都变了。
总之好处还是不少的。
你不知道的事---SringCloud的feign的继承特性

传统的feign的实现方式

传统的feign是怎样的实现的呢,我们先通过springmvc搞了一个controller,在controller里面实现我们代码。此时另一个微服务想直接调用这个请求,那么被调用的微服务就可以声明一个feign的客户端,将自身要提供给外部调用的方法,feign提供的方法的requestMapper路径映射和controller中的保持一致即可访问的到。

传统feign的代码实现

我们先写一个传统的controller。我们的目的很简单,这个controller来返回123。

@RestController
public class DemoController {

    @PostMapping("/demo/list")
    public String demo(
            @RequestBody DemoFeignQueryVO demoFeignQueryVO){
      return "123";
    }
}

有了controller,我们来写feign。feign的代码是十分简单的,只要保证feignClint的值是我们要调用的微服务的值,然后其中的postMapping的值和上方controller的一样即可调用到。

@FeignClient(value = "user-system")
@Component
public interface IDemoFeign {

    @PostMapping("/demo/list")
    public String demo(
            @RequestBody DemoFeignQueryVO demoFeignQueryVO);

}

传统feign的缺点

由上方的代码来看,有什么缺点呢,比如我要更改controller的参数,那么我需要改两处地方,一处是自己的controller实现,一处是feign的接口。如果忘改了的话,就会产生未知的错误。如果更改了controller的路径映射呢。同样的,也需要改两处位置。这是传统形式的一种缺点。

feign的继承特性

如果我们使用feign的继承特性,就不会有上方的情况产生,继承特性就是说,我们弄一个共用的接口,在接口上布置我们的requestMapping注解,规定我们的方法参数。然后通过controller实现这个接口。controller就可以针对接口中的方法进行一一的实现。然后针对于我们的feign接口,我们直接继承共用的接口。这样feign接口和controller方法是完全保持一致的,当需要修改的时候,完全不需要两个地方一起修改,直接更改共用的接口即可。

feign的继承特性的代码实现

我们将上述的传统方案的代码进行一个改写,通过对比,就可以看出好处。
先声明一个共用的接口。这里面写我们所有要提供给外面的方法。

public interface DemoInterface {
 
    @PostMapping("/demo/list")
    public String demo(
            @RequestBody DemoFeignQueryVO demoFeignQueryVO);
    
}

有了接口后,我们的controller直接实现这个接口。

@RestController
public class DemoController implements DemoInterface {

    @Override
    public String demo(
            @RequestBody DemoFeignQueryVO demoFeignQueryVO){
      return "123";
    }
}

我们此时就可以看到,controller不用关心路径映射了。而且针对于参数来说,如果接口一修改,controller必然会提示报错,编译不会通过,不会使你漏下改动。
接下来,实现一下我们的feign,feign是极其简单的,只要继承共用的接口即可。

@FeignClient(value = "user-system")
public interface DemoFeign extends DemoInterface  {
}

看上面是不是觉得非常方便。feign和controller都不用关心路径,通过共用端口保证了路径一定一致,接口的参数检查也保障了参数不会产生问题。

feign继承特性带来的requestMapping加载问题

在我们上述的提供方案中,如果requestMapping只是在注解上,是不会出现这种问题的。但是如果当requestMapping在controller的类上的时候,那么问题就来了。我们先看一下SpringMvc加载requestMapping到容器的逻辑是什么

@Override
protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

这个是处理请求映射的判断依据。从实现中我们看到,只要被扫描的类包含了@Controller或@RequestMapping注解,就会被springMVC加载,那么controller实现了接口具备了requestmapping。feignClient也继承了接口,那么也就具备了requestMapping。两个一摸一样的requstMapping,那么是必然会产生冲突的。那么我们的目的是什么呢。controller的正常加载,feignClient不应该进行加载。
我们重新修改下feign的配置类。

@Configuration
@ConditionalOnClass({Feign.class})
public class FeignConfiguration {
    @Bean
    public WebMvcRegistrations feignWebRegistrations() {
        return new WebMvcRegistrationsAdapter() {
            @Override
            public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
                return new FeignRequestMappingHandlerMapping();
            }
        };
    }
    private static class FeignRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
        @Override
        protected boolean isHandler(Class<?> beanType) {
            return super.isHandler(beanType) &&
                    !AnnotatedElementUtils.hasAnnotation(beanType, FeignClient.class);
        }
    }
}

上述代码我们进行了一个排除,带feignClint我们不进行加载。
至此feign继承特性的一个隐藏的坑在这里已经没了,大家可以放心使用了。
你不知道的事---SringCloud的feign的继承特性

总结

feign的继承特性是十分方便的一种方式,实实在在的减少了代码编写量,也保证了路径映射和参数的一致性。十分好用。
文中难免有不足,欢迎大家批评指正。

90DIR-CMD