In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces springboot how to automatically scan and add BeanDefinition source code, has a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let Xiaobian take you to understand.
1.
During the springboot startup process, the definition of the bean that needs to be loaded is collected and added to the BeanFactory as a BeanDefinition object.
Because there are only methods such as getBean to get bean objects in BeanFactory, adding BeanDefinition to BeanFactory is done through the void registerBeanDefinition (String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException; method of the BeanDefinitionRegistry interface.
So if our BeanFactory implementation class needs to have the ability to return bean objects and add and delete BeanDefinition through beanName, at least implement BeanFactory and BeanDefinitionRegistry interfaces.
Here let's take a look at how springboot looks up the definition of bean and adds it to BeanFactory.
Since we are only concerned with finding the definition of the bean object here, the BeanFactory we mentioned here will mainly focus on the interface BeanDefinitionRegistry.
We mainly analyze the configuration of springboot scan loading bean locally, which has little to do with our code, so let's use the simplest one. The specific code is as follows:
Package com.example.bootargs;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class BootargsApplication {public static void main (String [] args) {SpringApplication.run (BootargsApplication.class, args);}}
The unified main class mentioned later is com.example.bootargs.BootargsApplication.
two。
The definition of Springboot finding bean is mainly done through the class ConfigurationClassPostProcessor.
ConfigurationClassPostProcessor implements the BeanDefinitionRegistryPostProcessor interface. The BeanDefinitionRegistryPostProcessor interface adds the definition of bean to the implementation class of BeanDefinitionRegistry through the postProcessBeanDefinitionRegistry method.
BeanDefinitionRegistryPostProcessor inherits the BeanFactoryPostProcessor interface, while the BeanFactoryPostProcessor interface is mainly used to enhance BeanFactory. During the startup of springboot, BeanFactory is first created, and then BeanFactoryPostProcessor is called to BeanFactory.
Enhancements are made before the bean object is created.
BeanFactory is enhanced through BeanFactoryPostProcessor, mainly through the static method of PostProcessorRegistrationDelegate. The class ConfigurationClassPostProcessor is called in the process.
Because ConfigurationClassPostProcessor implements the BeanDefinitionRegistryPostProcessor interface, PostProcessorRegistrationDelegate calls the postProcessBeanDefinitionRegistry method of ConfigurationClassPostProcessor, and the processConfigBeanDefinitions method is called to find the definition of bean. Let's look at it from here as an entrance.
3.
Let's take a look at ConfigurationClassPostProcessor's processConfigBeanDefinitions method.
/ * * Build and validate a configuration model based on the registry of * {@ link Configuration} classes. * / public void processConfigBeanDefinitions (BeanDefinitionRegistry registry) {List configCandidates = new ArrayList (); String [] candidateNames = registry.getBeanDefinitionNames (); / / in the following for loop, you will look for Configuration annotated configuration classes from the definition of bean already in beanFactory. / / by default, all you get here is a main class for (String beanName: candidateNames) {. ConfigCandidates.add (new BeanDefinitionHolder (beanDef, beanName));}} / / if no configuration class is found, return / / Return immediately if no @ Configuration classes were found if (configCandidates.isEmpty ()) {return directly }. / / parse the configuration class / / Parse each @ Configuration class ConfigurationClassParser parser = new ConfigurationClassParser (this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry) through ConfigurationClassParser here; Set candidates = new LinkedHashSet (configCandidates) Set alreadyParsed = new HashSet (configCandidates.size ()); do {StartupStep processConfig = this.applicationStartup.start ("spring.context.config-classes.parse"); / / all lookups of bean definitions are done here. Let's take a look at the parse method parser.parse (candidates);. }
In the parse method in ConfigurationClassParser, since our configuration class is defined by annotations, we go to the branch of AnnotatedBeanDefinition. Continue to call processConfigurationClass (new ConfigurationClass (metadata, beanName), DEFAULT_EXCLUSION_FILTER); let's go directly to the processConfigurationClass method to see this sentence.
Protected void processConfigurationClass (ConfigurationClass configClass, Predicate filter) throws IOException {/ / first check whether there are Conditional annotations on the configuration class, and if so, parse it to see if you want to skip the annotation class if (this.conditionEvaluator.shouldSkip (configClass.getMetadata (), ConfigurationPhase.PARSE_CONFIGURATION)) {return. } / / all parsed configuration classes are placed in configurationClasses. Key is the currently parsed configuration class, and value indicates through whom the configuration class is imported. / / if the configuration class is not imported through another class, then key and value are the same. ConfigurationClass existingClass = this.configurationClasses.get (configClass) / / if the same configuration class is imported through multiple configuration classes So the import relationship between this and the configuration class needs to merge if (existingClass! = null) {if (configClass.isImported ()) {if (existingClass.isImported ()) {existingClass.mergeImportedBy (configClass) } / / Otherwise ignore new imported config class; existing non-imported class overrides it. Return;} else {/ / Explicit bean definition found, probably replacing an import. / / Let's remove the old one and go with the new one. This.configurationClasses.remove (configClass); this.knownSuperclasses.values (). RemoveIf (configClass::equals);}} / / Recursively process the configuration class and its superclass hierarchy. / / here is to convert the configuration class into a SourceClass object SourceClass sourceClass = asSourceClass (configClass, filter); do {/ / will parse the real configuration class here. / / Note that this is a do-while loop. After dealing with the current configuration class, it will continue to deal with the parent class of the current configuration class. / / if the parent class name of the current class does not start with java and has not been processed, it will continue to process sourceClass = doProcessConfigurationClass (configClass, sourceClass, filter);} while (sourceClass! = null); this.configurationClasses.put (configClass, configClass);} in this do-while loop
The filtering of this.conditionEvaluator.shouldSkip (configClass.getMetadata (), ConfigurationPhase.PARSE_CONFIGURATION) is mainly done by implementing the matches method through a subclass of the org.springframework.context.annotation.Condition interface.
For example, let's briefly say:
@ Configuration (proxyBeanMethods = false) @ ConditionalOnMissingBean (name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT) @ AutoConfigureOrder (Ordered.HIGHEST_PRECEDENCE) @ Conditional (ResourceBundleCondition.class) @ EnableConfigurationPropertiespublic class MessageSourceAutoConfiguration
Above is the definition of the MessageSourceAutoConfiguration class. You will first look for the Conditional annotations on it, and you will find two annotations:
@ ConditionalOnMissingBean (name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
Since this annotation has @ Conditional (OnBeanCondition.class) on it, it will be handed over to the class OnBeanCondition to handle.
@ Conditional (ResourceBundleCondition.class), it is left to the class ResourceBundleCondition to handle.
The processConfigurationClass method can be found in several places, mainly in three places:
This processConfigurationClass method is called when the parse method is called.
The processConfigurationClass method may also be called multiple times when parsing the properties of the current configuration class in doProcessConfigurationClass.
The processConfigurationClass method may also be called when this.deferredImportSelectorHandler.process () is called
All the configuration classes we parse here are added to the configurationClasses.put (configClass, configClass) method, so we end up with multiple classes added to the configurationClasses collection, at least how many times we call the processConfigurationClass method (as determined by the Conditional annotation, so the number of calls may be more than the number of elements eventually added to the configurationClasses collection)
@ Nullable protected final SourceClass doProcessConfigurationClass (ConfigurationClass configClass, SourceClass sourceClass, Predicate filter) throws IOException {/ / here, check to see if the class has Component comments, and if so, find the inner class of the current class Processing if (configClass.getMetadata (). IsAnnotated (Component.class.getName () {/ / Recursively process any member (nested) classes first / / it is possible to recursively call the above processConfigurationClass method processMemberClasses (configClass, sourceClass, filter) } / / Process any @ PropertySource annotations / / here, check to see if the class has PropertySources annotations, and if so, parse the property configuration Add to the environment context to for (AnnotationAttributes propertySource: AnnotationConfigUtils.attributesForRepeatable (sourceClass.getMetadata (), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource (propertySource) } else {logger.info ("Ignoring @ PropertySource annotation on [" + sourceClass.getMetadata () .getClassName () + "]. Reason: Environment must implement ConfigurableEnvironment ") }} / / Process any @ ComponentScan annotations / / here, check whether the class has ComponentScans annotations. If so, scan the directory according to the conditions here to find the definition of bean / / since we have SpringBootApplication annotations on our current classes, we can find ComponentScan annotations here. Will go into this method Set componentScans = AnnotationConfigUtils.attributesForRepeatable (sourceClass.getMetadata (), ComponentScans.class, ComponentScan.class) If (! componentScans.isEmpty () & &! this.conditionEvaluator.shouldSkip (sourceClass.getMetadata (), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan: componentScans) {/ / The config class is annotated with @ ComponentScan-> perform the scan immediately / / here It will deal with the content related to the ComponentScans comments. / / there is a basePackages attribute on the ComponentScans annotation that specifies the name of the package being scanned. / / if the basePackages attribute is not specified, look for the definition of the relevant bean under the package of the current class and all its subpackages. / / We generally do not specify the basePackages attribute, so we will look for the definition of bean under the package of the current sourceClass class and all its subpackages. / / the controller,service,dao defined in our own code, etc., all get the definition of bean at this step. Set scannedBeanDefinitions = this.componentScanParser.parse (componentScan, sourceClass.getMetadata (). GetClassName ()) / / Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder: scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition () .getOriginatingBeanDefinition () If (bdCand = = null) {bdCand = holder.getBeanDefinition () } if (ConfigurationClassUtils.checkConfigurationClassCandidate (bdCand, this.metadataReaderFactory)) {/ / here also calls the processConfigurationClass method parse (bdCand.getBeanClassName (), holder.getBeanName ()) indirectly. } / / Process any @ Import annotations / / here, the import annotations on the class are processed. / / getImports (sourceClass) will first get the class of import. / / there will be two, one is the AutoConfigurationPackages.Registrar.class annotated on AutoConfigurationPackage / the other is the annotated AutoConfigurationImportSelector.class on EnableAutoConfiguration) / / Let's take a look at the processImports method / / it may also call the processConfigurationClass method processImports (configClass, sourceClass, getImports (sourceClass), filter, true). }
DoProcessConfigurationClass is really used to deal with configuration classes.
In this method, the inner class, PropertySources annotations, ComponentScans annotations, Import annotations, ImportResource annotations, Bean annotations, the default method on the interface, and continue recursion to its parent class are processed.
Where:
The inner class will continue to call the processConfigurationClass method recursively to handle the
PropertySources comments are parsed and added to the environment context
The classes scanned by ComponentScans annotations will be added directly to beanFactory and will continue to call the processConfigurationClass method to recursively process.
Import comments are handled in three cases:
If the class of Import implements ImportSelector. If its subinterface DeferredImportSelector is implemented, it will be added to the deferredImportSelectors and processed later. If no subinterface is implemented, processImports is recursively called for processing.
If the class of Import implements ImportBeanDefinitionRegistrar. Is added to the properties of the current configuration class for subsequent processing.
If this is not the case, continue to recursively call processConfigurationClass for processing.
ImportResource annotations, Bean annotations, and default methods on interfaces are all parsed and added to the properties of the current configuration class for later processing.
Briefly describe several input parameters of the following methods:
Both parameters configClass,currentSourceClass directly refer to our main class that contains SpringBootApplication annotations.
Where configClass indicates who imported the class currently being processed, and currentSourceClass represents the class currently being processed. The two are generally the same resource class at the bottom, but there may be recursive calls, so the two may be different.
ImportCandidates is the class imported through import annotations. Here are AutoConfigurationPackages.Registrar.class and AutoConfigurationImportSelector.class importCandidates, which are the classes currently imported, that is, the classes processed here.
ExclusionFilter is defined in ConfigurationClassParser and is used to filter java.lang.annotation. And org.springframework.stereotype. Initial comments
CheckForCircularImports indicates whether to check for recursive imports
Private void processImports (ConfigurationClass configClass, SourceClass currentSourceClass, Collection importCandidates, Predicate exclusionFilter, boolean checkForCircularImports) {if (importCandidates.isEmpty ()) {return } / / here is error checking to see if recursive if (checkForCircularImports & & isChainedImportOnStack (configClass)) {this.problemReporter.error (new CircularImportProblem (configClass, this.importStack)) occurs. } else {/ / push the current configuration class onto the stack this.importStack.push (configClass) first Try {/ / here The class imported by the import tag will be processed. The for (SourceClass candidate: importCandidates) {/ / AutoConfigurationImportSelector.class class will go to the following branch if (candidate.isAssignable (ImportSelector.class)) { / / Candidate class is an ImportSelector-> delegate to it to determine imports Class candidateClass = candidate.loadClass () / / first create an object of class AutoConfigurationImportSelector here, ImportSelector selector = ParserStrategyUtils.instantiateClass (candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry) Predicate selectorFilter = selector.getExclusionFilter (); if (selectorFilter! = null) {exclusionFilter = exclusionFilter.or (selectorFilter) } if (selector instanceof DeferredImportSelector) {/ / here Encapsulate the current configuration class and AutoConfigurationImportSelector objects as DeferredImportSelectorHolder objects / / add to the delayed import collection deferredImportSelectors this.deferredImportSelectorHandler.handle (configClass, (DeferredImportSelector) selector) } else {String [] importClassNames = selector.selectImports (currentSourceClass.getMetadata ()) Collection importSourceClasses = asSourceClasses (importClassNames, exclusionFilter); processImports (configClass, currentSourceClass, importSourceClasses, exclusionFilter, false) }} / / the class AutoConfigurationPackages.Registrar.class will go to this branch / / in this branch First, create an object of AutoConfigurationPackages.Registrar and add it to the importBeanDefinitionRegistrars attribute of the current configuration class to else if (candidate.isAssignable (ImportBeanDefinitionRegistrar.class)) {/ / Candidate class is an ImportBeanDefinitionRegistrar-> / / delegate to it to register additional bean definitions Class candidateClass = candidate.loadClass () ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass (candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry) ConfigClass.addImportBeanDefinitionRegistrar (registrar, currentSourceClass.getMetadata ()) } else {/ / Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar-> / / process it as an @ Configuration class This.importStack.registerImport (currentSourceClass.getMetadata () Candidate.getMetadata () .getClassName () ProcessConfigurationClass (candidate.asConfigClass (configClass), exclusionFilter);}} catch (BeanDefinitionStoreException ex) {throw ex } catch (Throwable ex) {throw new BeanDefinitionStoreException ("Failed to process import candidates for configuration class [" + configClass.getMetadata () .getClassName () + "]", ex) } finally {this.importStack.pop ();}
The above import import class is finished, so let's go back to doProcessConfigurationClass and look at the rest.
@ Nullable protected final SourceClass doProcessConfigurationClass (ConfigurationClass configClass, SourceClass sourceClass, Predicate filter) throws IOException {. / / this part has been analyzed earlier. Let's move on to the following / / Process any @ ImportResource annotations / / here is dealing with the ImportResource annotation AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor (sourceClass.getMetadata (), ImportResource.class) If (importResource! = null) {String [] resources = importResource.getStringArray ("locations"); Class
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.