Feign、OpenFeign

简介

1、什么是 feign、openfeign

feign 是 Spring Cloud Netflix组件中的一个轻量级 Restful 的 HTTP服务客户端,内置了 Ribbon 和 RestTemplate,是一种声明式、模板化的 HTTP客户端(仅在 Consumer 中使用),feign 不支持 SpringMVC 的注解,它有一套自己的注解

openfeign 是 Spring Cloud 在 feign 的基础上支持了 SpringMVC 的注解,例如:@RequestMapping、@Pathvariable 等,

openfeign 的 @FeignClient 注解可以解析 SpringMVC 的 @RequestMapping注解下的接口,使得服务调用变得特别方便。

1.1、使用 OpenFeign 的简介

Feign 中全是远程调用接口,可以把 Feign 抽离成一个模块,让 consumer 更干净简洁。

Feign 是一个声明式的 WebService客户端。

springcloud 对 Feign进行了封装,使其支持了 Spring MVC 的相关注解。Feign默认还集成了 Ribbon,并可以和 Eureka结合,默认实现了负载均衡的效果。

从 consumer -> 注册中心 -> producer的架构,抽离出一层远程服务调用,

变成 consumer -> Feign -> 服务中心 -> producer,Feign则是被抽离到了接口中。

使用方法:(消费方访问 consumer,consumer会调用 Feign,由 Feign发起远程服务调用)

  1. 定义一个服务接口

  2. 在上面添加注解

1.2、Client 和 Controller 返回类型不一致

有时候可能业务场景不同,甲方的 Controller 给的返回值是 A类型,但是乙方不想要 A类型,我们通常以为 Client 必须和 Controller 的返回类型一致,其实是可以不同的。

OpenFeign 在做代理的时候,会将得到的 json字符串结果进行映射,如果参数名称一致,并且参数类型可以做转化,就将结果进行参数映射,如果不行就设为 null。

1.2.1、实例1

1.2.1.1、返回类型不同
@PostMapping("/elastic/document/_search")
R<SearchHitsDTO> search(@RequestBody ElasticRequestDTO request);
@Data
@Accessors(chain = true)
@JsonIgnoreProperties(ignoreUnknown = true)
public class SearchHitsDTO implements Serializable {
    private String maxScore;
    private Long totalHits;
    private List<HitsDTO> hits;
}
​
​
​
@ApiOperation(value = "查询文档")
@PostMapping("/document/_search")
public R<SearchHits> search(@RequestBody ElasticRequestDTO request) {
    return documentElasticSearchService.getDocumentList(request);
}
public final class SearchHits implements Streamable, ToXContentFragment, Iterable<SearchHit> {
    public static final SearchHit[] EMPTY = new SearchHit[0];
    private SearchHit[] hits;
    public long totalHits;
    private float maxScore;
    // ...
}
1.2.1.2、代理的参数映射
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (!"equals".equals(method.getName())) {
        if ("hashCode".equals(method.getName())) {
            return this.hashCode();
        } else {
            return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
        }
    } else {
        try {
            Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
            return this.equals(otherHandler);
        } catch (IllegalArgumentException var5) {
            return false;
        }
    }
}
​
public boolean equals(Object obj) {
    if (obj instanceof ReflectiveFeign.FeignInvocationHandler) {
        ReflectiveFeign.FeignInvocationHandler other = (ReflectiveFeign.FeignInvocationHandler)obj;
        return this.target.equals(other.target);
    } else {
        return false;
    }
}

2、简单使用

2.1、实例1

2.1.1、相关依赖

<dependencies>
  <!-- 服务注册 -->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  </dependency>
  
  <!-- 服务调用feign -->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
  </dependency>
  
  <!-- spring boot web -->
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
</dependencies>

2.1.2、服务者

服务方就正常写业务,然后正常把自己注册进服务中心就好了,一切照旧进行。

2.1.2.1、application.yml
server:
  # 服务端口
  port: 8101
  
spring:
  application:
    # 服务名称
    name: service-hello
  # 环境设置:dev、test、prod
  profiles:
    active: dev
  # nacos服务地址
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
2.1.2.2、业务代码

因为是测试实例,所以就直接写在 Controller 里就好了

@RestController
@RequestMapping("/admin/hello")
public class HelloController {
  @GetMapping("sayHello")
  public String sayHello() {
      System.out.println("这里是 hello服务方");
      return "hello";
  }
}
2.1.2.3、启动类

