In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/03 Report--
Preface
It was originally thought that the process of generating and registering Spring bean defintions by parsing the configuration of bean is not too complicated and simple, and there is no need to open a separate blog post to describe it. However, when analyzing the injection mechanism of @ Autowired, @ Component, @ Service annotations in the previous two chapters, it is found that if we do not fully understand the parsing and registration mechanism of bean defintions, it is difficult to understand the underlying operating mechanism of annotation in the Spring container. So, the author of this post will try to figure out how the Spring container parses the bean configuration and generates and registers the bean definitions.
Remarks, this article is the author's original work, please indicate the source when reproduced.
What is bean definition?
What is ➥ bean definitions?
In fact, it is very simple, it is the POJO in Java, which is used to describe the element element in the bean configuration. For example, we have a simple configuration like the following
Beans.xml
As you can see, there are three element on it
, root element, component-scan element, bean element
During the process that the configuration file beans.xml is parsed by Spring, each element will be parsed as a bean definition object cached in the Spring container
The configuration objects that ➥ needs to be described as bean definitions are mainly divided into the following categories
Xml-based, parsing xml beans; injecting beans parsing using @ Autowired and @ Required annotations
Parsing cases in which elements that require special handling and parsing are injected into beans using @ Component, @ Service, @ Repository,@Beans annotations
Elements that require special handling and parsing
➥ I did know bean definitions this way at first, but when I analyzed the relevant logic and source code about bean definitions, I sublimated my understanding of it. The reference is written at the end.
Source code analysis
The best way to analyze the source code is to get its blueprint (top-level design) through the flow chart, and then break it one by one according to the points on the blueprint; finally, you can achieve the level of comprehension and confidence; so, in this way, the author gives you an in-depth analysis of the core points in the Spring container and how the relevant main process works.
Test case
In order to clarify some of the situations described in the above source code analysis at once, we continue to use the test cases used in Spring Core Container source code analysis 6: @ Service; the only modification is to use a special element xmlns:p to configure john, so that we can further debug how the custom Spring configuration tag is implemented
Beans.xml
Process analysis
The whole process starts with parsing the bean definitions process, and the corresponding entry is the step 1.1.1.2 obtainFreshBeanFactory of the main process.
Entry process
First initialize the BeanFactory instance DefaultListableBeanFactory, which is used to register the bean definitions generated after parsing the configuration.
Then parse the Spring XML configuration file through XmlBeanDefinitionReader
Parse and get the Resource [] object according to the XML file path location specified by the user. For more information, please refer to the step 1.1.3.3.1.1 getResource (location) step. Here, we further analyze how to get the Resource [] object through location, and see the source code.
PathMatchingResourcePatternResolver.java
Public Resource [] getResources (String locationPattern) throws IOException {Assert.notNull (locationPattern, "Location pattern must not be null"); if (locationPattern.startsWith (CLASSPATH_ALL_URL_PREFIX)) {/ / a class path resource (multiple resources for same name possible) if (getPathMatcher (). IsPattern (locationPattern.substring (CLASSPATH_ALL_URL_PREFIX.length () {/ / a class path resource pattern return findPathMatchingResources (locationPattern) } else {/ / all class path resources with the given name return findAllClassPathResources (locationPattern.substring (CLASSPATH_ALL_URL_PREFIX.length ();}} else {/ / Only look for a pattern after a prefix here / / (to not get fooled by a pattern symbol in a strange prefix). Int prefixEnd = locationPattern.indexOf (":") + 1; if (getPathMatcher (). IsPattern (locationPattern.substring (prefixEnd) {/ / a file pattern return findPathMatchingResources (locationPattern);} else {/ / a single resource with the given name return new Resource [] {getResourceLoader (). GetResource (locationPattern)};}
The parsing process here is mainly divided into two cases, one is the case where the prefix is classpath:, and the other is the ordinary case. As in the case of the test case we are currently using, it is the case of new ClassPathXmlApplicationContext ("beans.xml"), and we do not intend to dig any further here.
Taking the test case beans.xml as an example, the Resource instance resource parsed from # 2 corresponds to the configuration information of beans.xml. Starting from step 1.1.3.3.1.2 loadBeanDefinitions, the elements in both resource and beans.xml will be parsed in turn. First, generate the org.w3c.Document object instance document corresponding to beans.xml, see step 1.1.3.3.1.2.2.1, then get the BeanDefinitionDocumentReader instance documentReader that parses the document object, encapsulate the current Resource object as the XmlReaderContext instance xmlReaderContext, and finally formally parse the document object through documentReader to get bean definitions and register it in the current beanFactory instance. This step is shown in step 1.1.3.3.1.2.2.2.3
After completing the above three steps, you will enter the register bean definitions process process
Register bean definitions process
➥ first of all, the two important things are
The Root instance root is obtained from the document object, see step 1.2
Look at a root element, what does it look like
Is the topmost element in an xml configuration file
Then initialize the parsed object of the documentReader instance, namely this.delegate, which will be used in subsequent parsing of the element element
After ➥, when the previous work is ready, let's take a look at how to parse element.
First of all, determine whether the namespace of the root element corresponds to default namespace. If not, we will enter step 1.3.3.3: parse custom element;. Here we focus on the regular process, that is, when the namespace of the root element is the process of default namespace.
Iterate through all element under the root element
If the namespace of element is default namespace, it will enter the parse default element process.
For example, if the current element is ordinary, if the namespace of element is not default namespace, it will enter the parse custom element process.
For example, the current element is or parse default element process.
As you can see, this process consists of four sub-processes that deal with different element elements in turn, and the other three are special cases. Here, we mainly focus on the process of "parsing" elements.
Parsing bean element process
Here, in order to show the logic in the process of parsing elements as much as possible, I will use a special process to comb through this part.
This element uses namespace xmlns:p= "http://www.springframework.org/schema/p""
➥ first parses the element through the BeanDefintionParserDelegate object to get a bdHolder instance of the BeanDefinitionHolder object; in the parsing process, the bean id, bean name, and related scope, init, autowired model, and other attributes are parsed in turn; see step 1.1
➥ secondly, carry on the related modification operation to the bean definition, see step 1.2
General steps
Traverse all the attributes in the current element, get the namespace URI corresponding to the node by atttribute node, and determine whether the namespace is custom namespace. If it is custom namespace, then formally enter the modification process of the attribute node, as described below.
Modification process of attribute node
Suppose that our current attribute node is "jane", and see how this attribute is parsed
First, get the corresponding NamespaceHandler instance handler through node namespace
The NamespaceHandler obtained through xmlns:p= "http://www.springframework.org/schema/p" is a SimplePropertyNamespaceHandler object.
Second, call the SimplePropertyNamespaceHandler object to parse the current element
As you can see, there is nothing special about the previous parsing, which is parsed to get propery name: spouse-ref,property value: jane from the element "jane". However, the subsequent parsing is quite special, and we need to deal with the case of REF_SUFFIX, that is, when the suffix of property name is-ref, it means that the attribute is a ref-bean attribute, and its attribute value refers to other bean instances. So, its property value is encapsulated as a RuntimeBeanReference object instance, indicating that when parsing the property value as Java Object in the future, the bean instance jane referenced by it needs to be initialized. Then inject it into the current property value
Finally, the parsed bean definition is encapsulated in a bean definition holder object for return
➥ finally, register bean definition
See step 1.3.2 register.registerBeanDefinition (beanName, beanDefinition). Register is the current bean factory instance. By registering bean name and bean definition in the current bean factory as key-value pairs, we can get the corresponding bean definition object through the name of bean.
➥ is written at the end of the section
We can also customize an element or element attribute and define the namespace and namespace handler associated with it, so that the Spring container can resolve custom elements, similar to the custom elements used in dubbo configuration
Parse custom element process
This step corresponds to step 1.3.3.2 in the register bean definitions process step
In this section I will try to use a common custom element: to comb through the entire process
First, we get the namespace uri: http://www.springframework.org/schema/context related to the element. See step 1.1 to get the corresponding NamespaceHandler through the namespace uri parsing obtained from # 1. Here we get the ContextNamespaceHandler; see step 1.2.
All built-in key-value pairs corresponding to namespace uri and namespace handler are returned from step 1.2.1 getHandlerMappings (); use the NamespaceHandler ContextNamespaceHandler returned from # 2 for parse operation, see step 1.3, refer to sub-process parse element by ContextNamespaceHandler. Note that a separate sub-process is used here because using ContextNamespaceHandler to parse is only one of the parsing cases, and more sub-process cases will be considered in the future.
Continue the examples used in the parse custom element process chapter to analyze the process
Before ➥ starts the analysis, see what the component-scan element looks like
Note that component-scan element itself contains annotation-config attribute
➥ process analysis
First of all, find the corresponding BeanDefinitionParser according to element name: component-scan, and initialize 8 pairs of built-in element name and parsers key-value pairs when ContextNamespaceHandler initializes; here, find the corresponding parser ComponentScanBeanDefinitionParser object according to the name component-scan
Second, start the parsing work using the ComponentScanBeanDefinitionParser object
First, the basePcakges String [] object is parsed.
Second, initialize the ClassPathBeanDefinitiionScanner object instance scanner, and then call the scanner.doScan method to enter the [do scan process] (# do-scan process), which will traverse all the .class files contained in the base package, parse them, and generate the corresponding bean definitions;. In addition, in this process, note that the bean definitions will finally be registered in the current bean factory object.
Finally, this step starts with step 1.2.4, and the main logic is that when element contains the annotation-config attribute, a series of post-processors-bean-definitions;do scan processes will be registered
This article mainly introduces the do scan process steps mentioned in step # 2 in the previous section, corresponding to the step 1.2.3 scanner.doScan in the parse element by ContextNamespaceHandler flowchart.
➥, let's take a look at step 1.2.3.1 findCandidateComponent (basePackage)
ClassPathScanningCandidateComponentProvider.java (a large number of incoherent codes have been removed)
Public Set findCandidateComponents (String basePackage) {Set candidates = new LinkedHashSet (); try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage (basePackage) +'/'+ this.resourcePattern; / / 1. From the current user-defined classpath subpath, you should pay special attention to the fact that you can query all the matching resources; through regex. / / Why don't you directly use Class Loader to get the classes to judge? Because this is equivalent to loading Class Type, and the loading process of Class Type is strictly controlled through the Spring container, it is not allowed to load / / casually, so instead, use a File Resource to read the relevant bytecode and parse it from the bytecode. Resource [] resources = this.resourcePatternResolver.getResources (packageSearchPath); boolean traceEnabled = logger.isTraceEnabled (); boolean debugEnabled = logger.isDebugEnabled (); / / 2. Iterate through the user-defined bean Class object for (Resource resource: resources) {if (traceEnabled) {logger.trace ("Scanning" + resource);} if (resource.isReadable ()) {try {/ / saves the relevant annotation (@ Service) obtained from the bytecode and the FileSystemResource object in metadataReader MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader (resource); if (isCandidateComponent (metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition (metadataReader); sbd.setResource (resource); sbd.setSource (resource); if (isCandidateComponent (sbd)) {candidates.add (sbd) }...}} catch (IOException ex) {throw new BeanDefinitionStoreException ("I ex O failure during classpath scanning", ex);} return candidates;}
Line 10 of the code
Resource [] resources = this.resourcePatternResolver.getResources (packageSearchPath)
This step is done by recursively searching all .class files in the base package directory and encapsulating their bytecodes into Resource [] objects; the comments above are explained very clearly, which encapsulates the bytecode of .class files instead of class type In addition to what is described in the notes, another reason why the Class Type is not directly loaded here is that when Spring loads Class Type, it is very likely that AOP is configured on the Class Type, and the original bytecode is modified by ASM bytecode technology, and then added to the Class Loader; therefore, things like this can not be parsed directly, but only through bytecode.
This step also warns us that when using the Spring container to develop applications, developers should not casually load Class Type into the container, because it is possible to modify the bytecode through the ASM AOP of the Spring container before loading Class Type.
Line 23 of the code
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader (resource)
Parse the current .class bytecode, parse the corresponding annotation, such as @ Service, and save it to the metadataReader object together with the FileSystemResource object
Line 24 of the code
Protected boolean isCandidateComponent (MetadataReader metadataReader) throws IOException {for (TypeFilter tf: this.excludeFilters) {if (tf.match (metadataReader, this.metadataReaderFactory)) {return false;}} for (TypeFilter tf: this.includeFilters) {/ / includedFilters contains three types of annotation,1. @ Component 2. @ ManagedBean 3. @ Named if (tf.match (metadataReader, this.metadataReaderFactory)) {return isConditionMatch (metadataReader);}} return false;}
Since it is one of the three annotations to determine the existence of 1. @ Component 2. @ ManagedBean 3. @ Named from the current metadataReader, if so, enter the following process
Line 25-29, encapsulate the annotation that meets the # 3 standard as ScannedGenericBeanDefinition annotation-bean-definition, and join candidates to return if (isCandidateComponent (metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition (metadataReader); sbd.setResource (resource); sbd.setSource (resource); if (isCandidateComponent (sbd)) {candidates.add (sbd);}.}
➥ processes and registers the returned candidates in turn
This step starts with step 1.2.3.2 in the flowchart parse element by ContextNamespaceHandler, and mainly does the following
Set the scope of candiate (i.e. annotation bean definition) to generate bean name through AnnotationBeanNameGenerator, because the bean injected through @ Component and @ Service annotations often does not configure bean name, so you often need to generate the corresponding bean name by yourself through the program. Look at the internal source code and how to generate bean name. / * because @ Component, @ Serivce and other annotations do not provide a name tag like the xml-based configuration You can specify bean name So, here you need to generate one for it separately; * / @ Overridepublic String generateBeanName (BeanDefinition definition, BeanDefinitionRegistry registry) {if (definition instanceof AnnotatedBeanDefinition) {String beanName = determineBeanNameFromAnnotation ((AnnotatedBeanDefinition) definition); / / deal with cases such as @ Service ("dogService") if (StringUtils.hasText (beanName)) {/ / Explicit bean name found. Return beanName;}} / / Fallback: generate a unique default bean name. The implementation logic is to program the first letter of Class Name in uppercase and lowercase, and then return; return buildDefaultBeanName (definition, registry);}
Normally, the first letter of the class name is lowercase and returned; it corresponds to step 1.2.2.3.3
Set the default value of annotation bean definition, refer to step 1.2.4 to set scoped proxy to the current annotation bean definition, and register annotation bean definition with the current bean factory registration post-processor-bean-definitions
This step starts with the step 1.2.4.2 registerAnnotationConfigProcessors of the flowchart parse element by ContextNamespaceHandler, and the post-processor-bean-definitions corresponding to the following post-processor class object will be registered in turn
The classEventListenerMethodProcessor.classDefaultEventListenerFactory.class obtained by ConfigurationClassPostProcessor.classAutowiredAnnotationBeanPostProcessor.classRequiredAnnotationBeanPostProcessor.classCommonAnnotationBeanPostProcessor.class transmitted by PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME
Note that all of these are registered through Class objects, not instantiated objects. Let's briefly analyze the source code related to registration, taking registration AutowiredAnnotationBeanPostProcessor post-processor-bean-definition as an example.
AnnotationConfigUtils#registerAnnotationConfigProcessors
If (! registry.containsBeanDefinition (AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {/ / encapsulate AutowiredAnnotationBeanPostProcessor.class as bean definition RootBeanDefinition def = new RootBeanDefinition (AutowiredAnnotationBeanPostProcessor.class); def.setSource (source); beanDefs.add (registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME);}
The above steps encapsulate AutowiredAnnotationBeanPostProcessor.class as bean definition
AnnotationConfigUtils.registerPostProcessor
Private static BeanDefinitionHolder registerPostProcessor (BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {definition.setRole (BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition (beanName, definition); / / register bean definition return new BeanDefinitionHolder (definition, beanName);}
This step injects the bean definition corresponding to AutowiredAnnotationBeanPostProcessor into the current bean factory
AutowiredAnnotationBeanPostProcessor provides the implementation of @ Autowired annotation injection mechanism. For more information, please see the AutowiredAnnotationBeanPostProcessor section.
Write at the end
Through the above analysis, we can clearly see what the function of bean definition is, that is, to define the business rules of instance instantiated by Class Type through the description in bean definition. Let's take a look at the annotation-bean-definition objects generated by the do scan process.
{% asset_img debug-scanned-generic-bean-definition.png%}
As you can see, when we later get an instance of DogService based on the annotation-bean-definition, the business rules to follow are as follows
Generic bean: class [org.shangyang.spring.container.DogService]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [/ Users/mac/workspace/spring/framework/sourcecode-analysis/spring-core-container/spring-sourcecode-test/target/classes/org/shangyang/spring/container/DogService.class]
Note, however, that the ScannedGenericBeanDefinition instance obtained here does not actually load org.shangyang.spring.container.DogService Class Type into the container, but only assigns the class name string to ScannedGenericBeanDefinition.beanClass. The implication is that when loading Class Type into the container in the future, like instantiating instance, we may have to limit its loading behavior according to the rules in bean definitions. At present, all I can think of is ASM bytecode technology. You can define ASM bytecode modification rules in bean definition to control the loading behavior of related Class Type
References
This article is reproduced from my private blog, heartbreaking blog http://www.shangyang.me/2017/04/07/spring-core-container-sourcecode-analysis-register-bean-definitions/
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.