歡迎訪問我的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的另一種型別的原始碼:<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部分的原理,卻又苦於沒有時間深入研究,可以看看下面這份提前小結的內容:
- 整個機制的運轉,可以分為相對獨立的四個部分:業務應用編碼使用spring-cloud-square相關的註解、bean的factory註冊到spring環境、bean的factory類在spring環境例項化、通過factory的例項在spring生產HelloService介面的實現類
- 根據上面的分析,最重要的應該是bean的factory類:<font color="blue">RetrofitClientFactoryBean</font>,它實現了FactoryBean介面,其getObject方法就是根據HelloService介面生成實現類和關鍵,最終會呼叫下圖紅框中的Retrofit.create方法建立例項:
- Retrofit類並非spring-cloud的專案,而是來自Retrofit庫,其create方法中使用了JDK的Proxy.newProxyInstance方法,該方法可以根據HelloService介面生成一個實現了該介面的例項:
- 在使用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子工程)
- 新建配置類OkHttpClientConfig,使用了EnableRetrofitClients註解,向spring環境註冊OkHttpClient.Builder例項:
@Configuration
@EnableRetrofitClients
class OkHttpClientConfig{
@Bean
@LoadBalanced
public OkHttpClient.Builder okHttpClientBuilder() {
return new OkHttpClient.Builder();
}
}
- 定義HelloService介面,用註解RetrofitClient修飾,註解的值是遠端呼叫的服務名<font color="blue"></font>,裡面宣告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,裡面使用了註解<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,在您的專案中對遠端呼叫的操作更加得心應手;
你不孤單,欣宸原創一路相伴
- Java系列
- Spring系列
- Docker系列
- kubernetes系列
- 資料庫+中介軟體系列
- DevOps系列
歡迎關注公眾號:程式設計師欣宸
微信搜尋「程式設計師欣宸」,我是欣宸,期待與您一同暢遊Java世界...
https://github.com/zq2599/blog_demos