主要就是加上 @EnableDiscoveryClient这个注解,进行服务注册

@SpringBootApplication
@ComponentScan(basePackages = {"com.chw"})
@EnableDiscoveryClient
public class ServiceHelloApplication {
  public static void main(String[] args) {SpringApplication.run(ServiceHelloApplication.class, args);}
}

2.1.3、服务调用客户端

feign客户端都不需要做什么配置,主要就是用一个 @FeignClient注解

2.1.3.1、客户端接口
// 服务方在注册中心的服务名称
@FeignClient("service-hello")
@Service
public interface HelloFeignClient {
    // 服务方的接口路径
    @GetMapping("/admin/hello/sayHello")
    String sayHello();
}

2.1.4、消费者

记得在 pom.xml 中引入 feign客户端模块的依赖

2.1.4.1、配置中心
server:
  # 服务端口
  port: 8102
  
spring:
  application:
    # 服务名
    name: service-hi
  # 环境设置:dev、test、prod
  profiles:
    active: dev
  cloud:
    nacos:
      discovery:
        # nacos服务地址
        server-addr: localhost:8848
2.1.4.2、Controller
@RestController
@RequestMapping("/admin/hi")
public class HiController {
    @Autowired
    private HiService hiService;
    
    @GetMapping("sayHi")
    public String sayHi() {
        System.out.println("这里是 hello调用方");
        hiService.sayHi();
        return "hi";
    }
}
2.1.4.3、Service层

自动注入我们定义的服务端接口,然后调用里面的方法就好了。

public interface HiService {
    void sayHi();
}
​
​
​
@Service
public class HiServiceImpl implements HiService {
    @Autowired
    private HelloFeignClient helloFeignClient;
    
    @Override
    public void sayHi() {
        String hello = helloFeignClient.sayHello();
        System.out.println("得到服务方的:" + hello);
        System.out.println("回应 hi");
    }
}
2.1.4.4、启动类

除了要加 @EnableDiscoveryClient 这个注解,发现注册中心的服务外,主要还要用 @EnableFeignClients(basePackages = {"com.chw"}) 这个注解,开启 feign 的客户端

@SpringBootApplication
@ComponentScan(basePackages = {"com.chw"})
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"com.chw"})
public class ServiceHiApplication {
    public static void main(String[] args) {SpringApplication.run(ServiceHiApplication.class, args);}
}

实例2

FeignConfiguration

@Configuration
@Slf4j
public class FeignConfiguration {
    /**
     * The constant OBJECT_MAPPER.
     */
    private static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    static {
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }
    /**
     * Feign logger level logger . level.
     *
     * @return the logger . level
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        //这里记录所有,根据实际情况选择合适的日志level
        //主要看feign 请求url的具体参数
        return Logger.Level.FULL;
    }
    /**
     * Feign decoder decoder.
     *
     * @return the decoder
     */
    @Bean
    public Decoder feignDecoder() {
        //feign 返回值解码问题
        return new Decoder() {
            @SneakyThrows
            @Override
            public Object decode(final Response response, final Type type) throws FeignException {
                if (response.status() == 404) {
                    return Util.emptyValueOf(type);
                }
                if (response.body() == null) {
                    return null;
                }
                Reader reader = response.body().asReader(StandardCharsets.UTF_8);
                if (!reader.markSupported()) {
                    reader = new BufferedReader(reader, 1);
                }
                try {
                    // Read the first byte to see if we have any data
                    reader.mark(1);
                    if (reader.read() == -1) {
                        return null; // Eagerly returning null avoids "No content to map due to end-of-input"
                    }
                    reader.reset();
                    Object ret = OBJECT_MAPPER.readValue(reader, OBJECT_MAPPER.constructType(type));
                    log.info("invoke facade result {}", JSONUtil.toJsonStr(ret));
                    return ret;
                } catch (RuntimeJsonMappingException e) {
                    if (e.getCause() != null && e.getCause() instanceof IOException) {
                        throw IOException.class.cast(e.getCause());
                    }
                    throw e;
                }
            }
        };
    }
}

feignClient

@FeignClient(name = "userAddressClient", url = "http://yyf2-dev.ringnex.com", configuration = FeignConfiguration.class)
public interface UserAddressClient {
  @GetMapping("/bff-user-api/customer/user/address/{userNo}")
  UserAddressResponse<List<RnUserAddressVO>> getByUserNo(@PathVariable("userNo") String userNo);
}

