SpringCloud + Zookeeper + Feign整合及Feign原理

知其然

SpringCloud + Zookeeper

Spring Cloud 与 Zookeeper的整合只需要添加相关的starter依赖和增加相关注解即可完成。

pom.xml 如下:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>FeignDemo</artifactId>
        <groupId>com.hui</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>feignHello-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <!-- 需要特别主题spring cloud 和zookeeper 的版本
            笔者本地使用了3.4.11的zk,因此在此处排除了默认的zk,单独引入-->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.11</version>
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!--笔者将api与service分为了独立的module,所以这里加入引用api-->
        <dependency>
            <groupId>com.hui</groupId>
            <artifactId>feignHello-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

bootstrap.yml如下:

复制代码
server:
  port: 8000
spring:
  application:
    name: feignHelloService
  cloud:
    zookeeper:
      connect-string: 192.168.4.192:2181 #zk地址

最后开启服务的注册与发现

复制代码
@SpringBootApplication
@EnableDiscoveryClient
public class HelloServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloServiceApplication.class, args);
    }
}

service 和controller实现

复制代码
@Service
public class HelloService{
    public HelloResponse sayHello(HelloRequest request) {
        return new HelloResponse(request.toString());
    }
}

@Api(value = "Hello 服务", tags = "Hello 服务")
@RestController
public class HelloController implements IHelloServiceClient {
    @Autowired
    HelloService helloService;

    @Override
    @ApiOperation(value = "say hello 带默认参数")
    @GetMapping("/hello")
    //http://localhost:8000/hello?name=qqqq&age=22
    public String sayHello(@RequestParam(name = "name", defaultValue = "tony") String name,
                           @RequestParam(name = "age", defaultValue = "18") int age) {
        HelloRequest request = new HelloRequest(name, age);
        return helloService.sayHello(request).toString();
    }

    @Override
    @GetMapping("/hello1")
    @PostMapping(value = "say hello 不带参数")
    public String sayHello() {
        HelloRequest request = new HelloRequest("tony", 19);
        return helloService.sayHello(request).toString();
    }

    @Override
    @PostMapping("/hello2")
    @ApiOperation(value = "say hello 带请求体")
    public HelloResponse sayHello(@RequestBody HelloRequest request) {
        return helloService.sayHello(request);
    }
}

笔者加入了swagger,如果需要只需加入如下依赖和配置:

复制代码
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-spring-boot-starter</artifactId>
        <version>2.0.7</version>
    </dependency>

@Configuration
@EnableSwagger2WebMvc
public class SwaggerAutoConfiguration {

    @Bean
    public Docket defaultApi(){
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder().title("helloService").description("RpcDemo").version("1.0").build())
                .select()
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

}

至此,spring cloud与zookeeper的整合就完成了,调用结果如下:


helloService.jpg

SpringCloud + Zookeeper + Feign

为了测试与Feign的整合,再构建一个消费者:与上述构建的过程类似。

pom.xml 增加spring-cloud-starter-openfeign依赖

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>FeignDemo</artifactId>
        <groupId>com.hui</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>feignHello-test</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.hui</groupId>
            <artifactId>feignHello-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.11</version>
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.4.RELEASE</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

bootstrap.yaml:

复制代码
server:
  port: 8001
spring:
  application:
    name: feignHelloTest
  cloud:
    zookeeper:
      connect-string: 192.168.4.192:2181
  main:
    allow-bean-definition-overriding: true #笔者定义了两个相同FeignClient,所以bean相同

开启服务注册与发现,@EnableFeignClients注解注册FeignClient

复制代码
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class HelloServiceTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloServiceTestApplication.class, args);
    }
}

@FeignClient注册声明定义FeignClient,笔者以两种方式定义了两个FeignClient:

1.通过请求路径定义FeignClient

复制代码
@FeignClient(value = "feignHelloService", path = "/hello")
public interface RemoteServiceClient {
    @RequestMapping("/")
    String testHello();
}

2.通过生产者(即上述构建的helloService)暴露出来的接口定义FeignClient

复制代码
@Component
@FeignClient(name = "feignHelloService")
public interface RemoteServiceRpcClient extends IHelloServiceClient {
}

controller 测试:

复制代码
@Api(value = "Hello 测试 服务", tags = "Hello 测试 服务")
@RestController
public class HelloTestController {

    @Resource
    RemoteServiceClient remoteService;

    @Autowired
    RemoteServiceRpcClient remoteServiceRpcClient;

