spring startup process exploration 1. register(componentClasses);

Posted by raccer on Fri, 04 Mar 2022 07:09:46 +0100

spring launch process

Spring has been used for many years, and I will occasionally look at or study the spring source code. Looking at the spring source code really makes it very easy for me to work and develop. However, I have never systematically explored the complete start-up process of spring. From these days, I began to sort out the start-up process of spring step by step.

start

To explore spring, you have to start it explicitly. I just did a simple test here:

		// Instantiate bean procedure
		AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(ConfigUtils.class);
Object b = annotationConfigApplicationContext.getBean("b");

Then we can start our spring exploration by starting debug.

AnnotationConfigApplicationContext(Class<?>... componentClasses)

	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		//1. Instantiate AnnotationConfigApplicationContext and initialize reader(
		// AnnotatedBeanDefinitionReader) and scanner (ClassPathBeanDefinitionScanner)
		// Instantiate genericapplicationcontext - > extensions - > abstractapplicationcontext - > defaultresourceloader
		//								 ->Call defaultlistablebeanfactory - > extensions - > abstractautowirecapablebeanfactory - >
		//								 extends->AbstractBeanFactory->extends

		this();

		register(componentClasses);
 		refresh();
	}

As you can see, the first person who comes in is AnnotationConfigApplicationContext (I load spring through annotation)
There are three main methods. The first one is this(); In this method, we don't study him in detail here, but just take him. We just need to know that he mainly did two jobs during this ().
1. Initialize reader and scanner
2. Initialize the DefaultListableBeanFactory (this is what we call the bean factory)
Next, we need to study register (component classes); That's the way

register(componentClasses);

@Override
	public void register(Class<?>... componentClasses) {
		Assert.notEmpty(componentClasses, "At least one component class must be specified");
		this.reader.register(componentClasses);
	}

First, let's look at the annotation Register one or more component classes to be processed (register one or more components to be processed) has roughly explained the function of this method. Then we went down his call chain.

	public void register(Class<?>... componentClasses) {
		for (Class<?> componentClass : componentClasses) {
			registerBean(componentClass);
		}
	}
public void registerBean(Class<?> beanClass) {
		doRegisterBean(beanClass, null, null, null, null);
	}
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
			@Nullable BeanDefinitionCustomizer[] customizers) {
		// Wrap the bean as AnnotatedGenericBeanDefinition
		//Metadata is standardannotation metadata
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(supplier);
		// The scope of metadata. Get scope from metadata Value of class
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());
 		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

		// Handle some general annotations
		// Lazy DependsOn Role
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		if (customizers != null) {
			for (BeanDefinitionCustomizer customizer : customizers) {
				customizer.customize(abd);
			}
		}

		// Under packaging
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}

Following his call chain, we come to his core method doRegisterBean. First, we need to look at the annotation,
Register a bean from the given bean class, deriving its metadata from class-declared annotations. (register a bean from a given bean class and derive its metadata from the annotation declared by the class.)
In this method, there are more places to analyze.
Let's do it step by step

  1.  AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
    
    In fact, this method can roughly understand his meaning at a glance, which is to package beanClass into an AnnotatedGenericBeanDefinition. We can jump in and have a look.

AnnotatedGenericBeanDefinition(beanClass);

	/**
	 * Create a new AnnotatedGenericBeanDefinition for the given bean class.
	 * @param beanClass the loaded bean class
	 */
	public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
		// Call the parent class AbstractBeanDefinition and set beanClass to the incoming beanClass
		setBeanClass(beanClass);
		this.metadata = AnnotationMetadata.introspect(beanClass);
	}

The first setBeanClass is not detailed, just look at the annotation. For the second method, we continue to track its call chain

	static AnnotationMetadata introspect(Class<?> type) {
		return StandardAnnotationMetadata.from(type);
	}
static AnnotationMetadata from(Class<?> introspectedClass) {
		return new StandardAnnotationMetadata(introspectedClass, true);
	}
	public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
		// Given base class
		super(introspectedClass);
		// Get a typemapedannotations
		this.mergedAnnotations = MergedAnnotations.from(introspectedClass,
				SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none());
		this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
	}

After tracing to the end, it is found that he finally returned the instance of standardannotation metadata, and then did three things during new
1. Assign value to introspectedClass of parent class StandardClassMetadata
2. Get a merged typemapedannotations (as for how to get this, interested partners can follow it up and have a look. I won't talk about it in detail here)
3.this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
Well, I guess I understand when I get here
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
What does this line of code do
1. AnnotatedGenericBeanDefinition is generated, and the value of metadata in it is standardannotation metadata,
Call the parent class AbstractBeanDefinition and set beanClass to the incoming beanClass

resolveScopeMetadata

		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

This code is only used to judge whether the registered bean needs to be skipped. It will not be described in detail

ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());
		// Take the lowercase class name of the registered class
 		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

These lines of code can be discussed together. First, let's look at the call chain

	@Override
	public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
		ScopeMetadata metadata = new ScopeMetadata();
		//The definition passed in at this time implements AnnotatedBeanDefinition, so he will definitely make the following judgment
		if (definition instanceof AnnotatedBeanDefinition) {
			AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
			// Get the property of scopeAnnotationType from metadata
			AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
					annDef.getMetadata(), this.scopeAnnotationType);
					// Assign the value if you can get it
			if (attributes != null) {
				metadata.setScopeName(attributes.getString("value"));
				// If there is a proxy class, use the proxy class. If there is no proxy class, use the default
				ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
				if (proxyMode == ScopedProxyMode.DEFAULT) {
					proxyMode = this.defaultProxyMode;
				}
				metadata.setScopedProxyMode(proxyMode);
			}
		}
		return metadata;
	}

In fact, there is nothing to say in these lines. Let's see the notes

applyScopedProxyMode

	// Handle some general annotations
		// Lazy DependsOn Role
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		if (customizers != null) {
			for (BeanDefinitionCustomizer customizer : customizers) {
				customizer.customize(abd);
			}
		}

In fact, there is nothing to say about this code. You can understand it by looking at the comments. Are dealing with some general annotations

// Packaging BeanDefinitionHolder 
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
	// Here is to judge whether to create a proxy class
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		// Re register registerBeanDefinition
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	static BeanDefinitionHolder applyScopedProxyMode(
			ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
		
		ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
		// Normally, if you don't use an agent, the default scopedProxyMode is on. Here, the original definition is returned directly
		if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
			return definition;
		}
		boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
		// To create a proxy definition
		return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
	}

Here, basically register(componentClasses); Almost. In fact, the main work of this method is to read the annotations on the configuration class and then make some initial preparations. So what I said is very simple. I didn't start talking about many places. If you don't understand when you read the article again, or you need me to start talking, you can comment, and I can take a look and add.
Next, I'll come to the most critical method in the spring startup process, refresh(). I've studied this method for a long time. There are many details and knowledge points in it. It's the one that has encountered problems in work and interview. Therefore, I'm going to talk about this refresh in several chapters.

Topics: Java source code