feignservice

这块也可以做成 service、impl 的形式。也可以直接拿到自己的 service中直接用,但还是推荐封装一下。

@Service
@Slf4j
public class UserAddressService {
  @Resource
  private UserAddressClient userAddressClient;
  
  public List<RnUserAddressVO> getByUserNo(String userNo) {
      UserAddressResponse<List<RnUserAddressVO>> response = userAddressClient.getByUserNo(userNo);
      List<RnUserAddressVO> result = null;
      if (Objects.nonNull(response) && Objects.equals(response.getCode(), CommonConstant.ONE) && Objects.nonNull(response.getData())) {
          result = response.getData();
      }
      return result;
  }
}

controller

@Api(tags = "粉丝收货地址接口")
@RestController
@Slf4j
@RequestMapping("fansAddress")
public class FansAddressController {
  @Resource
  private FansAddressBizService fansAddressBizService;
​
  @Operation(description = "获取用户所有地址")
  @GetMapping("/internal")
  public ApiResponse<List<UserAddressVO>> getUserAddressList() {
      return ApiResponse.ok(fansAddressBizService.getUserAddressList());
  }
}

service

public interface FansAddressBizService {
  List<UserAddressVO> getUserAddressList();
}

impl

@Service
@Slf4j
public class FansAddressBizServiceImpl implements FansAddressBizService {
  @Resource
  private UserAddressService userAddressService;
​
  @Override
  public List<UserAddressVO> getUserAddressList() {
    List<RnUserAddressVO> userAddressList = null;
    userAddressList = userAddressService.getByUserNo("d22ac7127be44959b27c685de70ab777");
    return Optional.ofNullable(userAddressList)
        .map(it -> it.stream().filter(item -> Objects.equals(item.getStatus(), CommonConstant.ONE) && Objects.equals(item.getType(), CommonConstant.ONE)).collect(Collectors.toList()))
        .map(item -> {
            List<UserAddressVO> result = Lists.newArrayList();
            userAddressConvert.toVo(result, item);
            return result;
        }).orElse(Lists.newArrayList());
  }
}

实例3 - OpenFegin 实现接口参数签名认证

https://juejin.cn/post/7018579522039578660

https://juejin.cn/post/7019228266347102222

3、负载均衡

3.1、自定义负载均衡策略

LoadBalancer默认提供了两种负载均衡策略:

  • (默认) RoundRobinLoadBalancer:轮询分配策略

  • RandomLoadBalancer:随机分配策略

3.1.1、写一个 config配置类

不需要添加 @Configuration注解。

public class LoadBalancerConfig {
  // 将官方提供的 RandomLoadBalancer 注册为 Bean
  @Bean
  public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory){
    String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
    return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
  }
}

3.1.2、修改 RestTemplate配置类

@Configuration
// 指定为 userservice服务,只要是调用此服务都会使用我们指定的策略。
// spring.application.name 中指定的服务名称
@LoadBalancerClient(value="userservice",configuration=LoadBalancerConfig.class)
public class RestTemplateConfig {
  @Bean
  @LoadBalanced
  RestTemplate restTemplate(){
    return new RestTemplate();
  }
}

4、性能调优

4.1、Gzip

gzip是一种数据格式,采用 deflate算法压缩数据,多用在 Linux中。

网络数据经过压缩后可降低网络传输的字节数,可加快网页加载速度,

4.1.1、HTTP协议关于压缩传输的规定

  1. 客户端向服务器的请求中带有:Accept-Encoding:gzip, deflate 字段,向服务器表示客户端支持的压缩格式,如果不发送消息头,服务端默认是不会压缩的

  2. 服务端收到请求后,若发现请求头中有:Accept-Encoding 字段,并且支持该类型的压缩,就会将响应报文压缩后返回给客户端,并且携带:Content-Encoding:gzip 消息头,表示响应报文是根据该格式进行压缩的,

  3. 客户端接收到请求后,先判断是否含有:Content-Encoding:gzip 消息头,若有的话就按该格式解压报文,否则就按正常报文处理

4.2、feign的压缩

4.2.1、全局

对客户端浏览器、消费者、服务者之间都实现压缩

