spring-cloud-square源码速读(retrofit + okhttp篇)
Posted 程序员欣宸
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring-cloud-square源码速读(retrofit + okhttp篇)相关的知识,希望对你有一定的参考价值。
欢迎访问我的GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
spring-cloud-square系列文章
- 五分钟搞懂spring-cloud-square
- spring-cloud-square开发实战(三种类型全覆盖)
- spring-cloud-square源码速读(spring-cloud-square-okhttp篇)
- spring-cloud-square源码速读(retrofit + okhttp篇)
本篇概览
- 本文是《spring-cloud-square学习》系列的终篇,上一篇咱们了解了spring-cloud-square-okhttp库的源码和原理,今天提升一点难度,看看spring-cloud-square的另一种类型的源码:spring-cloud-square-retrofit,也就是下图红框中的那种:
源码分析目标
- 接下来开始分析spring-cloud-square-retrofit工程的源码,如下图红框所示:
- 本篇目标非常明确,只搞清楚一件事:在使用spring-cloud-square的时候,以前文的consumer-retrofit-okhttp子工程为例,为什么咱们只写了HelloService接口,但却能通过Autowired注解使用HelloService的实现类?
提前小结
- 如果您想了解spring-cloud-square的retrofit部分的原理,却又苦于没有时间深入研究,可以看看下面这份提前小结的内容:
- 整个机制的运转,可以分为相对独立的四个部分:业务应用编码使用spring-cloud-square相关的注解、bean的factory注册到spring环境、bean的factory类在spring环境实例化、通过factory的实例在spring生产HelloService接口的实现类
- 根据上面的分析,最重要的应该是bean的factory类:RetrofitClientFactoryBean,它实现了FactoryBean接口,其getObject方法就是根据HelloService接口生成实现类和关键,最终会调用下图红框中的Retrofit.create方法创建实例:
- Retrofit类并非spring-cloud的项目,而是来自Retrofit库,其create方法中使用了JDK的Proxy.newProxyInstance方法,该方法可以根据HelloService接口生成一个实现了该接口的实例:
- 在使用spring-cloud-square的retrofit + okhttp方案时,HelloService接口中使用的还是远程服务的服务名,而不是地址和端口,这是因为使用了spring-cloud-square-okhttp库,所以服务名转为地址+端口的逻辑与前文《spring-cloud-square源码速读(spring-cloud-square-okhttp篇)》保持一致
- 以上就是整个源码分析的结论了,我将涉及到的关联代码流程整理成简图,如下所示:
回顾应用如何使用spring-cloud-square-retrofit
- 在分析源码之前,先回顾一下《spring-cloud-square开发实战》中的代码,咱们当时是如何使用spring-cloud-square-retrofit的(对应demo中的consumer-retrofit-okhttp子工程)
- 新建配置类OkHttpClientConfig,使用了EnableRetrofitClients注解,向spring环境注册OkHttpClient.Builder实例:
@Configuration
@EnableRetrofitClients
class OkHttpClientConfig{
@Bean
@LoadBalanced
public OkHttpClient.Builder okHttpClientBuilder() {
return new OkHttpClient.Builder();
}
}
- 定义HelloService接口,用注解RetrofitClient修饰,注解的值是远程调用的服务名,里面声明hello方法,用注解GET修饰,注解的值是远程调用的接口的path:
@RetrofitClient("provider")
public interface HelloService {
@GET("/hello-obj")
Call<HelloResponse> hello(@Query("name") String name);
}
- 在业务要做远程调用的时候,用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,里面使用了注解EnableRetrofitClients,这就是本次阅读代码的入口:
@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等于com.bolingcavalry.consumer.service.HelloService,对应的BeanDefinition的beanClass等于RetrofitClientFactoryBean:
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以上是关于spring-cloud-square源码速读(retrofit + okhttp篇)的主要内容,如果未能解决你的问题,请参考以下文章
spring-cloud-square源码速读(spring-cloud-square-okhttp篇)
spring-cloud-square源码速读(spring-cloud-square-okhttp篇)
spring-cloud-square开发实战(三种类型全覆盖)