OpenFeign学习(三):OpenFeign配置生成代理对象

说明

在之前的两篇博文《OkHttp的高级封装Feign学习(一): Feign注解的使用》《OpenFeign学习(二):高级用法自定义配置组件HttpClient / SLF4J / RequestInterceptor等》中,我简单介绍了OpenFeign的使用方法。在本篇博文中,我将通过源码来学习记录OpenFeign的工作原理。

正文

工作流程

在阅读源码前,我们先通过流程图来了解OpenFeign的具体工作流程:
在这里插入图片描述
如图所示,我把OpenFeign的工作流程分为了10个阶段。接下来通过阅读源码来了解每个阶段是如何实现的。

创建代理

在之前的博文中我们已经学习了如何使用OpenFeign来进行服务的调用,在接口方法中使用注释,最后通过Feign的构造器来创建接口的代理。

HelloService service = Feign.builder()
			.logger(new Slf4jLogger())
			.logLevel(Logger.Level.FULL)
			.client(new OkHttpClient())
			.encoder(new JacksonEncoder())
			.decoder(new JacksonDecoder())
			.requestInterceptor(new HeadersInterceptor())
			.errorDecoder(new CustomErrorDecoder())
			.retryer(new MyRetryer(5))
			.exceptionPropagationPolicy(ExceptionPropagationPolicy.UNWRAP)
			.target(HelloService.class, "http://localhost:8080/");

以上示例代码说明了通过Feign的构造器,我们可以自定义配置其中的组件,如client,encoder,decoder等。通过该构造器的源码来了解下未配置组件时的默认值。

public Builder() {
        this.logLevel = Level.NONE;
        this.contract = new Default();
        this.client = new feign.Client.Default((SSLSocketFactory)null, (HostnameVerifier)null);
        this.retryer = new feign.Retryer.Default();
        this.logger = new NoOpLogger();
        this.encoder = new feign.codec.Encoder.Default();
        this.decoder = new feign.codec.Decoder.Default();
        this.queryMapEncoder = new FieldQueryMapEncoder();
        this.errorDecoder = new feign.codec.ErrorDecoder.Default();
        this.options = new Options();
        this.invocationHandlerFactory = new feign.InvocationHandlerFactory.Default();
        this.closeAfterDecode = true;
        this.propagationPolicy = ExceptionPropagationPolicy.NONE;
}

可以看到contract使用的默认的Default实现,client使用的是默认的HttpURLConnection,retryer使用的是默认的Default实现等等。OpenFeign在保证了极大的扩展性的同时也保证了它的简单性。

在Builder的构造函数中,创建了InvocationHandlerFactory对象,使用的默认实现类,该对象在创建代理时,来创建代理对象的InvocationHandler。

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;
    }
}

在配置完成后,最后调用了构造器的target方法,设置接口类对象及请求地址URL。在target方法中,我们可以发现它先创建Target对象,在调用了build方法创建ReflectiveFeign对象,最后以Target对象为参数调用Reflective的newInstance(target)方法进行初始化

public <T> T target(Class<T> apiType, String url) {
    return this.target(new HardCodedTarget(apiType, url));
}

public <T> T target(Target<T> target) {
    return this.build().newInstance(target);
}

public Feign build() {
    Factory synchronousMethodHandlerFactory = new Factory(this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy);
    ParseHandlersByName handlersByName = new ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
    return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory, this.queryMapEncoder);
}

可以看到在build方法中,创建了synchronousMethodHandlerFactory工厂对象,从其名称可以知道该工厂创建的方法处理类是同步的。同时创建了ParseHandlersByName类对象,也可从名称知道该类是用来通过方法名称来解析获取Handler。

在ReflectiveFeign的newInstance(target)方法中,真正创建代理对象。通过其方法源码,来一步步解析代理对象如何创建。

 public <T> T newInstance(Target<T> target) {
    // 通过ParseHandlerByName解析接口得到方法名称和对应的MethodHandler
    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
                DefaultMethodHandler handler = new DefaultMethodHandler(method);
                defaultMethodHandlers.add(handler);
                methodToHandler.put(method, handler);
            } else {
                methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
            }
        }
    }
    
    // 通过默认的InvocationHandlerFactory工厂对象来创建FeignInvocationHandler对象
    InvocationHandler handler = this.factory.create(target, methodToHandler);
    // 再通过JDK动态代理创建代理对象
    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;
}