4.2.1.2.1、消费者的配置文件
server:
  port: 8001
  compression:
    # 是否开启 gzip压缩
    enbaled: true
    # 配置压缩支持的 MiME TYPE,可以不写,因为默认的就全部包括了
    mime-types: text/xml,text/plain,application/xml,application/json

4.2.2、局部

就是服务者与消费者之间的局部压缩

4.2.2.1、消费者的配置文件
feign:
  compression:
    request:
      # 配置压缩支持的 MiME TYPE
      mime-types: text/xml,application/xml,application/json
      # 配置压缩数据大小的最小阈值,默认 2048
      min-request-size: 512
      # 请求是否开启 gzip压缩
      enbaled: true
    response:
      # 响应是否开启 gzip压缩
      enbaled: true

4.3、请求超时

feign的负载均衡的底层就是 ribbon,默认情况下的超时时间是 1s,

4.3.1、全局

4.3.1.1、消费者的配置文件
ribbon:
  # 请求连接的超时时间
  ConnectTimeout: 5000
  # 请求处理的超时时间
  ReadTimeout: 5000

4.3.2、局部

4.3.2.1、消费者的配置文件
# 服务者在注册中心的服务名称
service-hello:
  ribbon:
    # 对所有请求都进行重试
    OkToRetryOnAllOperations: true
    # 对当前实例的请求次数
    MaxAutoRetries: 2
    # 切换实例的重试次数
    MaxAutoRetriesNextServer: 0
    # 请求连接的超时时间
    ConnectTimeout: 3000
    # 请求处理的超时时间
    ReadTimeout: 3000

进阶

0、SpringBoot 应用中的扫描触发流程

SpringApplication.run() -->

SpringApplication.refresh() -->

AbstractApplicationContext.refresh() --> AbstractApplicationContext.invokeBeanFactoryPostProcessors() -->

AbstractApplicationContext.invokeBeanDefinitionRegistryPostProcessors() -->

PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors() –>

ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()–>

ConfigurationClassPostProcessor.processConfigBeanDefinitions()–>

ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()–>

ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars -->

FeignClientsRegistrar.registerBeanDefinitions()

1、openfeign服务调用原理

1.1、@EnableFeignClients注解

主要是 import 了 FeignClientsRegistrar 这个类,然后里面有一个 clients 这个属性,就是会把配置了该注解的 class 放进去。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
    String[] value() default {};
    String[] basePackages() default {};
    Class<?>[] basePackageClasses() default {};
    Class<?>[] defaultConfiguration() default {};
    Class<?>[] clients() default {};
}

1.2、FeignClientsRegistrar

class FeignClientsRegistrar implements 
    ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
  private ResourceLoader resourceLoader;
  private Environment environment;
  // ...
}

可以看到它实现了很多接口,其中有 ImportBeanDefinitionRegistrar 这个接口,并实现了这个接口的方法。

FeignClientsRegistrar 重写的 registerBeanDefinitions方法

里面调用了两个方法,一个是注册默认的配置,另一个是注册 FeignClients,所以去看第二个方法。

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  this.registerDefaultConfiguration(metadata, registry);
  this.registerFeignClients(metadata, registry);
}

FeignClientsRegistrar 的 registerFeignClients方法

这个方法先是获取 EnableFeignClients 这个注解的所有属性,放入 Map,然后如果 attrs 这个 Map 不为空,就去获取 clients属性值,

如果 @EnableCleint 中配置了参数 clients 则根据设置类进行扫描,否则就根据 basePackages(就是主类所在包) 去扫描。反正最终就是得到那些 FeignClient 的类 Bean,这些就是候选组件。

方法最后迭代 candidateComponents 这些 client 候选组件,然后调用 this.registerFeignClient 方法去注册 FeignClient。

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet();
  Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
  new AnnotationTypeFilter(FeignClient.class);
  Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
  if (clients != null && clients.length != 0) {
    Class[] var13 = clients;
    int var15 = clients.length;
    for(int var17 = 0; var17 < var15; ++var17) {
      Class<?> clazz = var13[var17];
      candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
    }
  } else {
    ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
    scanner.setResourceLoader(this.resourceLoader);
    scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
    Set<String> basePackages = this.getBasePackages(metadata);
    Iterator var9 = basePackages.iterator();
    while(var9.hasNext()) {
      String basePackage = (String)var9.next();
      candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
    }
  }
  Iterator var14 = candidateComponents.iterator();
  while(var14.hasNext()) {
    BeanDefinition candidateComponent = (BeanDefinition)var14.next();
    if (candidateComponent instanceof AnnotatedBeanDefinition) {
      AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
      AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
      Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
      Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
      String name = this.getClientName(attributes);
      this.registerClientConfiguration(registry, name, attributes.get("configuration"));
      this.registerFeignClient(registry, annotationMetadata, attributes);
    }
  }
}