    @ApiOperation(value = "远程调用方式测试1")
    @GetMapping("/remote")
    public String remoteHello(){
        return remoteService.testHello();
    }

    @ApiOperation(value = "本地调用方式测试")
    @GetMapping("/local")
    public String localHello(){
        return "hello" + UUID.randomUUID().toString();
    }

    @ApiOperation(value = "远程调用方式测试2,带请求参数")
    @PostMapping("/remoteRpc")
    public String remoteRpcHello(){
        return remoteServiceRpcClient.sayHello("tony",110);
//        return remoteServiceRpcClient.sayHello();
    }

    @ApiOperation(value = "远程调用方式测试3, 带请求体")
    @PostMapping("/remoteRpc1")
    public HelloResponse remoteRpcHello1(){
        return remoteServiceRpcClient.sayHello(new HelloRequest("YYY",122));
    }

}

测试结果如下:


helloServiceTest.jpg

知其所以然

知道了如何将SpringCloud, Zookeeper 和Feign进行整合,我们知道了怎么使用,更重要的是要知道里面的原理,做到知其然更要知其所以然。

通过上述对整合过程的描述中可以发现,@EnableFeignClients和@FeignClient两个注解是将Feign整合进Spring Cloud的重要组成部分,因此,从这两个注解入手来了解Feign。

@EnableFeignClients:

复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
    /**
    * basePackages属性的别名
    */
    String[] value() default {};
    /**
    * 包扫描路径,扫描该路径下被@FeignClient标记的类
    */
    String[] basePackages() default {};
    /**
    * 指明包扫描的类
    */
    Class<?>[] basePackageClasses() default {};
    /**
    * 对所有feign client定制的配置类
    */
    Class<?>[] defaultConfiguration() default {};
    /**
    * 所有被@FeignClient标记的client,如果不为空,就不会基于classpath进行扫描
    */
    Class<?>[] clients() default {};
}

@EnableFeignClients -> FeignClientsRegistrar

@EnableFeignClients注解通过@Import引入了FeignClientsRegistrar进行feign客户端的注册, 同时FeignClientsRegistrar通过实现ImportBeanDefinitionRegistrar来将bean注册spring容器中:

复制代码
public interface ImportBeanDefinitionRegistrar {
    /**
     * 根据使用者的配置类的注解元数据来注册bean的定义
     * @param importingClassMetadata 配置类的注解元数据
     * @param registry 当前bean定义的注册器,一般指spring容器
     */
    default void registerBeanDefinitions(AnnotationMetadata
    importingClassMetadata, BeanDefinitionRegistry registry) {}
}

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
        
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        //注册默认的配置到spring容器中
        registerDefaultConfiguration(metadata, registry);
        //注册发现的feign client到spring容器中
        registerFeignClients(metadata, registry);
    }
}

@EnableFeignClients -> FeignClientsRegistrar ---> registerDefaultConfiguration

复制代码
    private void registerDefaultConfiguration(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //获取@EnableFeignClients注解的属性
        Map<String, Object> defaultAttrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName(), true);

        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
            String name;
            //拼接默认配置名
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            }
            else {
                name = "default." + metadata.getClassName();
            }
            registerClientConfiguration(registry, name,
                    defaultAttrs.get("defaultConfiguration"));
        }
    }
    
    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
            Object configuration) {
        //将feign client 配置构建成一个bean注册到spring容器中
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        registry.registerBeanDefinition(
                name + "." + FeignClientSpecification.class.getSimpleName(),
                builder.getBeanDefinition());
    }

@EnableFeignClients -> FeignClientsRegistrar ---> registerFeignClients

