Writing articles is not easy, please indicate the source of reprinting.
At the same time, if you like my article, please pay attention to me, let's make progress together.
I. overview
In the previous blog posts, we have made a general analysis of the source code of the @Configuration annotation in Spring, but in fact, we have covered Spring's IOC process in the process of analyzing the @Configuration source code. We have roughly analyzed the registration process of Spring container for Bean, and have a general understanding of BeanD. Definition, Bean Definition Map, and Bean Definition Names. However, annotation classes with @Configuration are actually handed over to Spring containers for registration, and in the real development process, we use more automatic scanning to let containers automatically load beans.
There are two common ways to open Spring automatic scanning, one is through xml files, and the other is through Java configuration class (annotated @Configuration annotation) which we talked about earlier (annotated @ComponentScan annotation for configuration class). After opening the automatic scanning bean, we just need to add the @Component annotation to the class that needs to be scanned, so this blog post mainly analyses how Spring accomplishes the automatic loading of beans through annotation scanning, and explores the Bean's declaration cycle from the source code.
Source code analysis
2.1 overview
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); register(annotatedClasses); refresh(); } // The GenericApplicationContext class is the parent of AnnotationConfigApplicationContext public GenericApplicationContext() { this.beanFactory = new DefaultListableBeanFactory(); }
As in previous blog posts, we also use Annotation Config Application Context to start Spring container. After following up on the construction method, it calls its own parametric constructor, instantiates some scanners in this parametric constructor and creates Spring in the process of calling the parent constructor. The most important BeanFactory (Bean Factory) then calls the register method and refresh method, completes the analysis and registration of annotated Class we passed in the construction method, which was analyzed in the previous blog post, and completes the analysis and registration of other B in the refresh method. Scan, parse and instantiate of ean, so we focus on refresh method here.
2.2 refresh method
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Eliminate part of the code. try { // Eliminate part of the code. // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Eliminate part of the code. // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Eliminate part of the code. } catch (BeansException ex) { // Eliminate part of the code. } finally { // Eliminate part of the code. } } }
Because this refresh method is Spring's comparative subject method, many components are processed in this method, because for the time being, we will first analyze the process of scanning, parsing and creating beans, so we only need to focus on invokeBeanFactoryPostProcessors and FinishBeanFactoryInitiali. The two methods are all right. The main function of the first method is to call all BeanFactoryPostProcessor methods, where the configuration class (with @Configuration and passed in as a parameter to the AnnotationConfigApplicationContext constructor) information is parsed (the @ComponentScan annotation is parsed). Solve, turn on automatic scanning, and then convert the beans we created ourselves into Bean Definition and store them in Bean Definition Map. The second method is to instantiate all non-lazy loaded beans.
2.2.1 invokeBeanFactoryPostProcessors
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Eliminate irrelevant code. } public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { // Eliminate irrelevant code. // Call BeanDefinition Registry Post Processor invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); // Eliminate irrelevant code. } private static void invokeBeanDefinitionRegistryPostProcessors(Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanDefinitionRegistry(registry); } } public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // Eliminate irrelevant code. processConfigBeanDefinitions(registry); }
Here we still focus on code related to Bean scanning, parsing and instantiation, so I omitted a lot of irrelevant code in this invokeBeanFactoryPostProcessors method, listing only the more important code call chains, which is the BeanDefinition Registry PostProcessor in the whole method. The method completes the scanning of beans, transforms them into BeanDefinition objects, and adds them to BeanDefinition Map. In this method, all BeanDefinition RegistryPostProcessors are removed through loops, their postProcessBeanDefinition Registry methods are called, and then proce is called again. SsConfigBeanDefinitions (belonging to the Configuration ClassPostProcessor class) method, so let's follow up on this method.
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // Obtaining the logical code with the @Configuration configuration class is omitted. // Resolve each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { // Here the parser is called to parse the configuration class parser.parse(candidates); parser.validate(); // Eliminate irrelevant code. } while (!candidates.isEmpty()); // Eliminate irrelevant code. }
This method was specifically analyzed in the last blog post that analyzed @ Configuration annotation, so I omitted a lot of code here. I also annotated the function of these codes. First of all, the above logic is to obtain all classes with @ Configuration annotation from BeanDefinitionMap, and then start to A Configuration class parses, and the specific logic of parsing is in the parse method of parse. Another interesting point here is that in Spring, BeanDefinition Registry is an interface specifically provided. Its main function is to provide external access to BeanDefinition Map in BeanFactory (for example, to get the specified BeanDefinition), so the entry here is BeanDefini. TionRegistry.
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); } // Process any @PropertySource annotations // Process any @ComponentScan annotations Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // If the configuration class has the @ComponentScan annotation, start the Bean scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check whether the scanned definition set has other configuration classes and parse them recursively as needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } // Determine whether the class just scanned is a configuration class and check its configuration information if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations // Process any @ImportResource annotations // Process individual @Bean methods // Process default methods on interfaces // Process superclass, if any // No superclass -> processing is complete return null; }
The parse method above is called layer by layer, and finally the doProcessConfiguration Class method is called. In this method, the @ComponentScan annotation is parsed in essence. In order to facilitate the analysis of code logic, I have omitted some other annotation parsing code directly. We only analyze it. @ ComponentScan's logic. When the configuration class has the @ComponentScan annotation, it immediately calls the Parse method of ComponentScan Annotation Parser for scanning.
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); // Eliminate attribute processing logic. // Get the scanned file path Set<String> basePackages = new LinkedHashSet<>(); String[] basePackagesArray = componentScan.getStringArray("basePackages"); for (String pkg : basePackagesArray) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); Collections.addAll(basePackages, tokenized); } for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(declaringClass)); } scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { @Override protected boolean matchClassName(String className) { return declaringClass.equals(className); } }); return scanner.doScan(StringUtils.toStringArray(basePackages)); }
After parsing the attributes in componentScan, the scanner's doScan method is called to scan the labeled classes (with @Component and other Bean annotations) on a given path, and the scanned classes are transformed into BeanDefinition Holder, which is then packaged into BeanDefinition Holder and returned to the collection.
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); // Traversing through all given paths for (String basePackage : basePackages) { // Scanning all classes with given annotations (@Component, etc.) on a given path and encapsulating them as BeanDefinition sets Set<BeanDefinition> candidates = findCandidateComponents(basePackage); // Bean Definition that traverses all beans scanned for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { // Calling Bean's postProcessBeanDefinition method postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { // Wrap BeanDefinition as BeanDefinition Holder BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // Registered in BeanFactory's BeanDefinition Map registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
In the doScan method, we have a substantial bean scanning process. Here, it will traverse every given file path, and then call the findcandidecomponents method to find the (@ Component, etc.) class with execution annotation under the path, convert it to beandefinition, and then return. After that, it will call the postprocessbean. The nDefinition method wraps the BeanDefinition as a BeanDefinition Holder and stores it in the collection into the BeanDefinition Map of BeanFactory, and returns the searched BeanDefinition Holder collection. In fact, the logic of Bean Scanning is almost complete, but we can go a little further to find Candidate Components and see how it scans.
There is no code here, because this part of the code is more complex, there are many validations and judgments, and the implementation of the findCandidate Components method is relatively simple, that is, by scanning to get all the classes under the specified package path, and then traverse whether the annotations they have include @Component, or not. @ Service, @Repository or @Controller annotations, if included, add this class directly to the candidate set.
2.2.2 BeanFactoryPostProcessor interface
@FunctionalInterface public interface BeanFactoryPostProcessor { /** * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for overriding or adding * properties even to eager-initializing beans. * @param beanFactory the bean factory used by the application context * @throws org.springframework.beans.BeansException in case of errors */ void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
Now that we have analyzed it, we can just explore the BeanFactoryPostProcessor interface that we often hear about, and the main function of this interface is to provide the outside as an extension point to modify the beans in the Bean Factory. From the source code of the interface above, we can see that the interface only provides a method is the postProcessBeanFactory method, which is often referred to as the BeanFactory, so when we get the BeanFactory, we can get the BeanDefinitionMap stored in it, and so on. Beans in BeanFactory can be modified.
However, it should be noted here that, as stated in its annotations, this interface method is called at a time when all BeanDefinition has been loaded into the BeanDefinition Map, but no Bean has been created through BeanDefinition at this time, so this also provides an external opportunity to follow BeanDefinition in Bean. Before the initial is created, the beans in the container are modified (when not created) by adding or modifying attributes in the BeanDefinition. So we can define a class, implement this interface, implement its postProcessBeanFactory method, and then do some operations on BeanDefinition in this method. But at this point, we may be curious about when the postProcessBeanFactory method was called by the container, but the answer is already in the code we just analyzed. Now let's go back to the invokeBeanFactoryPostProcessors method in the refresh method (in fact, just by this name, we can probably guess that the method is called here), and then follow up the PostProcessor Registration Delegate. invokeBeanFactoryPostProcessors method in it. Call.
public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { // Invoke BeanDefinitionRegistryPostProcessors first, if any. Set<String> processedBeans = new HashSet<>(); if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } else { regularPostProcessors.add(postProcessor); } } // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // Separate between BeanDefinitionRegistryPostProcessors that implement // PriorityOrdered, Ordered, and the rest. List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered. String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered. postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. boolean reiterate = true; while (reiterate) { reiterate = false; postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate = true; } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); } // Now, invoke the postProcessBeanFactory callback of all processors handled so far. invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); } else { // Invoke factory processors registered with the context instance. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); } // The following is a call to our own BeanFactoryPostProcessor implementation class for Spring. // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // Get the BeanFactoryPostProcessor implementation class we wrote String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); // Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // skip - already processed in first phase above } else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } } // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); // Next, invoke the BeanFactoryPostProcessors that implement Ordered. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); // Finally, invoke all other BeanFactoryPostProcessors. // Here we call the postProcessBeanFactory method of our BeanFactoryPostProcessor implementation class List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); // Clear cached merged bean definitions since the post-processors might have // modified the original metadata, e.g. replacing placeholders in values... beanFactory.clearMetadataCache(); }
Into this seemingly similar approach, we seem to have found something vaguely. The logical processing of @Configuration before we just started Bean Scanning seems to be the same way. The code for processing @Configuration is in the Configuration ClassPostProcessor class, which implements Bea. NDefinition RegistryPostProcessor interface, which implements the BeanFactoryPostProcessor interface. Okay, the truth is clear. Now we can see that Spring's processing of @Configuration annotations and automatic scanning of beans are essentially done through the BeanFactoryPostProcessor interface.
So when we comb the above code again, the idea is much clearer. Through my comments, we can divide it up. The first half of my comments is mainly used to call the postProcessBeanFactory method of the BeanFactoryPostProcessor implementation class, which is implemented by Spring itself, to implement the functions of some systems. In the second half of the comment, the user's own postProcessBeanFactory PostProcessor method is called to implement the class's postProcessBeanFactory method, and the specific execution methods are similar or different, basically through the type (BeanFactoryPostProcessor.class unified interface) to obtain BeanDefinition (note that because of ourselves) Written BeanFactoryPostProcessor implementation classes are also scanned to be added to the Spring container, and then, after some judgment, uniformly invoke their postProcessBeanFactory methods.
This excuse is roughly analyzed here. We know when and under what circumstances the postProcessBeanFactory method in the BeanFactoryPostProcessor class we implemented is invoked. Others will specialize in the role analysis of the BeanFactoryPostProcessor interface in Spring. Write a blog to describe.
2.2.3 finishBeanFactoryInitialization
/** * Finish the initialization of this context's bean factory, * initializing all remaining singleton beans. */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // Eliminate irrelevant code. // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }
We continue to analyze the general process of instantiation and initialization of beans. First, we follow up the finish Bean FactoryInitialization method. The work done by the previous code is all about initialization of BeanFactory. Therefore, we focus directly on the preInstantiate Singletons method of instantiated beans.
public void preInstantiateSingletons() throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); } // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... // Triggering non-lazy loading singleton Bean loading for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // Determine whether the class corresponding to the current Bean is an abstract class, whether the scope of the Bean is singleton, whether it is lazy to load if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // Determine whether a Bean is FactoryBean when its corresponding class is non-abstract and its scope of action is singleton and it is not lazy to load if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { // Ordinary Beans are not FactoryBean s, so only getBean methods are called after entering this block of statements. getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... // Life Cycle Callback Method for Triggering Bean s for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
This method has two main tasks, one is to instantiate beans, the other is to invoke the callback method of Bean life cycle. Because the content of this blog post is more, so we only analyze the instantiation process of beans for the time being, so we can see that the code logic after entering the method is basically as follows:
(1) Traversing BeanDefinition Map to get BeanDefinition;
(2) To determine whether the Bean Class corresponding to the current Bean Definition is an abstract class;
(3) Judging whether the scope of the Bean corresponding to the current Bean Definition is singular and whether it is lazy loading mode;
(4) If the current class satisfies the above criteria, then judge whether the current Bean is FactoryBean.
(5) If the current Bean is not a FactoryBean, call the getBean method directly;
// AbstractBeanFactory.java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. // Check if there are manual registered singleton beans in the singleton pool Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { // Eliminate log information. bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. // Determine whether the Bean corresponding to BeanName is currently being created if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Omission. } // Whether to retrieve instances for type checking if (!typeCheckOnly) { // Beans corresponding to the tag BeanName are being created markBeanAsCreated(beanName); } try { final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. // Let's start by checking whether the current Bean is dependent on the Bean. // If you have a dependency on Bean (@DependOn), you first call the getBean method instantiation String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { // Cyclic processing of each dependent Bean } } // Here we formally begin to instantiate Bean - ------------------------------------------------------------------------------------------------------------------------------------------- if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { // Call the createBean method to instantiate the Bean return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); bean = 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); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName = mbd.getScope(); final 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); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { // Omission. } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // Check if required type matches the type of the actual bean instance. if (requiredType != null && !requiredType.isInstance(bean)) { // Omission. } return (T) bean; }
The doGetBean method code is probably pasted here. The logic of the code is relatively clear:
(1) Firstly, the Bean singleton corresponding to BeanName is obtained from the singleton pool.
(2) If acquisition fails, first determine whether the Bean corresponding to the current BeanName is being created, and then throw an exception directly if it is being created. This method is also relatively simple. In the following steps, we will see that when Spring is about to start instantiating a bean, its BeanName will be placed first. Already Created is a Set set Set, so when we judge whether the current bean is being created later, we only need to judge whether the BeanName exists in the Set (the judgment here is mainly to solve the problem of cyclic dependency).
(3) Check whether there is a corresponding Bean Definition in the factory (in this case, the parent factory of the current factory is actually obtained, and the initialization time is usually null).
(4) If the Bean corresponding to the current BeanName is not being created, then judge whether to retrieve an instance for type checking, and add the BeanName directly to the alreadyCreated Set set we mentioned above if it is false.
(5) Obtain BeanDefinition according to BeanName and verify it.
(6) To determine whether the current Bean exists a dependent Bean (@DependOn), if it exists, call the getBean method to create its dependent Bean first;
(7) Beans are created differently here according to their scope of action, but ultimately they are called createBean methods (we will follow up and analyze this method below).
(8) Finally, check whether the required type matches the type of the actual Bean instance.
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { RootBeanDefinition mbdToUse = mbd; // Make sure that the Bean is actually parsed at this point // BeanDefinition is cloned in the case of Class that cannot be stored dynamically. Class<?> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } // Prepare method overrides. try { mbdToUse.prepareMethodOverrides(); } // Eliminate catch.. try { // Give BeanPostProcessor an opportunity to return the proxy instead of the target Bean instance Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } // Eliminate catch.. try { // Call the doCreateBean method to create a Bean Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; } // Eliminate catch.. }
This method first parses the Bean, obtains its BeanClass, and then calls the method in the BenPostProcessor implementation class to give the outside the opportunity to replace the Bean to be instantiated with the proxy Bean, and finally calls the doCreateBean method to start the creation.
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // 1. Instance Bean BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // Calling method to create BeanWrapper is also Bean instance instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { // Call BeanPostProcessor again to implement the method of the class applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // Caching singletons so that circular references can be resolved even when life cycle interfaces such as BeanFactoryAware trigger boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 2. Initialization Bean Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { // Abnormal information is omitted. } } } } // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
In this doCreateBean method, there are two parts. The first part is to instantiate the Bean, and the second part is to initialize the Bean. The instantiation here can be understood as creating the Bean instance, while the initialization can be understood as property injection, etc. In the first part, the core of instantiating beans is the following createBean Instance method, in which the instantiating work of beans is completed by calling factory method, constructor automatic assembly or simple instantiating method, while in the second part, a very complex and complex attribute injection is completed. The process is not analyzed here, but put in the next blog.
/** * Create a new instance for the specified bean, using an appropriate instantiation strategy: * factory method, constructor autowiring, or simple instantiation. * @param beanName the name of the bean * @param mbd the bean definition for the bean * @param args explicit arguments to use for constructor or factory method invocation * @return a BeanWrapper for the new instance * @see #obtainFromSupplier * @see #instantiateUsingFactoryMethod * @see #autowireConstructor * @see #instantiateBean */ protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. Class<?> beanClass = resolveBeanClass(mbd, beanName); if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { return autowireConstructor(beanName, mbd, null, null); } else { return instantiateBean(beanName, mbd); } } // Candidate constructors for autowiring? Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // Preferred constructors for default construction? ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } // No special handling: simply use no-arg constructor. return instantiateBean(beanName, mbd); }
Summary of Contents
In this blog post, the main part of the main code logic of scanning, registration and instantiation of beans (which should also be initialized) is sorted out, but the overall analysis is relatively simple, because the code volume and logic of these three parts are too complex, and there are many solutions in each part of the code. It's problematic code. So even if this blog article plays a guiding role, it will probably sort out the whole code call chain first, which is also a preparation for the detailed analysis of each part of the following.
Time is like water in a sponge. If you want to squeeze it, there will always be some.