FeignClientsRegistrar 的 registerFeignClient 方法

这个方法主要是做一些数据的装配,但 BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class) 是非常重要的一步,为 FeignClient 生成了一个 BeanDefinitionBuilder,这是一个 Builder,里面有 Proxy代理,所以先去看看 FeignClientFactoryBean 这个类,再看看方法的实现。

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
    this.validate(attributes);
    definition.addPropertyValue("url", this.getUrl(attributes));
    definition.addPropertyValue("path", this.getPath(attributes));
    String name = this.getName(attributes);
    definition.addPropertyValue("name", name);
    String contextId = this.getContextId(attributes);
    definition.addPropertyValue("contextId", contextId);
    definition.addPropertyValue("type", className);
    definition.addPropertyValue("decode404", attributes.get("decode404"));
    definition.addPropertyValue("fallback", attributes.get("fallback"));
    definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
    definition.setAutowireMode(2);
    String alias = contextId + "FeignClient";
    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
    beanDefinition.setAttribute("factoryBeanObjectType", className);
    boolean primary = (Boolean)attributes.get("primary");
    beanDefinition.setPrimary(primary);
    String qualifier = this.getQualifier(attributes);
    if (StringUtils.hasText(qualifier)) {
        alias = qualifier;
    }
    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

ImportBeanDefinitionRegistrar接口

public interface ImportBeanDefinitionRegistrar {
    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        this.registerBeanDefinitions(importingClassMetadata, registry);
    }
​
    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }
}

FeignClientFactoryBean类

它实现了很多接口,其中有一个 FactoryBean 这个接口。

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
    // ...
}

FeignClientFactoryBean 重写的 getObject()

Spring 会通过 getObject()方法,创建一个实例然后放入 IOC容器,而 getObject()方法主要是调用了 getTarget()方法,

然后 getTarget() 前面也是在做数据装配,即 url 的拼接,最后主要就是 targeter.target 这个方法,这个方法是 Targeter 这个接口的方法。

public Object getObject() throws Exception {
    return this.getTarget();
}
​
​
​
<T> T getTarget() {
  FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class);
  Builder builder = this.feign(context);
  if (!StringUtils.hasText(this.url)) {
    if (!this.name.startsWith("http")) {
        this.url = "http://" + this.name;
    } else {
        this.url = this.name;
    }
    this.url = this.url + this.cleanPath();
    return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, this.url));
  } else {
    if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
        this.url = "http://" + this.url;
    }
    String url = this.url + this.cleanPath();
    Client client = (Client)this.getOptional(context, Client.class);
    if (client != null) {
        if (client instanceof LoadBalancerFeignClient) {
            client = ((LoadBalancerFeignClient)client).getDelegate();
        }
        builder.client(client);
    }
    Targeter targeter = (Targeter)this.get(context, Targeter.class);
    return targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url));
  }
}

FactoryBean 接口

就是在定义一个 bean,

  • getObject() 方法就获取这个 bean,

  • getObjectType() 方法就是获取这个 bean 的类型,

  • isSingleton() 方法就是去判断是不是单例,

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    @Nullable
    T getObject() throws Exception;
    @Nullable
    Class<?> getObjectType();
    default boolean isSingleton() {
        return true;
    }
}

1.4、Targeter接口

它有两个实现类 DefaultTargeter 和 HystrixTargeter,我们去看 DefaultTargeter,

interface Targeter {
  <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target);
}

DefaultTargeter类

这个类主要就是调用了 feign.target(target) 这个方法,Builder 是 Feign 这个抽象类的静态内部类。

class DefaultTargeter implements Targeter {
    DefaultTargeter() {}
    public <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target) {
        return feign.target(target);
    }
}

Feign抽象类

public abstract class Feign {
  // 省略部分代码
  public abstract <T> T newInstance(Target<T> var1);
  // 省略部分代码
  
