Welcome to my GitHub
Here is a classification and summary of all original (including supporting source code): 16189ba0c5aae5 https://github.com/zq2599/blog_demos
spring-cloud-square series of articles
- spring-cloud-square in five minutes
- spring-cloud-square development combat (full coverage of three types)
- spring-cloud-square source code quick reading (spring-cloud-square-okhttp)
- spring-cloud-square source code quick reading (retrofit + okhttp)
Overview of this article
- This article is the final article of the "spring-cloud-square learning" series, last article we learned about the source code and principles of the spring-cloud-square-okhttp library, today we will increase the difficulty a little, and look at the other aspects of spring-cloud-square. One type of source code: <font color="blue">spring-cloud-square-retrofit</font>, which is the one in the red box in the figure below:
Source code analysis goal
- Next, start to analyze the source code of the spring-cloud-square-retrofit project, as shown in the red box in the following figure:
- The goal of this article is very clear, only one thing is clear: when using spring-cloud-square, the consumer-retrofit-okhttp sub-project in the previous article is taken as an example, why we only wrote the HelloService interface, but it can be annotated through Autowired Use the HelloService implementation class?
Summary in advance
- If you want to understand the principles of the retrofit part of spring-cloud-square, but you don’t have time to study in depth, you can take a look at the following summary in advance:
- The operation of the entire mechanism can be divided into four relatively independent parts: business application coding uses spring-cloud-square-related annotations, bean factory registration to spring environment, bean factory class instantiation in spring environment, and factory instance. Produce the implementation class of HelloService interface in spring
- According to the above analysis, the most important thing should be the bean factory class: <font color="blue">RetrofitClientFactoryBean</font>, which implements the FactoryBean interface, and its getObject method is to generate the implementation class and key based on the HelloService interface. Call the Retrofit.create method in the red box in the figure below to create an instance:
- The Retrofit class is not a spring-cloud project, but from the Retrofit library. Its create method uses the Proxy.newProxyInstance method of the JDK, which can generate an instance that implements the interface based on the HelloService interface:
- When using the retrofit + okhttp solution of spring-cloud-square, the service name of the remote service is used in the HelloService interface instead of the address and port. This is because the spring-cloud-square-okhttp library is used, so the service name is transferred The logic for <font color="red">address+port</font> is consistent with the previous "spring-cloud-square source code quick reading (spring-cloud-square-okhttp)"
- The above is the conclusion of the entire source code analysis. I will organize the related code flow involved into a simplified diagram, as shown below:
Review how the application uses spring-cloud-square-retrofit
- Before analyzing the source code, first review the code in "spring-cloud-square development combat" , how we used <font color="blue">spring-cloud-square-retrofit</font> (corresponding Consumer-retrofit-okhttp subproject in demo)
- Create a new configuration class OkHttpClientConfig, use the EnableRetrofitClients annotation, and register the OkHttpClient.Builder instance with the spring environment:
@Configuration
@EnableRetrofitClients
class OkHttpClientConfig{
@Bean
@LoadBalanced
public OkHttpClient.Builder okHttpClientBuilder() {
return new OkHttpClient.Builder();
}
}
- Define the HelloService interface and decorate it with the annotation RetrofitClient. The value of the annotation is the service name of the remote call <font color="blue"></font>, and the hello method is declared inside, which is decorated with the annotation GET. The value of the annotation is the interface of the remote call path:
@RetrofitClient("provider")
public interface HelloService {
@GET("/hello-obj")
Call<HelloResponse> hello(@Query("name") String name);
}
- When the business needs to be called remotely, the HelloService interface can be modified with Autowired annotation to call the HelloService.hello method. As for where the instance corresponding to the interface comes from, the developer does not need to pay attention:
@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();
}
}
- The above are the key operations of using spring-cloud-square when we develop business code. Next, we will analyze the role of these operations from the perspective of source code.
Source code analysis (class definition registration stage)
- Recall the OkHttpClientConfig.java we wrote, which uses the annotation <font color="blue">EnableRetrofitClients</font>, this is the entry point for reading the code this time:
@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 {};
}
- From the above code, it can be seen that RetrofitConfiguration and RetrofitClientsRegistrar are both better than instantiation. RetrofitConfiguration is too simple to look at. Focus on RetrofitClientsRegistrar. First look at its class diagram to figure out inheritance and implementation.
- As shown in the figure below, RetrofitClientsRegistrar is integrated from AbstractRetrofitClientsRegistrar, and AbstractRetrofitClientsRegistrar is integrated from ImportBeanDefinitionRegistrar
- Therefore, when RetrofitClientsRegistrar is instantiated, it is equivalent to the implementation class of ImportBeanDefinitionRegistrar interface is instantiated. This ImportBeanDefinitionRegistrar interface, I believe students familiar with spring will not be unfamiliar with it, it is used to dynamically register beans, then the next The key point is the specific content of the registerBeanDefinitions method of ImportBeanDefinitionRegistrar, to see what beans it registers
- The code of the registerBeanDefinitions method is in AbstractRetrofitClientsRegistrar.java (please find the location of AbstractRetrofitClientsRegistrar in the above class diagram), as shown below, because EnableRetrofitClients modifies the OkHttpClientConfig.java we created, so the following input AnnotationMetadata is an annotation of the OkHttpClientConfig class information:
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerRetrofitClients(metadata, registry);
}
- The first method registerDefaultConfiguration of the above code is to register configuration information, it is not important, skip
- The second method of the above code, registerRetrofitClients, is the key to this article. Please pay attention to the Chinese comments in the following code:
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);
}
}
}
}
- Expand the registerRetrofitClient method last called in the above code as follows. This code does a very important thing: register BeanDefinition to spring, and the registered name is equal to <font color="blue">com.bolingcavalry.consumer.service.HelloService< /font>, the beanClass of the corresponding BeanDefinition is equal to <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);
}
- At this moment, the class definition of HelloService has been registered in Spring. Next, it depends on where the implementation class of the HelloService interface comes from;
RetrofitClientFactoryBean in BeanDefinition is instantiated
- In the spring initialization process, the code in the red box will trigger the spring environment to instantiate the HelloService interface implementation class. The complete triggering process and detailed stack will not be elaborated. These are all spring standard processing procedures. Pick the key points to see
- The first is the famous SpringApplication.refresh method, which is the bean instantiation logic, which will execute an important method, DefaultListableBeanFactory.doGetBeanNamesForType, which will traverse all registered BeanDefinitions and process them one by one, as shown in the following figure:
- DefaultListableBeanFactory.doGetBeanNamesForType continues to execute, and it will go to the next important point: create a bean according to BeanDefinition, the stack is as follows, which is obtained with conditional breakpoints:
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)
- According to the above stack, to take a closer look at the doGetBean method of AbstractBeanFactory, please pay attention to the Chinese comments:
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);
}
- At this point, the RetrofitClientFactoryBean has been instantiated. Next, let’s see how the bean behind the HelloService interface is created.
How to create the bean corresponding to HelloService
- To recap, the scenario where HelloService is used in our application code, as shown in the red box in the following figure, uses Autowired annotation to decorate HelloService:
- First of all, the RemoteHello in the above figure will definitely create a bean. During the creation process, the DefaultListableBeanFactory.doResolveDependency method is responsible for processing the beans that RemoteHello depends on. As shown in the following figure, the instantiation of the HelloService bean is triggered here
- After tossing and turning, I went to the AbstractBeanFactory.doGetBean method again, this time it will execute the getObjectForBeanInstance method in the second red box in the following figure:
- Then to the most critical position: AbstractBeanFactory.getObjectForBeanInstance method, which uses RetrofitClientFactoryBean as a factory to produce HelloService:
- Continue to expand the getObjectFromFactoryBean method in the red box 2 of the above figure, and enter the FactoryBeanRegistrySupport.doGetObjectFromFactoryBean method. This completes the transition from the spring framework to the application customization: the creation of the bean is handed over to the factory registered by the application:
- In RetrofitClientFactoryBean.getObject, execute loadBalance(builder, context, serviceIdUrl):
- The implementation of loadBalance is in 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?");
}
- It can be seen from the above analysis that the key to writing only the HelloService interface and not the HelloService implementation is the retrofit.create method. After an interface definition is passed in, an instance of the implementation class of the interface can be returned.
- To be honest, the source code of retrofit.create does not belong to spring-cloud-square, but Retrofit's own. In this article, this source code belongs to the super class, but I still can't help but take a look:
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);
}
});
}
- So far, it really looks like a big white. Finally, Proxy.newProxyInstance is used to generate a proxy class instance of HelloService as the real implementation behind the HelloService.hello call.
- Finally, there seems to be a little doubt, that is, the attribute of the RetrofitClient annotation of HelloService is the service name provider, then how to convert it to the real address and port when a real network request is made?
- Look back at the pom.xml file of our consumer-retrofit-okhttp, as shown in the red box in the following figure. Like the previous article, spring-cloud-square-okhttp is also used here, and the OkHttpClientConfig.java we wrote is also the same as the previous article. The same, so, the operation of obtaining the address and port according to the service name can still be explained by the previous analysis:
- As for the HelloService.hello method, how to correspond to the web request, please let me say: this is a matter between retrofit and okhttp, which is super-classified here, and limited by space, it can't be expanded...
Endnote: About another spring-cloud-square type: retrofit + webflux
- The previous article has been analyzed, there are three types of spring-cloud-square, as shown in the figure below, the source code in the two green boxes has been analyzed, and only the red one is left<font color="blue"> retrofit + webflux</font> combination:
- Xin Chen would like to write another article on source code analysis of <font color="blue">retrofit + webflux</font>? No, no, it’s too tiring to read the source code, and you are tired of reading the articles you write, so stop here.
- If you work hard and want to read and analyze the source code of <font color="blue">retrofit + webflux</font> independently, here is a suggestion. Do you remember the class diagram in the front of this article, as shown below, use <font color When ="blue">retrofit + webflux</font>, spring-cloud-square-retrofit-webclient.jar will be used, this jar also has OkHttpClientConfig annotation, and its import will instantiate the class in the red box in the figure below , This class is the entry point for you to read the source code:
- So far, the "spring-cloud-square learning" series has been completed. I hope these four articles can help you fully grasp spring-cloud-square, and make the remote call operation more handy in your project;
You are not alone, Xinchen and original are with you all the way
- Java series
- Spring series
- Docker series
- kubernetes series
- database + middleware series
- DevOps series
Welcome to pay attention to the public account: programmer Xin Chen
Search "Programmer Xin Chen" on WeChat, I am Xin Chen, and I look forward to traveling the Java world with you...
https://github.com/zq2599/blog_demos
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。