通过以上代码注释,我们可以知道创建代理对象分以下几步:

  • 通过ParseHandlerByName的apply()方法获取方法名称对应的MethodHandler
  • 通过反射得到接口声明方法,并得到Method对象对应的MethodHandler生成Map集合nameToHandler,并使用invocationHandlerFactory创建InvocationHandler对象
  • 通过JDK动态代理创建代理对象
  • 将接口的默认方法绑定到代理对象

接下来,分别对每步的代码进行阅读,了解其中原理。

ParseHandlerByName.apply(target)

该方法解析生成接口声明方法对应的MethodHandler。通过源码来认识如何生成方法名对应的MethodHandler

public Map<String, MethodHandler> apply(Target target) {
    // 首先通过配置的协议Contract来解析获取方法元数据MethodMetadata
    List<MethodMetadata> metadata = this.contract.parseAndValidateMetadata(target.type());
    Map<String, MethodHandler> result = new LinkedHashMap();
    Iterator var4 = metadata.iterator();

    // 再根据方法元数据来分别创建不同的RequestTemplateFactory, 该对象用来resolve对应的RequestTemplate
    while(var4.hasNext()) {
        MethodMetadata md = (MethodMetadata)var4.next();
        Object buildTemplate;
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
            buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
        } else if (md.bodyIndex() != null) {
            buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
        } else {
            // 以上两种BuildTemplate都继承自BuildTemplateByResolvingArgs
            buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md, this.queryMapEncoder, target);
        }

        if (md.isIgnored()) {
            result.put(md.configKey(), (args) -> {
                throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
            });
        } else {
            // 使用SynchronousMethodHandlerFactor创建方法对应的MethodHandler
            result.put(md.configKey(), this.factory.create(target, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder));
        }
    }

    return result;
}

通过以上代码注释,我们了解到生成方法名对应的MethodHandler分为以下几个步骤:

  • 通过协议Contract解析获取方法的元数据MethodMetadata
  • 根据MethodMetadata创建每个方法对应的buildTemplate对象,该对象在请求时构造生成不同的requestTemplate
  • 对于每个方法,使用SynchronousMethodHandlerFactor创建对应的MethodHandler。并以方法元数据configKey为键,MethodHandler为值存储返回

在以上步骤中,前两步十分重要,直接关系到后续请求是否能成功进行。接下来对这两步中的源码进行了解

contract.parseAndValidateMetadata

通过配置的Contract协议来解析方法或类上的注解,生成方法的元数据,在之前的Builder构造函数代码示例中,OpenFeign使用的是默认协议Default。通过类图我们可以了解协议类Contract之间的关系。
在这里插入图片描述

通过类图可以看到,Default类实现了DeclarativeContract抽象类,DeclarativeContract继承了BaseContract抽象类,BaseContract抽象类实现了Contract接口。

在Contract接口中声明了parseAndValidateMetadata方法,在BaseContract抽象类中实现了该方法,并且BaseContract抽象类声明了模板方法processAnnotationOnClass,processAnnotationOnMethod,processAnnotationsOnParameter。从名称可以看出这三个方法是分别用来处理类,方法和参数上的注解的。

在BaseContract类的parseAndValidateMetadata方法实现中,我们可以看到也是通过反射获取声明的方法Method对象,再对注解进行处理生成MethodMetadata。

public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
    Util.checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s", new Object[]{targetType.getSimpleName()});
    Util.checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s", new Object[]{targetType.getSimpleName()});
    if (targetType.getInterfaces().length == 1) {
        Util.checkState(targetType.getInterfaces()[0].getInterfaces().length == 0, "Only single-level inheritance supported: %s", new Object[]{targetType.getSimpleName()});
    }

    Map<String, MethodMetadata> result = new LinkedHashMap();
    Method[] var3 = targetType.getMethods();
    int var4 = var3.length;

    for(int var5 = 0; var5 < var4; ++var5) {
        Method method = var3[var5];
        if (method.getDeclaringClass() != Object.class && (method.getModifiers() & 8) == 0 && !Util.isDefault(method)) {
            MethodMetadata metadata = this.parseAndValidateMetadata(targetType, method);
            Util.checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s", new Object[]{metadata.configKey()});
            result.put(metadata.configKey(), metadata);
        }
    }

    return new ArrayList(result.values());
}

在parseAndValidateMetadata(class, Method)方法中别调用了以上提到三个方法的实现,来处理类,方法和参数上的注解。