  public static class Builder {
    public <T> T target(Target<T> target) {
      return this.build().newInstance(target);
    }
    
    public Feign build() {
      // 省略部分代码
      InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);
      QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);
      Factory synchronousMethodHandlerFactory = new Factory(client, retryer, requestInterceptors, logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
      ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
  }
}

Feign 的静态内部类 Builder

Builder 的 target 和 build方法

这个方法主要是调用了通过 build() 返回的对象的 newInstance(target)方法,build()方法则主要是 return 了一个新 new 的 ReflectiveFeign对象,

newInstance方法是 Feign 的抽象方法,专门就是给子类重写的。所以我们可以猜到,Builder类中的 build()方法返回的 ReflectiveFeign对象,肯定是 Feign 的子类。

ReflectiveFeign类(Feign 的子类)

ReflectiveFeign 的 newInstance方法

可以看到,这个 newInstance方法,就是最终创建了 proxy代理的地方,主要是 Proxy.newProxyInstance 这个方法,

  • 参数一:对应类的 ClassLoader,

  • 参数二:当前对象的接口的方法,

  • 参数三:handler,

    • 这个 handler 的类型是 InvocationHandler,然后通过 factory.create(target, methodToHandler) 这个方法转换得到的,target 是传的参数,那么我们就去看 methodToHandler

    • methodToHandler 则是通过methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))) 得到的,

    • method 就是 target 的一个方法,主要是 nameToHandler,然后它调用了 get方法,

    • nameToHandler 则是通过 targetToHandlersByName.apply(target) 得到的, 所以我们先看 apply方法,再看 factory.create 方法(factory.create 这个方法是 InvocationHandlerFactory 这个接口的方法)

这个 create方法,创建了一个 FeignInvocationHandler

public class ReflectiveFeign extends Feign {
  private final ReflectiveFeign.ParseHandlersByName targetToHandlersByName;
  private final InvocationHandlerFactory factory;
  private final QueryMapEncoder queryMapEncoder;
  
  ReflectiveFeign(ReflectiveFeign.ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory, QueryMapEncoder queryMapEncoder) {
    this.targetToHandlersByName = targetToHandlersByName;
    this.factory = factory;
    this.queryMapEncoder = queryMapEncoder;
  }
  
  public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
    Method[] var5 = target.type().getMethods();
    int var6 = var5.length;
    for(int var7 = 0; var7 < var6; ++var7) {
      Method method = var5[var7];
      if (method.getDeclaringClass() != Object.class) {
          // 判断是不是默认的,我们这个肯定不是默认的
        if (Util.isDefault(method)) {
          DefaultMethodHandler handler = new DefaultMethodHandler(method);
          defaultMethodHandlers.add(handler);
          methodToHandler.put(method, handler);
        } else {
          methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
        }
      }
    }
    InvocationHandler handler = this.factory.create(target, methodToHandler);
    T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
    Iterator var12 = defaultMethodHandlers.iterator();
    while(var12.hasNext()) {
        DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
        defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }
}
1.4.3.1、ReflectiveFeign的 apply方法

contract.parseAndValidatateMetadata(key.type()) 就是去获取当前接口的方法里面的信息,然后封装给 metadata,里面的值就是 类名#方法名

然后 new 了一个 LinkedHashMap()对象 result,然后做 for循环,在里面调用了 result.put(md.configKey(), this.factory.create(key, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder)) 这个方法,

即 result.put、factory.create 这两个方法,这个 create方法,创建了一个 SynchronousMethodHandler

public Map<String, MethodHandler> apply(Target key) {
  List<MethodMetadata> metadata = this.contract.parseAndValidatateMetadata(key.type());
  Map<String, MethodHandler> result = new LinkedHashMap();
  MethodMetadata md;
  Object buildTemplate;
  for(Iterator var4 = metadata.iterator(); var4.hasNext(); result.put(md.configKey(), this.factory.create(key, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder))) {
      md = (MethodMetadata)var4.next();
      if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
          buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder);
      } else if (md.bodyIndex() != null) {
          buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder);
      } else {
          buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md, this.queryMapEncoder);
      }
  }
  return result;
}

1.5、SynchronousMethodHandler的静态内部类Factory的create方法

可以看到,SynchronousMethodHandler 实现了 MethodHandler接口,而 MethodHandler接口又是 InvocationHandlerFactory 的内部接口,

