spring-cloud-square源码速读(retrofit + okhttp篇)

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

spring-cloud-square系列文章

  1. 五分钟搞懂spring-cloud-square
  2. spring-cloud-square开发实战(三种类型全覆盖)
  3. spring-cloud-square源码速读(spring-cloud-square-okhttp篇)
  4. spring-cloud-square源码速读(retrofit + okhttp篇)

本篇概览

  • 本文是《spring-cloud-square学习》系列的终篇,上一篇咱们了解了spring-cloud-square-okhttp库的源码和原理,今天提升一点难度,看看spring-cloud-square的另一种类型的源码:<font color="blue">spring-cloud-square-retrofit</font>,也就是下图红框中的那种:
在这里插入图片描述

源码分析目标

  • 接下来开始分析spring-cloud-square-retrofit工程的源码,如下图红框所示:
在这里插入图片描述
  • 本篇目标非常明确,只搞清楚一件事:在使用spring-cloud-square的时候,以前文的consumer-retrofit-okhttp子工程为例,为什么咱们只写了HelloService接口,但却能通过Autowired注解使用HelloService的实现类?

提前小结

  • 如果您想了解spring-cloud-square的retrofit部分的原理,却又苦于没有时间深入研究,可以看看下面这份提前小结的内容:
  1. 整个机制的运转,可以分为相对独立的四个部分:业务应用编码使用spring-cloud-square相关的注解、bean的factory注册到spring环境、bean的factory类在spring环境实例化、通过factory的实例在spring生产HelloService接口的实现类

  2. 根据上面的分析,最重要的应该是bean的factory类:<font color="blue">RetrofitClientFactoryBean</font>,它实现了FactoryBean接口,其getObject方法就是根据HelloService接口生成实现类和关键,最终会调用下图红框中的Retrofit.create方法创建实例:

在这里插入图片描述
  1. Retrofit类并非spring-cloud的项目,而是来自Retrofit库,其create方法中使用了JDK的Proxy.newProxyInstance方法,该方法可以根据HelloService接口生成一个实现了该接口的实例:
在这里插入图片描述
  1. 在使用spring-cloud-square的retrofit + okhttp方案时,HelloService接口中使用的还是远程服务的服务名,而不是地址和端口,这是因为使用了spring-cloud-square-okhttp库,所以服务名转为<font color="red">地址+端口</font>的逻辑与前文《spring-cloud-square源码速读(spring-cloud-square-okhttp篇)》保持一致
  • 以上就是整个源码分析的结论了,我将涉及到的关联代码流程整理成简图,如下所示:
在这里插入图片描述

回顾应用如何使用spring-cloud-square-retrofit

  • 在分析源码之前,先回顾一下《spring-cloud-square开发实战》中的代码,咱们当时是如何使用<font color="blue">spring-cloud-square-retrofit</font>的(对应demo中的consumer-retrofit-okhttp子工程)
  1. 新建配置类OkHttpClientConfig,使用了EnableRetrofitClients注解,向spring环境注册OkHttpClient.Builder实例:
@Configuration
@EnableRetrofitClients
class OkHttpClientConfig{
    @Bean
    @LoadBalanced
    public OkHttpClient.Builder okHttpClientBuilder() {
        return new OkHttpClient.Builder();
    }
}
  1. 定义HelloService接口,用注解RetrofitClient修饰,注解的值是远程调用的服务名<font color="blue"></font>,里面声明hello方法,用注解GET修饰,注解的值是远程调用的接口的path:
@RetrofitClient("provider")
public interface HelloService {
    @GET("/hello-obj")
    Call<HelloResponse> hello(@Query("name") String name);
}
  1. 在业务要做远程调用的时候,用Autowired注解修饰HelloService接口,即可调用HelloService.hello方法,至于接口对应的实例来自哪里,开发者不必关注:
@RestController
public class RemoteHello {
    @Autowired(required = false)
    HelloService helloService;