此时,我们可以看DefaultContract类做了什么。
首先,在该类中声明了三个集合:

public abstract class DeclarativeContract extends BaseContract {
    private List<DeclarativeContract.GuardedAnnotationProcessor> classAnnotationProcessors = new ArrayList();
    private List<DeclarativeContract.GuardedAnnotationProcessor> methodAnnotationProcessors = new ArrayList();
    Map<Class<Annotation>, DeclarativeContract.ParameterAnnotationProcessor<Annotation>> parameterAnnotationProcessors = new HashMap();
    ...
}

这三个集合分别存储类,方法和参数的注解处理器,而这些注解器,正是通过Default类在其构造函数中进行注册的。通过源码我们可以看到,Default中注册的注释处理器,正是我们在使用OpenFeign声明接口方法时使用的注解。

 public static class Default extends DeclarativeContract {
        // 该正则表达式用来匹配@RequestLine注解中声明的HTTP请求方式
        static final Pattern REQUEST_LINE_PATTERN = Pattern.compile("^([A-Z]+)[ ]*(.*)$");

        public Default() {
            super.registerClassAnnotation(Headers.class, (header, data) -> {
                String[] headersOnType = header.value();
                Util.checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.", new Object[]{data.configKey()});
                Map<String, Collection<String>> headers = toMap(headersOnType);
                headers.putAll(data.template().headers());
                data.template().headers((Map)null);
                data.template().headers(headers);
            });
            super.registerMethodAnnotation(RequestLine.class, (ann, data) -> {
                String requestLine = ann.value();
                Util.checkState(Util.emptyToNull(requestLine) != null, "RequestLine annotation was empty on method %s.", new Object[]{data.configKey()});
                Matcher requestLineMatcher = REQUEST_LINE_PATTERN.matcher(requestLine);
                if (!requestLineMatcher.find()) {
                    throw new IllegalStateException(String.format("RequestLine annotation didn't start with an HTTP verb on method %s", data.configKey()));
                } else {
                    data.template().method(HttpMethod.valueOf(requestLineMatcher.group(1)));
                    data.template().uri(requestLineMatcher.group(2));
                    data.template().decodeSlash(ann.decodeSlash());
                    data.template().collectionFormat(ann.collectionFormat());
                }
            });
            // 省略其他处理器注册代码
            .... 
        }
}

通过以上源码可以看到,在使用@RequestLine注解时,必须声明HTTP的请求方式,并且为大写字母。

同时,DeclarativeContract类实现了BaseContract类的三个抽象方法 processAnnotationOnClass,processAnnotationOnMethod,processAnnotationsOnParameter。并且这些方法都以final修饰,不可被子类重写修改。在这三个方法中,使用Default注册的注解处理器,对类,方法和参数上的注解进行处理。

protected final void processAnnotationOnClass(MethodMetadata data, Class<?> targetType) {
    Arrays.stream(targetType.getAnnotations()).forEach((annotation) -> {
        this.classAnnotationProcessors.stream().filter((processor) -> {
            return processor.test(annotation);
        }).forEach((processor) -> {
            processor.process(annotation, data);
        });
    });
}

protected final void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, Method method) {
    this.methodAnnotationProcessors.stream().filter((processor) -> {
        return processor.test(annotation);
    }).forEach((processor) -> {
        processor.process(annotation, data);
    });
}

protected final boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
    Arrays.stream(annotations).filter((annotation) -> {
        return this.parameterAnnotationProcessors.containsKey(annotation.annotationType());
    }).forEach((annotation) -> {
        ((DeclarativeContract.ParameterAnnotationProcessor)this.parameterAnnotationProcessors.getOrDefault(annotation.annotationType(), DeclarativeContract.ParameterAnnotationProcessor.DO_NOTHING)).process(annotation, data, paramIndex);
    });
    return false;
}

至此,我们可以清晰的知道,Contract接口声明了解析方法元数据的方法parseValidateMetadata;抽象类BaseContract实现了该接口方法,并声明了处理类,方法和参数上的注解的模板方法;抽象类DeclarativeContract实现了BaseContract的模板方法,并设置了注解处理器的注册方法;Default类继承DeclarativeContract抽象类,并使用注册方法注册OpenFeign的注解处理器。在解析方法元数据时,通过Default注册的注解处理器来处理注解生成方法的元数据。