这个方法在里面 new 了一个 hander,SynchronousMethodHandler,

而 SynchronousMethodHandler这个类实现了 MethodHandler接口,

final class SynchronousMethodHandler implements MethodHandler {
  // 省略部分代码
  private SynchronousMethodHandler(Target<?> target, Client client, Retryer retryer, List<RequestInterceptor> requestInterceptors, Logger logger, Level logLevel, MethodMetadata metadata, feign.RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder, boolean decode404, boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy) {
    this.target = (Target)Util.checkNotNull(target, "target", new Object[0]);
    this.client = (Client)Util.checkNotNull(client, "client for %s", new Object[]{target});
    this.retryer = (Retryer)Util.checkNotNull(retryer, "retryer for %s", new Object[]{target});
    this.requestInterceptors = (List)Util.checkNotNull(requestInterceptors, "requestInterceptors for %s", new Object[]{target});
    this.logger = (Logger)Util.checkNotNull(logger, "logger for %s", new Object[]{target});
    this.logLevel = (Level)Util.checkNotNull(logLevel, "logLevel for %s", new Object[]{target});
    this.metadata = (MethodMetadata)Util.checkNotNull(metadata, "metadata for %s", new Object[]{target});
    this.buildTemplateFromArgs = (feign.RequestTemplate.Factory)Util.checkNotNull(buildTemplateFromArgs, "metadata for %s", new Object[]{target});
    this.options = (Options)Util.checkNotNull(options, "options for %s", new Object[]{target});
    this.errorDecoder = (ErrorDecoder)Util.checkNotNull(errorDecoder, "errorDecoder for %s", new Object[]{target});
    this.decoder = (Decoder)Util.checkNotNull(decoder, "decoder for %s", new Object[]{target});
    this.decode404 = decode404;
    this.closeAfterDecode = closeAfterDecode;
    this.propagationPolicy = propagationPolicy;
  }
  // 省略部分代码
  
  static class Factory {
    public MethodHandler create(Target<?> target, MethodMetadata md, feign.RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder) {
      return new SynchronousMethodHandler(target, this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, this.decode404, this.closeAfterDecode, this.propagationPolicy);
    }
  }
}

1.5.1、InvocationHandlerFactory的内部接口 MethodHandler

MethodHandler这个接口又是 InvocationHandlerFactory接口的内部接口,

MethodHandler里面有 invoke这个方法,这个方法就 JDK 代理之后要去调用的方法,

public interface InvocationHandlerFactory {
  InvocationHandler create(Target var1, Map<Method, InvocationHandlerFactory.MethodHandler> var2);
  public static final class Default implements InvocationHandlerFactory {
    public Default() {}
    
    public InvocationHandler create(Target target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
      return new FeignInvocationHandler(target, dispatch);
    }
  }
  public interface MethodHandler {
    Object invoke(Object[] var1) throws Throwable;
  }
}

1.5.2、InvocationHandlerFactory的 create方法

InvocationHandlerFactory 的 create方法已经有默认的实现类 Default 为其重写了,

这个重写的方法是 new 了一个 FeignInvocationHandler 然后做 return。

public interface InvocationHandlerFactory {
  InvocationHandler create(Target var1, Map<Method, InvocationHandlerFactory.MethodHandler> var2);
  public static final class Default implements InvocationHandlerFactory {
    public Default() {}
    public InvocationHandler create(Target target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
      return new FeignInvocationHandler(target, dispatch);
    }
  }
  
  public interface MethodHandler {
    Object invoke(Object[] var1) throws Throwable;
  }
}

1.5.3、ReflectiveFeign的静态内部类 FeignInvocationHandler

因为做代理之后,当去执行代理的方法,都会调用对应的 invoke方法,所以我们一看到 invoke的实现方法,就需要看一下

如果说这个方法的名称不是 "equals",就为 true,就进入,若为 false,就走 else中的方法,

如果是 true,再判断是不是 "hashCode",是的话就执行 hashCode() 方法,

如果不是,再次判断是不是 "toString",是的话就做 toString() 方法,

不是的话就执行 invoke方法,

即这个方法不是 "equals"、"hashCode"、"toString" 的话,就调用 invoke方法,

而这个 invoke方法,就是 SynchronousMethodHandler的 invoke实现方法