    @GetMapping("/remote-obj")
    public HelloResponse hello(@RequestParam("name") String name) throws IOException {
        return helloService.hello(name).execute().body();
    }
}
  • 以上就是咱们开发业务代码时使用spring-cloud-square的关键操作,接下来就从源码角度来分析这些操作到底发挥了什么作用

源码分析(类定义注册阶段)

  • 回忆一下咱们写的OkHttpClientConfig.java,里面使用了注解<font color="blue">EnableRetrofitClients</font>,这就是本次阅读代码的入口:
@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());
        // 过滤条件:有RetrofitClient注解修饰的类,对应咱们代码中的HelloService.java
        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) {
                // 找到的结果就是HelloService接口
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // verify annotated class is an interface
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    Assert.isTrue(annotationMetadata.isInterface(),
                            "@RetrofitClient can only be specified on an interface");

                    // 取得修饰HelloService类的RetrofitClient注解的所有属性
                    Map<String, Object> attributes = annotationMetadata
                            .getAnnotationAttributes(RetrofitClient.class.getCanonicalName());
                    // 根据这些属性,得到远程访问的服务名是provider
                    String name = getClientName(attributes);
                    // 在spring注册一个配置类,名为provider.RetrofitClientSpecification,
                    // 由于修饰HelloService的RetrofitClient注解并没有什么属性,所以这个配置类没有什么内容
                    registerClientConfiguration(registry, name, attributes.get("configuration"));
                    
                    // 这个方法要重点关注,
                    // 入参annotationMetadata是HelloService的元信息,
                    // attributes是修饰HelloService类的RetrofitClient注解的所有属性
                    registerRetrofitClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }
  • 将上述代码中最后调用的registerRetrofitClient方法展开如下,这段代码做了件很重要的事情:注册BeanDefinition到spring,注册的name等于<font color="blue">com.bolingcavalry.consumer.service.HelloService</font>,对应的BeanDefinition的beanClass等于<font color="red">RetrofitClientFactoryBean</font>:
private void registerRetrofitClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
            Map<String, Object> attributes) {
        // 由于注解修饰的是HelloService类,所以这里className等于com.bolingcavalry.consumer.service.HelloService
        String className = annotationMetadata.getClassName();
        // 注意getFactoryBeanClass()方法,来自RetrofitClientsRegistrar类,返回值是RetrofitClientFactoryBean.class,
        // 因此,RetrofitClientFactoryBean就被带入了definition中,
        // 注意,这个definition变量的类型是BeanDefinitionBuilder,
        // 其内部有个成员变量beanDefinition,此时该成员变量的beanClass字段已经被设置为RetrofitClientFactoryBean.class
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(getFactoryBeanClass());
        validate(attributes);
        // HelloService的RetrofitClient注解没有设置url属性,因此这里是空字符串
        definition.addPropertyValue("url", getUrl(attributes));
        // RetrofitClient注解的value属性配置为远程服务名,这里是provider
        String name = getName(attributes);
        definition.addPropertyValue("name", name);
        // 类型就是HelloService
        definition.addPropertyValue("type", className);
        // by_type,意味着autowire注解修饰HelloService的时候,可以用HelloService获取对应的实现类
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

        String alias = name + "RetrofitClient";
        
        // 通过BeanDefinitionBuilder得到了beanDefinition,
        // 这个beanDefinition的beanClass字段在前面已经被设置为RetrofitClientFactoryBean.class
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        
        beanDefinition.setPrimary(true);

        String qualifier = getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }
        
        // 将注册BeanDefinition所需的两个参数beanName和beanDefinition放入BeanDefinitionHolder对象中
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });
        // 完成BeanDefinition在spring环境的注册,name等于com.bolingcavalry.consumer.service.HelloService,对应的BeanDefinition的beanClass等于RetrofitClientFactoryBean(注意,这很重要)
        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 {
        // 入参name等于"com.bolingcavalry.consumer.service.HelloService"
        String beanName = transformedBeanName(name);
        Object beanInstance;

        // Eagerly check singleton cache for manually registered singletons.
        Object sharedInstance = getSingleton(beanName);
        // sharedInstance等于null,因此下面的if判断不成立
        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 {
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // Check if bean definition exists in this factory.
            BeanFactory parentBeanFactory = getParentBeanFactory();
            // parentBeanFactory等于null,因此下面的if判断不成立
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                            nameToLookup, requiredType, args, typeCheckOnly);
                }
                else if (args != null) {
                    // Delegation to parent with explicit args.
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else if (requiredType != null) {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
                else {
                    return (T) parentBeanFactory.getBean(nameToLookup);
                }
            }
            
            // typeCheckOnly等于true,因此下面的if判断不成立
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
                    .tag("beanName", name);
            try {
                if (requiredType != null) {
                    beanCreation.tag("beanType", requiredType::toString);
                }
                // 前面咱们分析过,BeanDefinition已经注册到spring环境了,
                // 此处调用getMergedLocalBeanDefinition即可取得这个BeanDefinition
                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Guarantee initialization of beans that the current bean depends on.
                String[] dependsOn = mbd.getDependsOn();
                // HelloService的BeanDefinition没有依赖,
                // 因此dependsOn等于null,下面的if不成立
                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);
                        }
                    }
                }

                // Create bean instance.
                // HelloService的bean是单例,因此下面的if判断成立
                if (mbd.isSingleton()) {
                    // 这里是创建bean的关键!!!
                    // getSingleton传入一个lambda表达式,方法内会调用该表达式,
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            // 根据BeanDefinition创建bean,
                            // 实际上执行的是AbstractAutowireCapableBeanFactory.createBean方法
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    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) {
        // 应用代码的OkHttpClientConfig.java中,okHttpClientBuilder方法生成了OkHttpClient.Builder实例,此处的instances中就是这个实例
        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();
            // 应用代码的OkHttpClientConfig.java中,okHttpClientBuilder方法上已经用了LoadBalanced注解,
            //所以下面这个if判断为true
            if (applicationContext.findAnnotationOnBean(beanName, LoadBalanced.class) != null) {
                // 创建了OkHttpClient实例,传给了这个Retrofit.Builder
                builder.client(clientBuilder.build());
                // 使用这个Retrofit.Builder去创建retrofit,相当于把上面创建的OkHttpClient实例带给了retrofit
                // 所以,这个retrofit实例的底层就是OkHttpClient
                Retrofit retrofit = buildAndSave(context, builder);
                // type的类型是HelloService,
                // retrofit.create就是要创建一个实例,该实例实现了HelloService接口
                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) {
    // 一些检查,例如service是不是接口
    validateServiceInterface(service);
    return (T)
        // 这个实例是通过JDK的Proxy.newProxyInstance创建的
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];
                
              // 业务应用执行HelloService的hello方法时,实际上执行的是下面的方法   
              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                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一共有三种类型,如下图所示,两个绿框中的源码都分析过了,还剩下的只有红色的<font color="blue">retrofit + webflux</font>组合:
在这里插入图片描述
  • 欣宸还要再写一篇<font color="blue">retrofit + webflux</font>源码分析的文章?不不不,读源码太累,写出的文章,聪明的您读起来也累,所以就此打住吧

  • 如果勤奋努力的您想独立阅读分析<font color="blue">retrofit + webflux</font>源码,这里给您一个建议,还记得本篇前面的那个类图吗,如下图,使用<font color="blue">retrofit + webflux</font>的时候,会用到spring-cloud-square-retrofit-webclient.jar,这个jar里面也有OkHttpClientConfig注解,它的import会实例化下图红框中的类,这个类就是您阅读源码的入口:

在这里插入图片描述
  • 至此《spring-cloud-square学习》系列已经全部完成,希望这四篇文章可以帮助您全面掌握spring-cloud-square,在您的项目中对远程调用的操作更加得心应手;

你不孤单,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列

欢迎关注公众号:程序员欣宸

微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...

https://github.com/zq2599/blog_demos

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容