由此可以看出,OpenFeign有极大的扩展性,我们可以定制自己的注解,并重新实现DeclarativeContract抽象类来注册自己的处理器,就可以扩展注解的使用。同时,在官方文档中也说明了可以支持多种协议Contract,如Spring4,SOAP等等。
在这里插入图片描述

在完成方法元数据解析后,我们再回到MethodHandler创建的步骤中,接下来就是根据方法的元数据来创建不同的buildTempalte。

BuildTemplateByResolvingArgs
while(var4.hasNext()) {
    MethodMetadata md = (MethodMetadata)var4.next();
    Object buildTemplate;
    if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
        buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
    } else if (md.bodyIndex() != null) {
        buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
    } else {
        buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md, this.queryMapEncoder, target);
    }
    
    ....
}

以上源码说明了通过解析到的方法的元数据,为每个方法创建不同的buildTemplate对象,该对象实际是个factory对象,用来创建RequestTemplate。其中,BuildFormEncodedTemplateFromArgs和BuildEncodedTemplateFromArgs都继承自BuildTemplateByResolvingArgs类。在解析时生成RequestTemplate时,对请求参数或者请求体使用配置的Encoder进行编码。

private static class BuildEncodedTemplateFromArgs extends ReflectiveFeign.BuildTemplateByResolvingArgs {
    private final Encoder encoder;
    ....
}

private static class BuildFormEncodedTemplateFromArgs extends ReflectiveFeign.BuildTemplateByResolvingArgs {
    private final Encoder encoder;
    ....
}

private static class BuildTemplateByResolvingArgs implements Factory {
    private final QueryMapEncoder queryMapEncoder;
    protected final MethodMetadata metadata;
    protected final Target<?> target;
    private final Map<Integer, Expander> indexToExpander;
    ....
}

interface Factory {
    RequestTemplate create(Object[] var1);
}

在创建完buildTemplate对象后,以此为参数使用synchronousMethodHandlerFactory创建方法对应的MethodHandler。

synchronousMethodHandlerFactory.create
result.put(md.configKey(), this.factory.create(target, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder));

方法的configKey在解析元数据时生成,其形式为 classSimpleName#methodName(paramTypeSimpleName,…)

在得到方法对应的MethodHandler的Map集合后,我们回到创建代理的newInstance方法中,第二步是通过反射获取接口中的方法对象,并以之前获取的Map<configKey, MethodHandler> map为依据,获取Map<Method, MethodHandler> nameToHandler的map集合。生成代理对象使用的InvocationHandler对象。

InvocationHandler

在该步骤中,我们重点关注的是接口中默认方法是如何处理的。

在java8中,允许接口中声明实现默认方法Default Methods,针对默认方法如何创建其对应的MethodHandler?

 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)));
        }
    }
}

通过源码可以看出,当判断方法为默认方法时,创建DefaultMethodHandler对象,在上面提到正常方法的methodHandler是通过解析的方法元数据创建的,而默认方法则是通过方法句柄来创建MethodHandler。

public DefaultMethodHandler(Method defaultMethod) {
    try {
        Class<?> declaringClass = defaultMethod.getDeclaringClass();
        Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP");
        field.setAccessible(true);
        Lookup lookup = (Lookup)field.get((Object)null);
        // 通过Lookup的unreflectSpecial创建默认方法的methodHandler
        this.unboundHandle = lookup.unreflectSpecial(defaultMethod, declaringClass);
    } catch (NoSuchFieldException var5) {
        throw new IllegalStateException(var5);
    } catch (IllegalAccessException var6) {
        throw new IllegalStateException(var6);
    }
}

关于什么是方法句柄,请看以下文章进行学习了解:
《JDK1.7方法句柄学习》
《Method Handles in Java》

如何通过方法句柄调用默认方法,请看《How to explicitly invoke default method from a dynamic Proxy?》

在创建完InvocationHandler后,进行第三步使用JDK动态代理创建代理对象。

Proxy.newProxyInstance

通过JDK动态代理创建代理对象,这也是为什么要使用接口声明方法的原因。在创建完代理对象后,将默认方法的MethodHandler绑定到代理对象上。

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);
}

至此,OpenFeign配置生成代理过程的源码已经阅读完毕,可以发现框架的根本就是反射和代理,这涉及到java的基础知识,反射,动态代理,方法句柄,设计模式等等。OpenFeign以其出色的设计,支持了极强的扩展性并保持了使用的简单性,非常值得我们去研究学习。

