@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ RetrofitConfiguration.class, RetrofitClientsRegistrar.class })
public @interface EnableRetrofitClients {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
Class<?>[] clients() default {};
从上述代码可见RetrofitConfiguration和RetrofitClientsRegistrar都会比实例化,RetrofitConfiguration过于简单就不看了,重点关注RetrofitClientsRegistrar,先来看它的类图,搞清楚继承和实现
如下图所示,RetrofitClientsRegistrar集成自AbstractRetrofitClientsRegistrar,而AbstractRetrofitClientsRegistrar又集成自ImportBeanDefinitionRegistrar
所以,RetrofitClientsRegistrar被实例化的时候,就相当于ImportBeanDefinitionRegistrar接口的实现类被实例化了,这个ImportBeanDefinitionRegistrar接口,相信熟悉spring的同学对其不会陌生,它是用来动态注册bean的,那么接下来的重点就是ImportBeanDefinitionRegistrar的registerBeanDefinitions方法的具体内容,看看它到底注册了什么bean
registerBeanDefinitions方法的代码在AbstractRetrofitClientsRegistrar.java中(请在上面的类图中找到AbstractRetrofitClientsRegistrar的位置),如下所示,由于EnableRetrofitClients修饰的是咱们创建的OkHttpClientConfig.java,所以下面的入参AnnotationMetadata是OkHttpClientConfig类的注解信息:
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerRetrofitClients(metadata, registry);
上述代码的第一个方法registerDefaultConfiguration是注册配置信息的,非重点,跳过
上述代码的第二个方法registerRetrofitClients,这是本篇的关键,请重点关注下面代码中的中文注释:
public void registerRetrofitClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
Map<String, Object> attrs = metadata.getAnnotationAttributes(getAnnotationClass().getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(RetrofitClient.class);
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
else {
final Set<String> clientClasses = new HashSet<>();
basePackages = new HashSet<>();
for (Class<?> clazz : clients) {
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
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)));
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@RetrofitClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(RetrofitClient.class.getCanonicalName());
String name = getClientName(attributes);
registerClientConfiguration(registry, name, attributes.get("configuration"));
registerRetrofitClient(registry, annotationMetadata, attributes);
将上述代码中最后调用的registerRetrofitClient方法展开如下,这段代码做了件很重要的事情:注册BeanDefinition到spring,注册的name等于com.bolingcavalry.consumer.service.HelloService,对应的BeanDefinition的beanClass等于RetrofitClientFactoryBean:
private void registerRetrofitClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(getFactoryBeanClass());
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = name + "RetrofitClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setPrimary(true);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
此刻,HelloService的类定义已经在spring完成了注册,接下来要看HelloService接口的实现类来自何处;
BeanDefinition中的RetrofitClientFactoryBean被实例化
在spring初始化过程中,上述红框中的代码会触发spring环境对HelloService接口实现类的实例化,完整的触发过程和详细堆栈就不细说了,这都是spring的标准处理流程,接下来会挑这里面的重点去看
首先就是大名鼎鼎的SpringApplication.refresh方法,这里面是bean的实例化逻辑,会执行一个重要方法,就是DefaultListableBeanFactory.doGetBeanNamesForType,这里面会遍历所有已注册的BeanDefinition,逐个处理,如下图:
DefaultListableBeanFactory.doGetBeanNamesForType继续执行,会到下一个重点:根据BeanDefinition创建bean,堆栈如下,这是用条件断点得到的:
doGetBean:256, AbstractBeanFactory (org.springframework.beans.factory.support) [2]
getTypeForFactoryBean:1709, AbstractBeanFactory (org.springframework.beans.factory.support)
getTypeForFactoryBean:899, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
isTypeMatch:637, AbstractBeanFactory (org.springframework.beans.factory.support)
doGetBeanNamesForType:583, DefaultListableBeanFactory (org.springframework.beans.factory.support)
getBeanNamesForType:542, DefaultListableBeanFactory (org.springframework.beans.factory.support)
beanNamesForTypeIncludingAncestors:265, BeanFactoryUtils (org.springframework.beans.factory)
findAutowireCandidates:1546, DefaultListableBeanFactory (org.springframework.beans.factory.support)
doResolveDependency:1343, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveDependency:1300, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveAutowiredArgument:887, ConstructorResolver (org.springframework.beans.factory.support)
createArgumentArray:791, ConstructorResolver (org.springframework.beans.factory.support)
instantiateUsingFactoryMethod:541, ConstructorResolver (org.springframework.beans.factory.support)
instantiateUsingFactoryMethod:1334, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBeanInstance:1177, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:564, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:524, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:335, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 1485624601 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$488)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:333, AbstractBeanFactory (org.springframework.beans.factory.support) [1]
getBean:213, AbstractBeanFactory (org.springframework.beans.factory.support)
registerBeanPostProcessors:258, PostProcessorRegistrationDelegate (org.springframework.context.support)
registerBeanPostProcessors:762, AbstractApplicationContext (org.springframework.context.support)
refresh:567, AbstractApplicationContext (org.springframework.context.support)
refresh:769, SpringApplication (org.springframework.boot)
refresh:761, SpringApplication (org.springframework.boot)
refreshContext:426, SpringApplication (org.springframework.boot)
run:326, SpringApplication (org.springframework.boot)
loadContext:123, SpringBootContextLoader (org.springframework.boot.test.context)
loadContextInternal:99, DefaultCacheAwareContextLoaderDelegate (org.springframework.test.context.cache)
loadContext:124, DefaultCacheAwareContextLoaderDelegate (org.springframework.test.context.cache)
getApplicationContext:124, DefaultTestContext (org.springframework.test.context.support)
setUpRequestContextIfNecessary:190, ServletTestExecutionListener (org.springframework.test.context.web)
prepareTestInstance:132, ServletTestExecutionListener (org.springframework.test.context.web)
prepareTestInstance:244, TestContextManager (org.springframework.test.context)
postProcessTestInstance:138, SpringExtension (org.springframework.test.context.junit.jupiter)
lambda$invokeTestInstancePostProcessors$6:350, ClassBasedTestDescriptor (org.junit.jupiter.engine.descriptor)
execute:-1, 2001115307 (org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$344)
executeAndMaskThrowable:355, ClassBasedTestDescriptor (org.junit.jupiter.engine.descriptor)
lambda$invokeTestInstancePostProcessors$7:350, ClassBasedTestDescriptor (org.junit.jupiter.engine.descriptor)
accept:-1, 1650113431 (org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$343)
accept:-1, 796667727 (java.util.stream.StreamSpliterators$WrappingSpliterator$$Lambda$107)
accept:193, ReferencePipeline$3$1 (java.util.stream)
accept:175, ReferencePipeline$2$1 (java.util.stream)
forEachRemaining:1384, ArrayList$ArrayListSpliterator (java.util)
copyInto:482, AbstractPipeline (java.util.stream)
wrapAndCopyInto:472, AbstractPipeline (java.util.stream)
forEachRemaining:312, StreamSpliterators$WrappingSpliterator (java.util.stream)
forEachRemaining:743, Streams$ConcatSpliterator (java.util.stream)
forEachRemaining:742, Streams$ConcatSpliterator (java.util.stream)
forEach:580, ReferencePipeline$Head (java.util.stream)
invokeTestInstancePostProcessors:349, ClassBasedTestDescriptor (org.junit.jupiter.engine.descriptor)
lambda$instantiateAndPostProcessTestInstance$4:270, ClassBasedTestDescriptor (org.junit.jupiter.engine.descriptor)
execute:-1, 1547883191 (org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$342)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
instantiateAndPostProcessTestInstance:269, ClassBasedTestDescriptor (org.junit.jupiter.engine.descriptor)
lambda$testInstancesProvider$2:259, ClassBasedTestDescriptor (org.junit.jupiter.engine.descriptor)
get:-1, 795748540 (org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$335)
orElseGet:267, Optional (java.util)
lambda$testInstancesProvider$3:258, ClassBasedTestDescriptor (org.junit.jupiter.engine.descriptor)
getTestInstances:-1, 361398902 (org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$$Lambda$234)
getTestInstances:31, TestInstancesProvider (org.junit.jupiter.engine.execution)
lambda$prepare$0:101, TestMethodTestDescriptor (org.junit.jupiter.engine.descriptor)
execute:-1, 451312813 (org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$334)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
prepare:100, TestMethodTestDescriptor (org.junit.jupiter.engine.descriptor)
prepare:65, TestMethodTestDescriptor (org.junit.jupiter.engine.descriptor)
lambda$prepare$1:111, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 1008315045 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$182)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
prepare:111, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:79, NodeTestTask (org.junit.platform.engine.support.hierarchical)
accept:-1, 1976870338 (org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService$$Lambda$201)
forEach:1259, ArrayList (java.util)
invokeAll:38, SameThreadHierarchicalTestExecutorService (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$5:143, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 1647809929 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$197)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$7:129, NodeTestTask (org.junit.platform.engine.support.hierarchical)
invoke:-1, 928294079 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$196)
around:137, Node (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$8:127, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 728885526 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$195)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
executeRecursively:126, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:84, NodeTestTask (org.junit.platform.engine.support.hierarchical)
accept:-1, 1976870338 (org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService$$Lambda$201)
forEach:1259, ArrayList (java.util)
invokeAll:38, SameThreadHierarchicalTestExecutorService (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$5:143, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 1647809929 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$197)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$7:129, NodeTestTask (org.junit.platform.engine.support.hierarchical)
invoke:-1, 928294079 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$196)
around:137, Node (org.junit.platform.engine.support.hierarchical)
lambda$executeRecursively$8:127, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:-1, 728885526 (org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$195)
execute:73, ThrowableCollector (org.junit.platform.engine.support.hierarchical)
executeRecursively:126, NodeTestTask (org.junit.platform.engine.support.hierarchical)
execute:84, NodeTestTask (org.junit.platform.engine.support.hierarchical)
submit:32, SameThreadHierarchicalTestExecutorService (org.junit.platform.engine.support.hierarchical)
execute:57, HierarchicalTestExecutor (org.junit.platform.engine.support.hierarchical)
execute:51, HierarchicalTestEngine (org.junit.platform.engine.support.hierarchical)
execute:108, EngineExecutionOrchestrator (org.junit.platform.launcher.core)
execute:88, EngineExecutionOrchestrator (org.junit.platform.launcher.core)
lambda$execute$0:54, EngineExecutionOrchestrator (org.junit.platform.launcher.core)
accept:-1, 607932305 (org.junit.platform.launcher.core.EngineExecutionOrchestrator$$Lambda$150)
withInterceptedStreams:67, EngineExecutionOrchestrator (org.junit.platform.launcher.core)
execute:52, EngineExecutionOrchestrator (org.junit.platform.launcher.core)
execute:96, DefaultLauncher (org.junit.platform.launcher.core)
execute:75, DefaultLauncher (org.junit.platform.launcher.core)
startRunnerWithArgs:71, JUnit5IdeaTestRunner (com.intellij.junit5)
startRunnerWithArgs:33, IdeaTestRunner$Repeater (com.intellij.rt.junit)
prepareStreamsAndStart:221, JUnitStarter (com.intellij.rt.junit)
main:54, JUnitStarter (com.intellij.rt.junit)
根据上述堆栈,要细看AbstractBeanFactory的doGetBean方法,请关注中文注释:
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object beanInstance;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
else {
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
else {
return (T) parentBeanFactory.getBean(nameToLookup);
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if (requiredType != null) {
beanCreation.tag("beanType", requiredType::toString);
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
registerDependentBean(dep, beanName);
try {
getBean(dep);
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
finally {
afterPrototypeCreation(beanName);
beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
else {
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
finally {
afterPrototypeCreation(beanName);
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
catch (IllegalStateException ex) {
throw new ScopeNotActiveException(beanName, scopeName, ex);
catch (BeansException ex) {
beanCreation.tag("exception", ex.getClass().toString());
beanCreation.tag("message", String.valueOf(ex.getMessage()));
cleanupAfterBeanCreationFailure(beanName);
throw ex;
finally {
beanCreation.end();
return adaptBeanInstance(name, beanInstance, requiredType);
至此,RetrofitClientFactoryBean已经完成了实例化,接下来要去看HelloService接口背后的bean是怎么创建的
HelloService对应的bean是如何创建的
回顾一下,咱们的应用代码中用到HelloService的场景,如下图红框所示,使用Autowired注解修饰HelloService:
首先,上图中的RemoteHello是一定会创建bean的,在创建的过程中,DefaultListableBeanFactory.doResolveDependency方法负责处理RemoteHello依赖的bean,如下图所示,在此处触发了HelloService的bean的实例化
辗转反侧,再次走到了AbstractBeanFactory.doGetBean方法,这次会执行下图第二个红框中的getObjectForBeanInstance方法:
然后到了最关键的位置:AbstractBeanFactory.getObjectForBeanInstance方法,这里面将RetrofitClientFactoryBean当做factory用了,用来生产HelloService:
将上图红框2中的getObjectFromFactoryBean方法继续展开,进入FactoryBeanRegistrySupport.doGetObjectFromFactoryBean方法,这里完成了从spring框架到应用自定义之间的过渡:将bean的创建交给应用自己注册的Factory来处理:
在RetrofitClientFactoryBean.getObject中,执行loadBalance(builder, context, serviceIdUrl):
loadBalance的实现在RetrofitClientFactoryBean中:
protected Object loadBalance(Retrofit.Builder builder, RetrofitContext context, String serviceIdUrl) {
Map<String, OkHttpClient.Builder> instances = context.getInstances(this.name, OkHttpClient.Builder.class);
for (Map.Entry<String, OkHttpClient.Builder> entry : instances.entrySet()) {
String beanName = entry.getKey();
OkHttpClient.Builder clientBuilder = entry.getValue();
if (applicationContext.findAnnotationOnBean(beanName, LoadBalanced.class) != null) {
builder.client(clientBuilder.build());
Retrofit retrofit = buildAndSave(context, builder);
return retrofit.create(this.type);
throw new IllegalStateException(
"No Retrofit Client for loadBalancing defined. Did you forget to include spring-cloud-starter-square-okhttp?");
从上面的分析可见,咱们只写HelloService接口不写HelloService实现的关键就是retrofit.create方法,传入了一个接口定义,就能返回该接口的实现类的实例
说实话retrofit.create的源码并不属于spring-cloud-square,而是Retrofit自己的,在本文中看这段源码属于超纲,但我还是忍不住要看一下:
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
至此终于真像大白,最终还是用Proxy.newProxyInstance生成了HelloService的代理类实例,作为HelloService.hello调用背后的真正实现
最后似乎还有一点疑问,就是HelloService的RetrofitClient注解的属性是服务名provider,那么真正网络请求的时候,是如何转成真实的地址和端口的呢?
再回头看看咱应用consumer-retrofit-okhttp的pom.xml文件,如下图红框所示,和前文一样,这里也使用了spring-cloud-square-okhttp,而且咱们写的OkHttpClientConfig.java和前文也是一样的,所以,根据服务名获取地址和端口的操作依旧可以用前文的分析来解释:
至于HelloService.hello方法,如何对应到web请求,请容我说一声:这是retrofit和okhttp之间的事情,在这里算是超纲了,篇幅所限,实在展不开了...
尾记:关于另一种spring-cloud-square类型:retrofit + webflux
之前的文章已经分析过,spring-cloud-square一共有三种类型,如下图所示,两个绿框中的源码都分析过了,还剩下的只有红色的retrofit + webflux组合:
至此《spring-cloud-square学习》系列已经全部完成,希望这四篇文章可以帮助您全面掌握spring-cloud-square,在您的项目中对远程调用的操作更加得心应手;
你不孤单,欣宸原创一路相伴
Java系列
Spring系列
Docker系列
kubernetes系列
数据库+中间件系列
DevOps系列
欢迎关注公众号:程序员欣宸
微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...
github.com/zq2599/blog…