复制代码
    public void registerFeignClients(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //定义一个基于classpath的扫描器,用来获取被@FeignClient注解标注的feign clent
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);

        Set<String> basePackages;
        
        //获取@EnableFeignClients注解的属性
        Map<String, Object> attrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName());
        //@FeignClient注解过滤器
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                FeignClient.class);
        //获取@EnableFeignClient注解的clients属性的值
        final Class<?>[] clients = attrs == null ? null
                : (Class<?>[]) attrs.get("clients");
        if (clients == null || clients.length == 0) {
            //@EnableFeignClients注解没有配置clients属性的情况
            //扫描器中加入注解过滤器
            scanner.addIncludeFilter(annotationTypeFilter);
            //获取@EnableFeignClients注解中的basePackages属性
            basePackages = getBasePackages(metadata);
        }
        else {
            //@EnableFeignClients注解配置了clients属性的情况
            final Set<String> clientClasses = new HashSet<>();
            basePackages = new HashSet<>();
            for (Class<?> clazz : clients) {
                //遍历client,获取器包路径和类名
                basePackages.add(ClassUtils.getPackageName(clazz));
                clientClasses.add(clazz.getCanonicalName());
            }
            //定义过滤器,只获取在clientClasses集合中的feign client
            AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
                @Override
                protected boolean match(ClassMetadata metadata) {
                    String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                    return clientClasses.contains(cleaned);
                }
            };
            scanner.addIncludeFilter(
                    new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
        }
        //使用扫描器scanner扫描每一个basePackage,获取被@FeignClient标注的客户端
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidateComponents = scanner
                    .findCandidateComponents(basePackage);
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // verify annotated class is an interface
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    Assert.isTrue(annotationMetadata.isInterface(),
                            "@FeignClient can only be specified on an interface");

                    //获取@FeignClient注解的属性
                    Map<String, Object> attributes = annotationMetadata
                            .getAnnotationAttributes(
                                    FeignClient.class.getCanonicalName());

                    String name = getClientName(attributes);
                    //将针对特定feign client的配置注册到spring容器
                    registerClientConfiguration(registry, name, attributes.get("configuration"));
                    //注册feign client到spring容器       
                    registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }
    private void registerFeignClient(BeanDefinitionRegistry registry,
            AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        //通过FeignClientFactoryBean工厂bean构建BeanDefinitionBuilder
        BeanDefinitionBuilder definition = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientFactoryBean.class);
        validate(attributes);
        //@FeignClient注解的属性作为bean的属性
        definition.addPropertyValue("url", getUrl(attributes));
        definition.addPropertyValue("path", getPath(attributes));
        String name = getName(attributes);
        definition.addPropertyValue("name", name);
        String contextId = 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(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

        String alias = contextId + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);

        // has a default, won't be null
        boolean primary = (Boolean) attributes.get("primary");

        beanDefinition.setPrimary(primary);

        String qualifier = getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                new String[] { alias });
        //注册feign client到spring容器
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

至此,我们知道了通过@EnableFeignClients和@FeignClient两个注解以及其相关属性,在服务启动时,将每个feign client 以及其对应的配置和每个客户端通用的配置以bean的方式注册完到spring容器中。

FeignClient的自动注入

当使用@Autowired注解自动注入FeignClient时,Spring容器会使用注册FeignClient用到的FeignClientFactoryBean为其生成FeignClient实例。

复制代码
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {

    @Override
    public Object getObject() throws Exception {
        return getTarget();
    }

    /**
     * @param <T> the target type of the Feign client
     * @return a {@link Feign} client created with the specified data and the context
     * information
     */
    <T> T getTarget() {
        //从应用上下文中获取FeignClient的上下文
        FeignContext context = applicationContext.getBean(FeignContext.class);
        //通过FeignClient的上下文构建feignClient构造器
        Feign.Builder builder = feign(context);

        
        if (!StringUtils.hasText(url)) {
            //@FeignClient没有配置url的情况,根据name和path属性拼接成url
            if (!name.startsWith("http")) {
                url = "http://" + name;
            }
            else {
                url = name;
            }
            url += cleanPath();
            //没有配置url属性,需要在多个服务节点之间进行负载均衡,生产Feign client
            return (T) loadBalance(builder, context,
                    new HardCodedTarget<>(type, name, url));
        }
        //配置了url属性的情况
        if (StringUtils.hasText(url) && !url.startsWith("http")) {
            url = "http://" + url;
        }
        String url = this.url + cleanPath();
        //从上下文获取FeignClien:LoadBalancerFeignClient
        Client client = getOptional(context, Client.class);
        if (client != null) {
            if (client instanceof LoadBalancerFeignClient) {
                // not load balancing because we have a url,
                // but ribbon is on the classpath, so unwrap
                client = ((LoadBalancerFeignClient) client).getDelegate();
            }
            if (client instanceof FeignBlockingLoadBalancerClient) {
                // not load balancing because we have a url,
                // but Spring Cloud LoadBalancer is on the classpath, so unwrap
                client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
            }
            builder.client(client);
        }
        //从上下文中获取targeter
        Targeter targeter = get(context, Targeter.class);
        //通过targeter、builder生成Feign client
        return (T) targeter.target(this, builder, context,
                new HardCodedTarget<>(type, name, url));
    }
    
    
    protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
        //上下文获取Feign client
        Client client = getOptional(context, Client.class);
        if (client != null) {
            //将builder与client关联
            builder.client(client);
            Targeter targeter = get(context, Targeter.class);
            //生产Feign client
            return targeter.target(this, builder, context, target);
        }

        throw new IllegalStateException(
                "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
    }
}

