美文网首页
SpringCloud + Zookeeper + Feign整

SpringCloud + Zookeeper + Feign整

作者: 灰fw | 来源:发表于2021-09-13 17:07 被阅读0次

    知其然

    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用以处理调用转发。

    相关文章

      网友评论

          本文标题:SpringCloud + Zookeeper + Feign整

          本文链接:https://www.haomeiwen.com/subject/arzxwltx.html