本篇博文我对OpenFeign代理对象过程中涉及到的组件扩展配置,协议解析方法元数据,MethodHandler对象的生成及最后代理对象的生成的代码进行了阅读总结,后续我将继续对执行过程中的请求生成,参数编码,结果解码,重试控制等源码进行学习总结。

参考资料:
https://blog.csdn.net/luanlouis/article/details/82821294

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ork.cloud:spring-cloud-starter-openfeign:jar是一个基于Spring Cloud的开源库,用于简化微服务架构中服务之间的调用和通信。 OpenFeign是一个声明式的Web服务客户端,它简化了编写HTTP请求的代码,使得服务间调用更加简单和高效。它是Spring Cloud提供的一个集成了Ribbon和Hystrix的库,可以方便地与其他微服务组件集成使用。 使用OpenFeign,我们可以通过编写接口的方式来定义服务间的调用,而不需要关注底层的HTTP请求。通过注解配置OpenFeign会自动根据接口定义生成对应的代理类,并且提供了负载均衡、断路器等功能,方便处理高并发和服务故障的情况。 在微服务架构中,服务之间的调用是非常频繁的,而且随着微服务的增多,手动编写HTTP请求的代码会变得非常繁琐和容易出错。使用OpenFeign可以大大简化服务之间的调用流程,提高开发效率和代码质量。 总结来说,ork.cloud:spring-cloud-starter-openfeign:jar是一个方便而强大的库,可以帮助我们简化微服务架构中服务之间的调用和通信,并且提供了负载均衡和断路器等功能,能够提高系统的可靠性和性能。 ### 回答2: ork.cloud:spring-cloud-starter-openfeign:jar是一个基于Spring Cloud的开源项目,它提供了一种方便的编写和调用RESTful服务的方式。Feign是一个声明式的Web服务客户端,它可以简化HTTP请求的处理和封装,使得开发者可以更加专注于业务逻辑的编写。 使用Spring Cloud Starter OpenFeign可以快速地编写和调用其他微服务。它通过注解的方式将HTTP请求映射到对应的方法上,自动进行了服务的发现和负载均衡。 Feign支持多种请求方式,包括GET、POST、PUT、DELETE等,还可以使用@PathVariable、@RequestParam等注解处理路径参数和查询参数。Feign还支持对请求体进行处理,可以将请求体转换成Java对象,方便业务逻辑的处理。 在使用Feign时,不需要手动编写HTTP请求的代码,只需要定义一个接口并使用Feign的注解进行标记即可。Feign会根据注解生成代理对象来完成请求的发送和接收。这样可以大大简化开发的工作量,并且使得代码更加清晰易读。 Spring Cloud Starter OpenFeign还集成了Ribbon和Hystrix,这使得我们在使用Feign时可以实现负载均衡和熔断的功能。即使请求的目标服务发生宕机或故障,也能够保证系统的高可用性和稳定性。 总之,Spring Cloud Starter OpenFeign是一个非常实用和方便的工具,可以简化微服务架构下的服务调用,并提供了负载均衡和熔断等功能。它的使用可以加快开发速度,提高系统的可靠性和稳定性。 ### 回答3: spring-cloud-starter-openfeign是一个开源的Spring Cloud组件,用于简化在微服务架构中进行远程服务调用的过程。它基于Netflix的Feign库进行开发,提供了一种声明式的、基于接口的远程服务调用方式,可以方便地实现服务之间的通信和数据交互。 ork.cloud:spring-cloud-starter-openfeign:jar是spring-cloud-starter-openfeign组件的一个特定版本的jar包。在使用Spring Boot构建的项目中,可以通过引入这个jar包来集成并使用spring-cloud-starter-openfeign组件,从而简化远程服务调用的代码编写和配置。 使用spring-cloud-starter-openfeign,我们只需要定义一个接口,通过注解的方式声明远程服务的地址和调用方法,然后在需要调用远程服务的地方直接调用这个接口的方法即可。Spring Cloud会根据注解信息自动进行服务发现和负载均衡,将我们的调用请求转发到对应的服务实例上。 该jar包中除了包含spring-cloud-starter-openfeign的核心功能外,还可能包含一些额外的依赖库或工具,以及特定版本的相关代码和配置文件。通过引入这个jar包,我们可以一键集成和启用spring-cloud-starter-openfeign组件,省去了手动添加依赖和配置的步骤,能够更快速地搭建起微服务架构中的服务调用机制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值