默认使用的targeter是HystrixTargeter,根据builder的类型设置不同的属性,并生产Feign client

复制代码
class HystrixTargeter implements Targeter {

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
            FeignContext context, Target.HardCodedTarget<T> target) {
        if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
            return feign.target(target);
        }
        //....省略
        return feign.target(target);
    }
}

public abstract class Feign {
      public static class Builder {
            //构建客户端并创建feign client实例
            public <T> T target(Target<T> target) {
                return build().newInstance(target);
            }
            public Feign build() {
                  Client client = Capability.enrich(this.client, capabilities);
                  Retryer retryer = Capability.enrich(this.retryer, capabilities);
                  List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
                      .map(ri -> Capability.enrich(ri, capabilities))
                      .collect(Collectors.toList());
                  Logger logger = Capability.enrich(this.logger, capabilities);
                  Contract contract = Capability.enrich(this.contract, capabilities);
                  Options options = Capability.enrich(this.options, capabilities);
                  Encoder encoder = Capability.enrich(this.encoder, capabilities);
                  Decoder decoder = Capability.enrich(this.decoder, capabilities);
                  InvocationHandlerFactory invocationHandlerFactory =
                      Capability.enrich(this.invocationHandlerFactory, capabilities);
                  QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
            
                  SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
                      new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                          logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
                  ParseHandlersByName handlersByName =
                      new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
                          errorDecoder, synchronousMethodHandlerFactory);
                  //根据相关配置,构建ReflectiveFeign
                  return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
           }
      }
}

public class ReflectiveFeign extends Feign {
    private final ParseHandlersByName targetToHandlersByName;
    private final InvocationHandlerFactory factory;
    private final QueryMapEncoder queryMapEncoder;
    
    ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,
          QueryMapEncoder queryMapEncoder) {
        this.targetToHandlersByName = targetToHandlersByName;
        this.factory = factory;
        this.queryMapEncoder = queryMapEncoder;
    }
    @Override
    public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if (Util.isDefault(method)) {
        //对于缺省的方法使用DefaultMethodHandler
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        //对于每个对应服务端的方法,使用nameToHandler获取methodHandler
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    //通过InvocationHandlerFactory创建FeignInvocationHandler,该handler包含了上面创建的methodTohHandler
    //构成dispatch,用于对应@FeignClient标注的接口方法,当调用时进行转发处理
    InvocationHandler handler = factory.create(target, methodToHandler);
    //为feign客户端实例创建动态代理对象
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);
    //将缺省的methodHander绑定到动态代理对象上
    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }
}

总结

从上面的分析可以得出,当服务启动时,通过@EnableFeignClients注解,启动对标注了@FeignClient注解的类进行扫描和注册,通过FeignClientFactoryBean将FeignClient注册到Spring容器中。当使用@Autowired注解进行自动注入时,注册到Spring容器中FeignClient会以动态代理的形式注入,这些动态代理中包含了接口方法的methodHandler用以处理调用转发。
最后编辑于:2025-06-15 09:49:01
© 著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务

喜欢的朋友记得点赞、收藏、关注哦!!!

相关推荐
__万波__7 小时前
二十三种设计模式(十三)--模板方法模式
java·设计模式·模板方法模式
动亦定7 小时前
微服务中如何保证数据一致性?
java·数据库·微服务·架构
王桑.7 小时前
Spring中IoC的底层原理
java·后端·spring
BullSmall7 小时前
Apache Doris 精细化调优配置指南
linux·运维·服务器·database
Liii4037 小时前
Java集合详细讲解
java·开发语言
QT 小鲜肉7 小时前
【Linux命令大全】001.文件管理之chattr命令(实操篇)
linux·运维·服务器·笔记
瀚高PG实验室7 小时前
timestampdiff (MYSQL)函数在Highgo DB中的写法
数据库·mysql·瀚高数据库
b***25117 小时前
18650与21700电芯电池组PACK自动化生产线的核心差异与协同发展
运维·自动化
Han.miracle7 小时前
Spring Boot 项目从入门到排障:核心结构、依赖管理与启动全解析
java·jar
麦麦鸡腿堡8 小时前
Java_通过反射获取类的结构信息
java·开发语言