[TOC]
This note mainly records the following contents:
Using the ClassPathXmlApplicationContext, through in the XML to register a bean, tracking code, to understand it from a configuration file < bean > tag, Detailed procedure for loading beanDefinitionMap into the BeanFactory registry.
The code shown has extracted some core methods, removed some default Settings and log output, and also removed most error exceptions. If you want to see the detailed code, annotations and demo, you can download the notes I uploaded at 📒
Code cloud Gitee address
Making the address
By reading the source code process, understand the designer’s design ideas and learning from it, have a basic understanding of Spring.
infrastructure
We’ll start by showing you how to register and use beans in code:
config.xml
<?xml version="1.0" encoding="UTF-8"? >
<beans>
<bean id="book" class="domain.SimpleBook"/>
</beans>
Copy the code
Define a simple class:
SimpleBook.java
public class SimpleBook {
private int id;
private String name = "Default Name";
}
Copy the code
Using the ClassPathXmlApplicationContext bean was obtained from the XML configuration file:
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
SimpleBook book = context.getBean(SimpleBook.class);
System.out.println(book.getName());
}
Copy the code
When the code runs properly, the console prints:
Default Name
Copy the code
Normally, we need to instantiate an object by new initialization, memory allocation, etc. But with the Spring container, we can turn SimpleBook over to Spring to manage without having to do new SimpleBook in our code. Object instances ~ can be easily obtained by automatic injection (such as the @Autowire annotation) or, as in the example, by obtaining context objects and then using the getBean() method.
ClassPathXmlApplicationContext
ClassPathXmlApplicationContext inheritance system structure diagram:
The Diagrams are displayed through the Diagrams function of the IDEA editor. Right-click the current class, you can see the inheritance system, which classes are inherited and which interfaces are referenced, which is convenient for us to understand ~
ClassPathXmlApplicationContext inherited from AbstractApplicationContext, And AbstractRefreshableApplicationContext is AbstractApplicationContext abstract subclass, the use of the class is DefaultListableBeanFactory registered factories, This registered factory is also very important and will be described later.
In a nutshell,DefaultListableBeanFactory
是 Spring
Registration and Loadingbean
By default, it will be registeredbean
In thebeanDefinitionMap
forkey-value
Form storage.
As you can see in the upper right corner of the image, ResourceLoader is its top-level interface, indicating that this class implements resource loading.
Constructor code:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
// Comment 1.1 Getting resource files
setConfigLocations(configLocations);
if(refresh) { refresh(); }}Copy the code
The constructor
As you can see from this line, the subclass constructor calls the parent class constructor:
super(parent)
Has been tracking code, found starting from the subclass, along the parent class has been called up, until the AbstractApplicationContext:
public AbstractApplicationContext(a) {
this.resourcePatternResolver = getResourcePatternResolver();
}
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this(a); setParent(parent); }Copy the code
protected ResourcePatternResolver getResourcePatternResolver(a) {
return new PathMatchingResourcePatternResolver(this);
}
Copy the code
Initialization functions are used to set the resource matching handler. The ResourcePatternResolver interface defines a strategy for parsing location patterns (for example, Ant style path patterns) into resource objects. Specific implementation class is PathMatchingResourcePatternResolver (path matching parser resource model, we used to parse the incoming path config. The XML)
Set the configuration file path
org.springframework.context.support.AbstractRefreshableConfigApplicationContext
public void setConfigLocations(@Nullable String... locations) {
if(locations ! =null) {
Assert.noNullElements(locations, "Config locations must not be null");
1.2 Put the configuration resource path into the configLocations array
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim(); }}else {
this.configLocations = null; }}Copy the code
A resolvePath resolves a given path and replaces placeholders with placeholder placeholders
For example, new ClassPathXmlApplicationContext (” classpath: config. XML “); , you need to resolve the classpath to the correct path.
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
Copy the code
We have different operating environments, such as dev, test or Prod, and the loaded configuration files and properties should be different. In this case, we need to use Environment to distinguish between them.
The Spring environment and properties are made up of four parts:
Environment
: Environment, byProfile
å’ŒPropertyResolver
Combination.Profile
: configuration file, which can be interpreted as the attributes and values of multiple configuration groups in a containerbean
, only activeprofile
, its corresponding group attributes andbean
Before it gets loadedPropertySource
: Attribute source, usedCopyOnWriteArrayList
Array for property pairskey-value
storedPropertyResolver
: property resolver, which is used to parse properties
Environment
First look at StandardServletEnvironment inheritance system:
As you can see, the top level interface is the PropertyResolver, which is used to parse properties, and ultimately parse the calling method
PropertyPlaceholderHelper.replacePlaceholders
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value, "'value' must not be null");
// Replace all placeholders in the format {@code ${name}} with the returned value
return parseStringValue(value, placeholderResolver, null);
}
Copy the code
Profile
Using this attribute, you can deploy two sets of configurations in the configuration file at the same time, which are applicable to the production environment and the development environment. In this way, you can easily switch the development and deployment environment, and often change different databases or configuration files.
Demo :(referenced in article 4 of resources)
<! -- Test environment configuration file -->
<beans profile="test">
<context:property-placeholder location="classpath:test/*.properties, classpath:common/*.properties" />
</beans>
<! -- Production environment configuration file -->
<beans profile="production">
<context:property-placeholder location="classpath:production/*.properties, classpath:common/*.properties" />
</beans>
<! -- Development environment configuration file -->
<beans profile="development">
<context:property-placeholder location="classpath:dev/*.properties, classpath:common/*.properties" />
</beans>
Copy the code
There are two ways to set which configuration to use:
â‘ Set the parameters in web. XML
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>test</param-value>
</context-param>
Copy the code
â‘¡ Set when the code starts
context.getEnvironment().setActiveProfiles("test");
Copy the code
Property
Property official comment description:
/**
* A description of a JavaBeans Property that allows us to avoid a dependency on
* {@code java.beans.PropertyDescriptor}. The {@code java.beans} package
* is not available in a number of environments (e.g. Android, Java ME), so this is
* desirable for portability of Spring's core conversion facility.
*
**/It allows us to avoid using {@codeDependency on java.bean.propertyDescriptor}. Because {@codeJava. bean}packageIt is not available in many environments (Android, Java ME, for example), so this is ideal for the portability of Spring's core transformation tools.Copy the code
You can find it in abstractenvironment. Java. When setting env, you create a MutablePropertySources object that holds properties:
private final MutablePropertySources propertySources = new MutablePropertySources()
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
public AbstractEnvironment(a) {
customizePropertySources(this.propertySources);
}
Copy the code
PropertySource interface
The inheritance system is shown as follows:
From the PropertySource inheritance, the customizePropertySources method is called all the way up from the subclass:
AbstractEnvironment
-> StandardServletEnvironment
-> StandardEnvironment
Finally, the properties are stored in StandardEnvironment using the CopyOnWriteArrayList array
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
Copy the code
For example, propertySourceList will store the system’s parameters:
These parameters can then be retrieved in the context of the launched application
((MutablePropertySources)((StandardEnvironment)context.environment).propertySources).propertySourceList
Copy the code
summary
Just a series of prelude work, just to identify the path resources and load system parameters
- Setup constructor
- Identify path variables
- Setting environment ParametersMain is:
Environment
System, and inpropertySources
The runtime parameters are saved in
Bean parsing and registration
There is an important method for parsing and registering Spring beans, refresh()
AbstractApplicationContext.refresh()
public void refresh(a) throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// Class registration to bean Factory is also in this step
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...resetCommonCaches(); }}}Copy the code
This method is tracked and analyzed below.
The prepareRefresh is ready for an update
This method prepares this context for refreshing, setting its start date and active flag, and performing initialization of any property sources.
protected void prepareRefresh(a) {
// Switch to active.
// Initialize any placeholder property sources in the context.
initPropertySources();
// Validate that all properties are marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
Copy the code
The specific verification method
org.springframework.core.env.AbstractPropertyResolver#validateRequiredProperties
public void validateRequiredProperties(a) {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key : this.requiredProperties) {
if (this.getProperty(key) == null) { ex.addMissingRequiredProperty(key); }}if(! ex.getMissingRequiredProperties().isEmpty()) {throwex; }}Copy the code
As you can see, the validation logic iterates over requiredProperties, which is a Set of characters that is null by default, indicating that no elements need to be validated. If there is a value in the list and the corresponding environment variable is null based on the key, an exception will be thrown, causing Spring container initialization to fail.
Customize environment variable verification
Now that we have a requireProperties list, we can add the required environment variables to it:
- Create a class that inherits from
AnnotationConfigServletWebServerApplicationContext
, overloadinginitPropertySources
- When the application starts, set your new class as the application context (
application.setApplicationContextClass(CustomContext.class);
)
For example :(cited in article 5 of resources)
public class CustomApplicationContext extends AnnotationConfigServletWebServerApplicationContext {
@Override
protected void initPropertySources(a) {
super.initPropertySources();
// use "MYSQL_HOST" as the environment variable that must be verified at startup
getEnvironment().setRequiredProperties("MYSQL_HOST"); }}public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(CustomizepropertyverifyApplication.class);
springApplication.setApplicationContextClass(CustomApplicationContext.class);
springApplication.run(args);
}
Copy the code
By adding custom validation values, you can pre-validate Spring applications when they start
To obtainbean
The container
In this line of code ConfigurableListableBeanFactory the beanFactory = obtainFreshBeanFactory ();
Specifically called is:
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
protected final void refreshBeanFactory(a) throws BeansException {
// On update, if it already exists, the previous bean is cleaned up and the old bean container is closed
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
// Comment 1.3 Start loading (bean registration)
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory; }}catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex); }}Copy the code
This entry method is important, creating a new bean container and resolving beans, and registering the beans in the container.
BeanFactory inheritance
In this example, and in most cases, use the bean container are DefaultListableBeanFactory, since the introduction of its inheritance system:
As you can see, the inheritance system is very large, inheriting multiple registries and implementing multiple interfaces. Singleton registries and Alias registries are commonly used. These two concepts are also very large. We’ll talk about it later when we have a chance.
BanFactory custom
Here’s how: With this method, you can customize the factory and let subclasses configure it freely:
org.springframework.context.support.AbstractRefreshableApplicationContext#customizeBeanFactory
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding ! =null) {
// The default is false and overwriting is not allowed
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences ! =null) {
// The default is false and circular references are not allowed
beanFactory.setAllowCircularReferences(this.allowCircularReferences); }}Copy the code
Bean loading and parsing
The core method is this:
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory. support.DefaultListableBeanFactory)
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// Create a new XmlBeanDefinitionReader for the given BeanFactory
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// Then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
Copy the code
In parsing XML, two inheritance systems are used: EntityResolver and BeanDefinitionReader
EntityResolver
Interface is the full path: org. XML.. Sax EntityResolver, specific analytical method is:
org.springframework.beans.factory.xml.ResourceEntityResolver#resolveEntity
This method is used to parse schemas and DTDS, but parsing XML is not what I want to know, so I’ll skip ~ for now
BeanDefinitionReader
The top-level interface is BeanDefinitionReader, a bean-definition reader for XML Bean definitions. Delegate the XML document that is actually read to the implementation.
The purpose of these two classes is to turn XML into an input stream, and you can follow them further
Loading configuration files
Entry method :(because there are multiple methods with the same name, so when copying the path, the type of the parameter is also copied)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)
The core method is these two lines
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// Get resource file (resource loader identifies resource file from path)
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location)
// Comment 1.6 Loads beans from resource files
intcount = loadBeanDefinitions(resources); ...}Copy the code
Once you get the resource file, you parse the resource file (the config.xml that was passed in the first place) and convert it to Document
Tracing the code, you can see that the parsed Resource file is wrapped from Resource as EncodeResouce, adding character encoding (null by default) to the input stream, embodiments the design-decorator pattern
The core method is the following two lines:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.En codedResource)
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
// Comment 1.7 Get the input stream from the resource file
InputStream inputStream = encodedResource.getResource().getInputStream();
InputSource inputSource = new InputSource(inputStream);
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
Copy the code
Bean parsing
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
// comment 1.8 Parse resource files into documents
Document doc = doLoadDocument(inputSource, resource);
// Note 1.10 Parses elements from doc and resources to register with bean Factory
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
Copy the code
In the doLoadDocument() method, the resource file is parsed into a Docuemnt document
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
/ / use BeanDefinitionDocumentReader DefaultBeanDefinitionDocumentReader instantiated
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// Record the number of beanDefinitions loaded before statistics
int countBefore = getRegistry().getBeanDefinitionCount();
/ / load and registered bean, is a registered factories DefaultListableBeanFactory here
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// Record the number of beanDefinitions loaded (new value - old value)
return getRegistry().getBeanDefinitionCount() - countBefore;
}
Copy the code
How to convert document and documentReader to document initialization is not explained here
Want to say is bean container DefaultListableBeanFactory parse the document below
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if(! getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return; }}}PreProcess and postProcess are empty methods. These methods are left to subclasses to override and represent the design pattern-template method
preProcessXml(root);
// Annotate the 1.11 core method to parse the doc element
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
Copy the code
Can be seen from the above, before parsing, if the namespace is http://www.springframework.org/schema/beans at the beginning, and will check the profile properties
Once the validation passes, the doc element is formally parsed
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
// Comment 1.12 Traversing the node list in doc
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// Note 1.13 bean registry that recognizes default tags
// Depending on the element name, call a different loading method to register the bean
parseDefaultElement(ele, delegate);
}
else{ delegate.parseCustomElement(ele); }}}}else{ delegate.parseCustomElement(root); }}Copy the code
In this step, the attributes we configured in the XML can be mapped to the Document object for later use in the process
Default tag parsing
I won’t go into detail in this section, but I’ll cover it in a later article, so let’s take a quick look at how the default tag is resolved in code
- IMPORT: IMPORT labels
- ALIAS: indicates an ALIAS label
- BEAN:
bean
The label - NESTED_BEANS:
beans
Tags (nestedbeans
)
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recursedoRegisterBeanDefinitions(ele); }}Copy the code
Let’s see how to parse bean tags
Bean label parsing
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// Comment 1.15 Parsing elements of bean names
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if(bdHolder ! =null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance. (Note 1.16 Register the last decorated instance)
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder)); }}Copy the code
Here’s what the key methods do
Get id and name
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// Get the ID attribute
String id = ele.getAttribute(ID_ATTRIBUTE);
// Get the NAME attribute
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
// Name according to,; segmentation
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if(! StringUtils.hasText(beanName) && ! aliases.isEmpty()) {// If id is not specified, the first value of name is used as id
beanName = aliases.remove(0);
}
/ / null by default
if (containingBean == null) {
// Check if the name is unique, and throw an error if the ID is duplicated
// Internal usedNames is a HashSet that will store loaded names and aliases
checkNameUniqueness(beanName, aliases, ele);
}
AbstractBeanDefinition {GenericBeanDefinition} // Put a public property in AbstractBeanDefinition
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if(beanDefinition ! =null) {
if(! StringUtils.hasText(beanName)) {if(containingBean ! =null) {
// If both id and name are empty, spring will generate a default name for it
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if(beanClassName ! =null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
Copy the code
The process of getting the ID and name attributes is clear step by step as you follow the code comments
The main workflow of this method is as follows:
- Extract elements from
id
name
attribute - All other attributes are further parsed and unified encapsulated to
GenericBeanDefinition
Type - detected
bean
Is not specifiedbeanName
Use default rule generationbeanName
- Encapsulate the obtained information to
BeanDefinitionHolder
In the instance of the
Parsing of other attributes in the tag
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, java.lang.String, org.springframework.beans.factory.config.BeanDefinition)
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) {
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
Copy the code
Initialize BeanDefiniton in this method :(the implementation is a subclass of GenericBeanDefinition.)
BeanDefinitionReaderUtils.createBeanDefinition(parentName, className, this.readerContext.getBeanClassLoader())
public static AbstractBeanDefinition createBeanDefinition( @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if(className ! =null) {
if(classLoader ! =null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else{ bd.setBeanClassName(className); }}return bd;
}
Copy the code
The following is to parse the content of other tags, and then will patch the hole ~
BeanDefinition inheritance system
As can be seen from the figure, BeanDefinition is an interface, GenericBeanDefinition, RootBeanDefinition, ChildBeanDefinition, All three of these inherit AbstractBeanDefinition.
Where BeanDefinition is the internal representation of the configuration file
element tag in the container.
The
element tag has class, scope, and lazy-init configuration attributes, while the BeanDefinition provides the corresponding beanClass, scope, and lazyInit attributes, which correspond to each other.
BeanDefinitionHolder modification
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#decorateBeanDefinitionIfRequired(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinitionHolder, org.springframework.beans.factory.config.BeanDefinition)
public BeanDefinitionHolder decorateBeanDefinitionIfRequired( Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {
// The third argument in the method is the parent bean
// When analyzing a nested configuration, we need to pass it in order to use the scope attribute of the parent class, in case the child class does not have scope, we can use the scope attribute of the parent class
BeanDefinitionHolder finalDefinition = definitionHolder;
// Decorate based on custom attributes first.
NamedNodeMap attributes = ele.getAttributes();
// Iterate over all attributes to modify attributes
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
// Decorate based on custom nested elements.
NodeList children = ele.getChildNodes();
// Iterate through all the child nodes, modifying the child elements
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if(node.getNodeType() == Node.ELEMENT_NODE) { finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); }}return finalDefinition;
}
Copy the code
After the previous general attribute parsing, this step is mainly used to complete the parsing of the custom tag element, here continues to leave a hole ~
Bean registered
Through the painstaking parsing operations in the above columns, we have finally arrived at a way to register bean information
org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
/ / comment 1.17 in DefaultListableBeanFactory beanDefinitionMap add bean definition
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if(aliases ! =null) {
for(String alias : aliases) { registry.registerAlias(beanName, alias); }}}Copy the code
Also said above, here is the use of the bean container is DefaultListableBeanFactory, registration method key operation when the following two lines of code:
org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
}
Copy the code
At this point, I’m going tobean
Put information intobeanDefinitionMap
, the operation of class registration is completed
In order to describe the integrity of the code logic, the following methods are briefly introduced.
prepareBeanFactory
Prepare class loader environment, to the front to get to the beanFactory (ConfigurationListableBeanFactory) related Settings, including this, post – processors, etc
postProcessBeanFactory
When all bean definitions are loaded, but the bean has not been instantiated, its internal bean container is modified after the standard initialization of the application context.
This allows you to register special BeanPostProcessors, etc., in a particular ApplicationContext implementation.
This is also an empty method, to be implemented by subclasses
invokeBeanFactoryPostProcessors
Instantiate and call all registered BeanFactoryPostProcessorBean, these are the post-processor, processing type is the BeanFactory, the Spring container allows before instantiation bean, bean information and modify its properties.
This gives the user one last chance to modify the bean information before instantiation.
Also, execution can be ordered by Order value, depending on whether these processors implement PriorityOrdered, Order interfaces.
registerBeanPostProcessors
Instantiate and register all post-handlers. Unlike the above method, this method handles beans of type and, like the above method, also has the concept of priority
initMessageSource
Initialize the message source for this context
initApplicationEventMulticaster
Initializes the event multicast program for this context
onRefresh
Template methods that can be overridden to add context-specific refresh work.
Call the initialization of a particular bean before instantiating the singleton. (fog, don’t know what special bean, leave a pit =-=)
This implementation is null.
registerListeners
Check the listener beans and register them
The EventListener type is java.util.eventlistener
finishBeanFactoryInitialization
Complete the initialization of the bean container, instantiating all remaining (non-lazy-initialized) singletons
finishRefresh
Finally, publish the corresponding event
The event type is java.util.eventobject
resetCommonCaches
The last step in real registration is to clear the cache
Reset the public introspection cache in the Spring core, because we may never need the singleton bean metadata again
conclusion
The notes in this chapter simply document how a bean is loaded from XML into the registry of the bean container, goes through many lines of code, and finally figures out the call link.
Here’s a summary of the core loadBeanDefinitions(beanFactory) workflow:
â‘ Read the configuration file
- Encapsulating resource files: Obtain the path file and encapsulate it as
EncodeResource
- Get input streamFrom:
Resource
To obtain the correspondingInputStream
And constructInputSource
- Passing parametersBy construction
InputSource
Instance andResource
Instance, passed todoLoadBeanDefinitions
methods
(2) load the beans
- Access to
XML
Validation mode for resource files - loading
XML
The resource file is parsed into the correspondingDocument
The document: There are many in itNode
Node information, which holds the configuration information we wrote - According to the
Document
A fileBean
Information parsing
â‘¢ Bean label parsing and registration
- entrust
BeanDefinitionDelegate
Of the classparseBeanDefinitionElement
methods: Parses the element and returnsBeanDefinitionHolder
An instance of theclass
,name
,id
,alias
Such attributes) - Parsing labels: Determine the label type and determine whether the default or custom labels are parsed
- right
bdHodler
To register: After parsing, registerbean
Information, registration operations delegated toBeanDefinitionReaderUtils
çš„registerBeanDefinition
methods - Send response event: Notifies associated listeners, notifies
bean
The container has been loaded
See you in the next note
Record on pit
Javadoc generation failed. Generated Javadoc options file (useful for troubleshooting)
The gradle file contains the following configuration: gradle: gradle: gradle: gradle: gradle: gradle: gradle: gradle
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none'.'-quiet')
options.addStringOption('encoding'.'UTF-8')}Copy the code
The resources
1, spring – analysis/note/spring. The md
2. Spring Framework 5.0.0m3
3. Deep analysis of Spring source code/Hao Jia. — Beijing: Posts and Telecommunications Press
Profile configuration with Spring3.1 enables different environments to load different profiles
5, one of the extension practices of spring4.1.8: custom environment variable validation
Portal:
-
Spring source learning – environment preparation
-
(1) The infrastructure of the container
-
Spring source code learning (2) default tag parsing
-
Spring source code learning (3) custom tags
-
Spring source code learning (four) bean loading
-
Spring source code learning (5) loop dependency
-
Spring source code learning (six) extension function part 1
-
Spring source code learning (seven) extension features part 2
-
Spring source learning (eight) AOP use and implementation principle
-
Spring source code learning (9) Transaction Transaction
-
(10) Spring MVC