public class ReflectiveFeign extends Feign {
  static class FeignInvocationHandler implements InvocationHandler {
    private final Target target;
    private final Map<Method, MethodHandler> dispatch;
    FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
        this.target = (Target)Util.checkNotNull(target, "target", new Object[0]);
        this.dispatch = (Map)Util.checkNotNull(dispatch, "dispatch for %s", new Object[]{target});
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if (!"equals".equals(method.getName())) {
        if ("hashCode".equals(method.getName())) {
          return this.hashCode();
        } else {
          return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
        }
      } else {
        try {
          Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return this.equals(otherHandler);
        } catch (IllegalArgumentException var5) {
          return false;
        }
      }
    }
    
    public boolean equals(Object obj) {
      if (obj instanceof ReflectiveFeign.FeignInvocationHandler) {
        ReflectiveFeign.FeignInvocationHandler other = (ReflectiveFeign.FeignInvocationHandler)obj;
        return this.target.equals(other.target);
      } else {
        return false;
      }
    }
    public int hashCode() {
      return this.target.hashCode();
    }
    public String toString() {
      return this.target.toString();
    }
  }
}

1.5.4、SynchronousMethodHandler的 invoke实现方法

先通过 buildTemplateFromArgs.create(argv) 方法获得了一个 RequestTemplate对象,

然后调用 executeAndDecode(template, options)这个方法,即执行并且解码,

public Object invoke(Object[] argv) throws Throwable {
  RequestTemplate template = this.buildTemplateFromArgs.create(argv);
  Options options = this.findOptions(argv);
  Retryer retryer = this.retryer.clone();
  while(true) {
    try {
      return this.executeAndDecode(template, options);
    } catch (RetryableException var9) {
      RetryableException e = var9;
      try {
        retryer.continueOrPropagate(e);
      } catch (RetryableException var8) {
        Throwable cause = var8.getCause();
        if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
            throw cause;
        }
        throw var8;
      }
      if (this.logLevel != Level.NONE) {
        this.logger.logRetry(this.metadata.configKey(), this.logLevel);
      }
    }
  }
}

1.5.5、SynchronousMethodHandler的 executeAndDecode方法

这时候,RequestTemplate 内已经有访问地址了,

然后执行 HTTP调用方法,client.execute(request, options),就会去做一个 HTTP请求,然后获得响应 response,

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
  Request request = this.targetRequest(template);
  if (this.logLevel != Level.NONE) {
    this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
  }
  long start = System.nanoTime();
  Response response;
  try {
      response = this.client.execute(request, options);
  } catch (IOException var16) {
    if (this.logLevel != Level.NONE) {
        this.logger.logIOException(this.metadata.configKey(), this.logLevel, var16, this.elapsedTime(start));
    }
    throw FeignException.errorExecuting(request, var16);
  }
  long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
  boolean shouldClose = true;
  try {
    if (this.logLevel != Level.NONE) {
        response = this.logger.logAndRebufferResponse(this.metadata.configKey(), this.logLevel, response, elapsedTime);
    }
    if (Response.class == this.metadata.returnType()) {
      Response var19;
      if (response.body() == null) {
          var19 = response;
          return var19;
      } else if (response.body().length() != null && (long)response.body().length() <= 8192L) {
          byte[] bodyData = Util.toByteArray(response.body().asInputStream());
          Response var21 = response.toBuilder().body(bodyData).build();
          return var21;
      } else {
          shouldClose = false;
          var19 = response;
          return var19;
      }
    } else {
      Object result;
      Object var11;
      if (response.status() >= 200 && response.status() < 300) {
          if (Void.TYPE == this.metadata.returnType()) {
              result = null;
              return result;
          } else {
              result = this.decode(response);
              shouldClose = this.closeAfterDecode;
              var11 = result;
              return var11;
          }
      } else if (this.decode404 && response.status() == 404 && Void.TYPE != this.metadata.returnType()) {
          result = this.decode(response);
          shouldClose = this.closeAfterDecode;
          var11 = result;
          return var11;
      } else {
          throw this.errorDecoder.decode(this.metadata.configKey(), response);
      }
    }
  } catch (IOException var17) {
    if (this.logLevel != Level.NONE) {
        this.logger.logIOException(this.metadata.configKey(), this.logLevel, var17, elapsedTime);
    }
    throw FeignException.errorReading(request, response, var17);
  } finally {
    if (shouldClose) {
        Util.ensureClosed(response.body());
    }
  }
}

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值