The most detailed source code analysis of Spring core IOC (different insights every time)
The most important concepts of Spring are IOC and AOP. This article is actually to lead you to analyze the IOC container of Spring. Since we usually use Spring, how can we not have a good understanding of Spring? Reading this article will not make you a Spring expert, but it will help you understand many concepts of Spring and help you troubleshoot some Spring related problems in your application.
Reading suggestions: readers should at least know how to configure Spring and understand various concepts in Spring. I also assume that readers have used Spring MVC. Generally speaking, the IOC to be mentioned in this article has two most important places: one is to create a Bean container and the other is to initialize a Bean. If readers feel a little pressure after reading this article at one time, they can digest it twice according to this idea. Readers may not be interested in the source code of the Spring container. Perhaps the knowledge introduced in the appendix will help readers.
The source code version I use is 4.3.11 Release, 5.0 A relatively new version before X. In order to reduce the difficulty, all the contents mentioned in this paper are based on xml configuration. Few people have done so in actual use, at least not pure xml configuration. However, from the perspective of understanding the source code, this method is undoubtedly the most appropriate. If readers are interested in the source code of annotation, maybe I can write an article when I have time.
I hope to write this article into a good article on Spring IOC source code analysis. I hope that through this article, readers can not be afraid to read Spring source code.
In order to maintain the preciseness of the article, if the reader finds out what I said wrong, please point it out. I hope to hear the voice of the reader very much.
catalogue
- introduction
- [introduction to beanfactory]( https://javadoop.com/post/spring-ioc#BeanFactory Introduction)
- Start up process analysis
- [preparations before creating Bean container]( https://javadoop.com/post/spring-ioc# Preparation before creating Bean container)
- Create a Bean container, load and register beans
- [beandefinition interface definition]( https://javadoop.com/post/spring-ioc#BeanDefinition Interface definition)
- customizeBeanFactory
- Load Bean: loadBeanDefinitions
- doRegisterBeanDefinitions:
- processBeanDefinition
- [register bean]( https://javadoop.com/post/spring-ioc# Register Bean)
- [after bean container instantiation is completed]( https://javadoop.com/post/spring-ioc#Bean Container instantiation (after completion)
- [prepare Bean container: preparebeanfactory]( https://javadoop.com/post/spring-ioc# Prepare Bean container (% 3A prepareBeanFactory)
- Initialize all singleton beans
- preInstantiateSingletons
- getBean
- Create Bean
- [create Bean instance]( https://javadoop.com/post/spring-ioc# Create Bean instance)
- [bean attribute injection]( https://javadoop.com/post/spring-ioc#bean Attribute injection)
- initializeBean
- appendix
- [ID and name]( https://javadoop.com/post/spring-ioc#id And name)
- [configure whether Bean overrides and circular dependencies are allowed]( https://javadoop.com/post/spring-ioc# Configure whether Bean overrides and circular dependencies are allowed)
- profile
- [factory pattern generation bean]( https://javadoop.com/post/spring-ioc# Factory pattern generation (Bean)
- FactoryBean
- [initialize callback of Bean]( https://javadoop.com/post/spring-ioc# Initialize callback of Bean)
- [destroy Bean callback]( https://javadoop.com/post/spring-ioc# Destroy Bean callback)
- ConversionService
- [bean inheritance]( https://javadoop.com/post/spring-ioc#Bean (inherited)
- Method injection
- BeanPostProcessor
- summary
introduction
Let's take a look at the most basic example of starting the Spring container:
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml");}
The above code can use the configuration file to start a Spring container. Please use maven's small partner to directly add the following dependencies to the dependencies. I am opposed to those who don't know what dependencies to add and then add all related things of Spring.
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.11.RELEASE</version></dependency>
Spring context will automatically bring in the basic jar packages of spring core, spring beans, spring AOP and spring expression.
To add a word, many developers directly contact spring MVC when they get started. In fact, they don't know much about spring. Spring is a progressive tool, not very intrusive, and its modules are reasonably divided. Even if your application is not a web application, or you haven't used spring at all before, and you want to inject this function with spring's dependency, In fact, it is completely possible. Its introduction will not conflict with other components.
With that nonsense, let's go on. ApplicationContext context = new ClassPathXmlApplicationContext(...) In fact, it's easy to understand. One or two guesses from the name are to find the xml configuration file in ClassPath and build the ApplicationContext according to the content of the xml file. Of course, in addition to classpathxmlapplicationcontext, we also have other options for building ApplicationContext. Let's take a look at the general inheritance structure:
Readers can take a general look at the class name. When analyzing the source code, they won't be unable to find which class to look at, because each interface provided by Spring may have many implementation classes in order to adapt to various use scenarios. For us, it is to grasp a complete branch. Of course, when reading this article, readers don't have to worry too much. When analyzing each code block, I will tell readers which class and line we are talking about.
We can see that ClassPathXmlApplicationContext took a long time to get to the ApplicationContext interface. Similarly, we can also use the green FileSystemXmlApplicationContext and AnnotationConfigApplicationContext classes.
The constructor of FileSystemXmlApplicationContext requires the path of an xml configuration file in the system, which is basically the same as ClassPathXmlApplicationContext.
AnnotationConfigApplicationContext is used based on annotations. It does not need configuration files. It is a relatively simple way to configure it by using java configuration classes and various annotations. It is also the general trend.
However, this article is intended to help you understand the whole construction process, so we decided to use ClassPathXmlApplicationContext for analysis.
Let's start with a simple example to see how to instantiate ApplicationContext.
First, define an interface:
public interface MessageService { String getMessage();}
Define interface implementation classes:
public class MessageServiceImpl implements MessageService { public String getMessage() { return "hello world"; }}
Next, we create a new configuration file in the resources directory. The file name is arbitrary, usually called application XML or application XXX XML is OK:
<?xml version="1.0" encoding="UTF-8" ?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName"> <bean id="messageService" class="com.javadoop.example.MessageServiceImpl"/></beans>
So we can run:
public class App { public static void main(String[] args) { // Use our configuration file to start an ApplicationContext. ApplicationContext = new classpathxmlapplicationcontext ("classpath: application. XML"); System. out. Println ("context started successfully")// Take out our Bean from the context instead of using new MessageServiceImpl(), messageservice = context getBean(MessageService.class); // This sentence will output: Hello world system out. println(messageService.getMessage()); }}
The above example is very simple, but it is enough to lead to the topic of this article, that is, how to start Spring's ApplicationContext through the configuration file? That is the core of the IOC we are going to analyze today. During the startup of ApplicationContext, it will be responsible for creating instance beans and injecting dependencies into each Bean.
Introduction to BeanFactory
Beginners should not think that what I said before has nothing to do with BeanFactory. The ApplicationContext mentioned earlier is actually a BeanFactory. Let's take a look at the main inheritance structures related to the BeanFactory interface:
I think everyone may not be very happy after reading this picture. The inheritance structure under ApplicationContext has been described in the previous figure, which will not be repeated here. It's definitely unnecessary to memorize this picture. Just explain a few key points to you.
- ApplicationContext inherits ListableBeanFactory. This Listable means that through this interface, we can obtain multiple beans. The methods of the topmost BeanFactory interface are to obtain a single Bean.
- ApplicationContext inherits the Hierarchical BeanFactory. The Hierarchical word itself can explain the problem, that is, we can create multiple beanfactories in the application, and then set each BeanFactory as a parent-child relationship.
- Autowire in the name of AutowireCapableBeanFactory is very familiar to everyone. It is used to automatically assemble beans. However, looking carefully at the figure above, ApplicationContext does not inherit it, but don't worry. If inheritance is not used, it does not mean that combination can not be used, If you see the last method getAutowireCapableBeanFactory() in the ApplicationContext interface definition, you will know.
- ConfigurableListableBeanFactory is also a special interface. See the figure. The difference is that it inherits all three interfaces of layer 2, but ApplicationContext does not. This will be used later.
- Please don't spend time on other interfaces and classes. Just understand what I said first.
Then, please open the editor and look through the codes of BeanFactory, ListableBeanFactory, hierarchalbeanfactory, AutowireCapableBeanFactory and ApplicationContext. Let's have a general idea of the methods in each interface. I won't post the code introduction due to space limitation.
Start up process analysis
The following will be a lengthy code analysis. Please have a drink first. Remember, you must open the source code in the computer, otherwise you will be very tired.
In the first step, we must start with the construction method of ClassPathXmlApplicationContext.
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { private Resource[] configResources; // If an ApplicationContext already exists and needs to be configured as a parent-child relationship, call the constructor public classpathxmlapplicationcontext (ApplicationContext parent) {super (parent);} Public classpathxmlapplicationcontext (string [] configlocations, Boolean refresh, ApplicationContext parent) throws beansexception {super (parent); / / according to the provided path, process it into configuration file array (separated by semicolon, comma, space, tab and newline character) setconfiglocations (configlocations); if (refresh) {refresh() ; // Core method}}...}
Next, we'll talk about refresh(). Here's why it's a method with the name refresh() instead of init(). After the ApplicationContext is established, we can actually rebuild it by calling the refresh() method. In this way, the original ApplicationContext will be destroyed and then the initialization operation will be performed again.
Looking down, you can see that the refresh() method calls so many methods. It must be not simple. Please read the details first and then talk about them in detail.
@Overridepublic void refresh() throws BeansException, IllegalStateException { // Give a lock, otherwise refresh() is not over, and you start or destroy the container again, synchronized (this.startupShutdownMonitor) {/ / preparation. Record the container startup time, mark the "started" status, and handle the placeholder prepareRefresh() in the configuration file ; // This is a key step. After this step is completed, the configuration file will be parsed into Bean definitions and registered in BeanFactory. / / of course, the Beans mentioned here have not been initialized, but the configuration information has been extracted, //Registration only saves these information to the registration center (in the final analysis, the core is a map of beanname - > beandefinition). Configurablelistablebeanfactory BeanFactory = obtainfreshbeanfactory()// Set the class loader of BeanFactory, add several beanpostprocessors, and manually register several special Beans. / / the preparebeanfactory (BeanFactory) will be introduced later; Try {/ / [here you need to know the knowledge of BeanFactory postprocessor. If the Bean implements this interface, / / after the container is initialized, spring will be responsible for calling the postProcessBeanFactory method inside.]// Here is the extension point provided to subclasses. When you get there, all Beans have been loaded and registered, but they have not yet been initialized. / / for specific subclasses, you can add some special implementation classes of beanfactoryprocessor or or do something postProcessBeanFactory(beanFactory)// Call the postProcessBeanFactory(factory) method invokeBeanFactoryPostProcessors(beanFactory) of each implementation class of beanfactoryprocessor// Register the implementation class of BeanPostProcessor. Pay attention to the difference between beanfactoryprocessor and beanfactoryprocessor. / / there are two methods in this interface: postProcessBeforeInitialization and postProcessAfterInitialization. / / the two methods are executed before and after Bean initialization respectively. Note that the Bean has not yet initialized registerBeanPostProcessors(beanFactory)// Initialize the MessageSource of the current ApplicationContext. Internationalization will not be expanded here, otherwise it will be endless initMessageSource()// Initialize the event broadcaster of the current ApplicationContext, and initapplicationeventmulticast() is not expanded here// You can know from the method name that for typical template methods (hook methods), / / specific subclasses can initialize some special Beans here (before initializing singleton beans) onRefresh()// Register the event listener. The listener needs to implement the ApplicationListener interface. This is not our focus, too. We have used registerListeners()// Focus, focus, focus / / initialize all singleton beans / / (except lazy init) finishBeanFactoryInitialization(beanFactory)// Finally, broadcast the event, and the ApplicationContext initialization is completed. 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. // Destroy the initialized singleton beans to avoid that some Beans will always occupy resources destroybeans()// Reset 'active' flag. cancelRefresh(ex); // Throw out the exception throw ex;} finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } }}
Next, let's start dismembering the refresh() method step by step.
Preparations before creating Bean container
This is relatively simple. Just look at a few comments in the code.
protected void prepareRefresh() { // Record the startup time, / / set the active property to true and the closed property to false. Both of them are AtomicBoolean type this startupDate = System. currentTimeMillis(); this. closed. set(false); this. active. set(true); if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // Initialize any placeholder property sources in the context environment initPropertySources(); // Verify the xml configuration file getenvironment() validateRequiredProperties(); this. earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();}
Create a Bean container, load and register beans
Note that this method is one of the most important parts of the full text. It will initialize BeanFactory, load beans, register beans, and so on.
Of course, after this step, the Bean did not complete initialization.
// AbstractApplicationContext.java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // Close the old BeanFactory (if any), create a new BeanFactory, load bean definitions, register beans, etc. refreshBeanFactory()// Return the newly created BeanFactory configurablelistablebeanfactory BeanFactory = getbeanfactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + BeanFactory); } return BeanFactory;}
// AbstractRefreshableApplicationContext.java 120
@Overrideprotected final void refreshBeanFactory() throws BeansException { // If beanfactories have been loaded in the ApplicationContext, destroy all beans and close beanfactories. / / note that there can be multiple beanfactories in the application. This does not mean whether there are beanfactories in the global application, It is whether the current / / ApplicationContext has BeanFactory if (hasbeanfactory()) {destroybeans(); closebeanfactory();} Try {/ / initialize a DefaultListableBeanFactory. Why use this? Let's talk about it right away. DefaultListableBeanFactory beanFactory = createBeanFactory(); / / it is used for BeanFactory serialization. I don't think some people should use BeanFactory. Setseriationid (getid()) ); // The following two methods are very important. Don't lose them. Tell me the details later. / / set the two configuration properties of BeanFactory: whether to allow Bean overwrite and whether to allow circular reference to customizeBeanFactory(beanFactory)// Load beans into BeanFactory loadbeandefinitions (BeanFactory); synchronized (this.beanFactoryMonitor) { this.BeanFactory = BeanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing Bean definition source for " + getDisplayName(), ex); }}
When seeing this, I think readers should stand high and look at ApplicationContext. ApplicationContext inherits from BeanFactory, but it should not be understood as the implementation class of BeanFactory, but that it holds an instantiated BeanFactory (DefaultListableBeanFactory). All operations related to BeanFactory in the future are actually handled by this instance.
Let's talk about why we chose to instantiate DefaultListableBeanFactory? As mentioned earlier, there is a very important interface ConfigurableListableBeanFactory, which implements all three interfaces in the lower layer of BeanFactory. I'll take the previous inheritance diagram and have a closer look:
We can see that ConfigurableListableBeanFactory has only one implementation class DefaultListableBeanFactory, and the implementation class DefaultListableBeanFactory also eats the right way by implementing AbstractAutowireCapableBeanFactory on the right. So the conclusion is that the DefaultListableBeanFactory at the bottom is basically the best BeanFactory, which is why this class is used to instantiate.
Before moving on, we need to understand BeanDefinition. We say that BeanFactory is a Bean container, so what is a Bean?
The BeanDefinition here is what we call Spring beans. In fact, each Bean defined by ourselves will be transformed into BeanDefinition, which exists in the BeanFactory of Spring.
Therefore, if someone asks you what a Bean is, you should know that a Bean is an instance of BeanDefinition at the code level.
BeanDefinition stores our Bean information, such as which class the Bean points to, whether it is a singleton, whether it is lazy to load, which beans the Bean depends on, and so on.
BeanDefinition interface definition
Let's look at the interface definition of BeanDefinition:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { // We can see that only sington and prototype are provided by default. / / many readers know that there are also request, session, globalsession, application and websocket. / / however, they are web-based extensions. String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; // It's not important. Just skip int ROLE_APPLICATION = 0; int ROLE_SUPPORT = 1; int ROLE_INFRASTRUCTURE = 2; // Setting the parent Bean involves Bean inheritance, not java inheritance. Please refer to the appendix to introduce void setParentName(String parentName)// Get parent Bean string getparentname()// Set the class name of the Bean void setBeanClassName(String beanClassName)// Get the class name of Bean String getBeanClassName()// Set the scope void setscope (string scope) of the Bean; String getScope(); // Set whether to lazy load void setlazyinit (Boolean lazyinit); boolean isLazyInit(); // Set all beans that the Bean depends on. Note that the dependency here does not refer to attribute dependency (as marked by @ Autowire), / / it is the value set by the dependencies on = "" attribute. void setDependsOn(String... dependsOn); // Return all dependencies of the Bean String[] getDependsOn()// Set whether the Bean can be injected into other beans, which is only valid for injection according to type. / / if the Bean is injected according to name, void setAutowireCandidate(boolean autowireCandidate) is OK even if false is set here// Can this Bean be injected into other beans? boolean isAutowireCandidate()// major. For multiple implementations of the same interface, if no name is specified, Spring will give priority to the Bean void setprimary (Boolean primary) with primary set to true// Whether it is the primary's boolean isPrimary()// If the Bean is generated using the factory method, specify the factory name. For readers unfamiliar with the factory, please refer to the appendix void setFactoryBeanName(String factoryBeanName)// Get factory name String getFactoryBeanName()// Specify the factory method name in the factory class void setFactoryMethodName(String factoryMethodName)// Get the factory method name in the factory class String getFactoryMethodName()// Constructor parameter constructorargumentvalues getconstructorargumentvalues()// The property values in the Bean. MutablePropertyValues getPropertyValues() will be mentioned later when injecting property values into the Bean// Whether singleton Boolean is singleton()// Whether prototype Boolean isprototype()// If the Bean is an abstract class, you cannot instantiate Boolean isabstract(); int getRole(); String getDescription(); String getResourceDescription(); BeanDefinition getOriginatingBeanDefinition();}
In fact, this BeanDefinition already contains a lot of information. It doesn't matter what all methods correspond to for the time being. I hope readers can thoroughly understand everything after reading this article.
There are so many interfaces here, but there is no method like getInstance() to obtain the instance of the class we defined. Where is the real instance generated by the class we defined? Don't worry, this will be discussed later.
After having the concept of BeanDefinition, let's move on to the rest of the refreshBeanFactory() method:
customizeBeanFactory(beanFactory);loadBeanDefinitions(beanFactory);
Although there are only two ways, there is still a long way to go...
customizeBeanFactory
customizeBeanFactory(beanFactory) is relatively simple, which is to configure whether BeanDefinition coverage and circular reference are allowed.
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { // Allow Bean definitions to override beanfactory setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } If (this. Allowcircularreferences! = null) {/ / whether to allow circular dependency between beans beanFactory.setAllowCircularReferences(this.allowCircularReferences);}}
You may encounter the problem of overriding BeanDefinition, that is, the same id or name is used when defining beans in the configuration file. By default, allowBeanDefinitionOverriding property is null. If it is repeated in the same configuration file, an error will be thrown, but if it is not in the same configuration file, overriding will occur.
Circular references are also well understood: A depends on B, and B depends on A. Or A depends on B, B depends on C, and C depends on A.
By default, Spring allows circular dependency. Of course, if you rely on B in the construction method of A, you can't rely on A in the construction method of B.
How are these two properties configured? I introduced it in the appendix. Especially for coverage, many people want to prohibit Bean coverage, but Spring can override different files by default.
After that, these two attributes will appear in the source code, and the reader can have an impression.
Load Bean: loadBeanDefinitions
Next is the most important loadBeanDefinitions(beanFactory) method. This method will load each Bean according to the configuration, and then put it into BeanFactory.
The operation of reading configuration is in XmlBeanDefinitionReader, which is responsible for loading configuration and parsing.
// AbstractXmlApplicationContext.java 80
/** We can see that this method will load each Bean through an XmlBeanDefinitionReader instance.*/@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Instantiate an xmlbeandefinitionreader for this 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)); // Initialize BeanDefinitionReader. In fact, this method is provided for subclass override. / / I have a look. There is no class override method. Let's take it as unimportant for the time being. initBeanDefinitionReader(beanDefinitionReader)// Here's the point. Continue to load bean definitions (bean definition reader);}
Now it is still in this class. Next, use the Reader just initialized to load the xml configuration. This code can be skipped by readers, which is not very important. In other words, readers can easily skip the following code block.
// AbstractXmlApplicationContext.java 120
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { // Look down at reader loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); }} // Although there are two branches above, However, the second branch will soon be converted to a resource by parsing the path. It will also enter here later @ overridpublic int loadbeandefinitionstoreexception (Resource... Resources) throws beandefinitionstoreexception {assert.notnull (resources, "resource array must not be null") ; int counter = 0; // Note that this is a for loop, that is, each file is a resource for (resource resource: resources) {/ / continue to look at counter + = loadbean definitions (resource);} return counter;} // XmlBeanDefinitionReader 303@Overridepublic int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource));} // XmlBeanDefinitionReader 314public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } // Use a ThreadLocal to store all configuration file resources set < encoded resource > currentresources = this resourcesCurrentlyBeingLoaded. get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // Core part return doloadbeandefinitions (inputsource, encodedresource. Getresource());} finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } }} // Also in this file, line 388 protected int doloadbeandefinitions (inputsource, inputsource, resource) throws beandefinitionstoreexception {try {/ / don't look here. Document doc = doLoadDocument(inputSource, resource); / / continue to return registerbeandefinitions (DOC, resource);} Catch (...} / / in this file, line 505 / / returns the number of beanpublic int registerbeandefinitions (document document, resource) throws beandefinitionstoreexception {beandefinitiondocumentreader documentreader = createbeandefinitiondocumentreader(); int countbefore = getregistry() .getBeanDefinitionCount(); documentReader. registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry(). getBeanDefinitionCount() - countBefore;}// DefaultBeanDefinitionDocumentReader 90@Overridepublic void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root);}
After a long link, a configuration file is finally converted into a DOM tree. Note that this refers to one of the configuration files, not all of them. Readers can see that there is a for loop on it. Start from the root node:
doRegisterBeanDefinitions:
// DefaultBeanDefinitionDocumentReader 116protected void doRegisterBeanDefinitions(Element root) {/ / we can tell from the name that beandefinitionparserdelegate must be an important class, which is responsible for parsing Bean definitions, / / why should a parent be defined here? You can see later that it is a recursive problem, / / because < beans / > can be defined internally, so the root of this method is not necessarily the root node of xml, but also the root node Therefore, the < beans / > node nested in it. From the perspective of source code analysis, we can treat it as the root node. Beandefinitionparserdelegate parent = this delegate; this. delegate = createDelegate(getReaderContext(), root, parent); If (this. Delegate. Isdefaultnamespace (root)) {/ / this section refers to the root node < beans... Profile = "dev" />Whether the profile in is required by the current environment. / / if the profile configured in the current environment does not contain this profile, return it directly. Don't < beans / > analyze this. / / you are not familiar with what a profile is and how to configure a profile. For readers, please move to the appendix area, string profilespec = root getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped xml Bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); // The hook parsebean definitions (root, this. Delegate); postProcessXml(root); // Hook this delegate = parent;}
preProcessXml(root) and postProcessXml(root) are hook methods for subclasses. Since they are not used and are not our focus, we skip them directly.
This involves the issue of profile. For readers who don't understand it, I have made a simple explanation of profile in the appendix, which readers can refer to.
Next, look at the core parsing method parsebean definitions (root, this. Delegate):
// The default namespace involves four tags < import / >, < alias / >, < bean / > and < beans / >, //Other protected void parsebeandefinitions (element root, beandefinitionparserdelegate) {if (delegate. Isdefaultnamespace (root)) {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)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); }}
From the above code, we can see that for each configuration, enter parseDefaultElement(ele, delegate); And delegate parseCustomElement(ele); These two branches.
parseDefaultElement(ele, delegate) means that the resolved nodes are,,, and.
The four tags here are default because they are defined in this namespace:
http://www.springframework.org/schema/beans
It's time for beginners to popularize science again. Readers who are not familiar with namespace please see the xml posted below. The second line here is xmlns.
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">
For other tags, go to delegate Parsecustomelement (element) is the branch. For example, we often use,,, etc.
These are extensions. If you need to use the above "non default" tags, you should also introduce the path of the corresponding namespace and. xsd file in the above xml header, as shown below. At the same time, you need to provide corresponding parser s in the code to parse, such as MvcNamespaceHandler, TaskNamespaceHandler, ContextNamespaceHandler, AopNamespaceHandler, etc.
If readers want to analyze the implementation principle of ` ` they should find the answer in ContextNamespaceHandler.
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd " default-autowire="byName">
Come back and see how to handle the default tag:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // Process the < import / > tag importbeandefinitionresource (ELE);} Else if (delegate. Nodenameequals (ELE, alias_element)) {/ / process < alias / > tag definition / / < alias name = "fromname" alias = "toname" / > processaliasregistration (ELE);} Else if (delegate. Nodenameequals (ELE, bean_element)) {/ / process < bean / > tag definition, which is our focus. Processbeandefinition (ELE, delegate);} Else if (delegate. Nodenameequals (ELE, nested_beans_element)) {/ / if you encounter nested < beans / > tags, you need to recurse doRegisterBeanDefinitions(ele);}}
If every label says, I don't spit blood, you'll spit blood. Let's pick our key label and say it.
processBeanDefinition
The following is the processBeanDefinition resolution ` ` tag:
// DefaultBeanDefinitionDocumentReader 298
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // Extract the information from the < bean / > node and package it into a BeanDefinitionHolder. For details, see BeanDefinitionHolder bdholder = delegate parseBeanDefinitionElement(ele); // Don't look at the following lines first. Skip first, skip first, skip first, If (bdholder! = null) {bdholder = delegate.decoratebendefinitionifrequired (ELE, bdholder); try {/ / register the final modified 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(new BeanComponentDefinition(bdHolder)); }}
Before moving on to how to parse, let's take a look at the attributes that can be defined in the ` ` tag:
Property | |
---|---|
class | Fully qualified name of class |
name | id and name can be specified (separated by commas, semicolons and spaces) |
scope | Scope |
constructor arguments | Specify construction parameters |
properties | Set the value of the property |
autowiring mode | No (default), byName, byType, constructor |
lazy-initialization mode | Whether to load lazily (if it is dependent on non lazily loaded bean s, it cannot be loaded lazily) |
initialization method | This method will be called after the bean property is set |
destruction method | Callback method after bean destruction |
I think everyone is very familiar with the contents in the above table. If you are not familiar with it, you don't know enough about Spring configuration.
Simply put, it's like this:
<bean id="exampleBean" name="name1, name2, name3" class="com.javadoop.ExampleBean" scope="singleton" lazy-init="true" init-method="init" destroy-method="cleanup"> <!-- Construction parameters can be specified in the following three forms --> <constructor-arg type="int" value="7500000"/> <constructor-arg name="years" value="7500000"/> <constructor-arg index="0" value="7500000"/> <!-- property Several cases of --> <property name="beanOne"> <ref bean="anotherExampleBean"/> </property> <property name="beanTwo" ref="yetAnotherBean"/> <property name="integerProperty" value="1"/></bean>
Of course, in addition to the above examples, there are also factory bean, factory method,,,,, which are familiar to you?
With the above knowledge, we will continue to look at how to parse bean elements and how to convert them to BeanDefinitionHolder.
// BeanDefinitionParserDelegate 428
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null);} public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<String>(); // Define the name attribute as comma, semicolon The space "splits" to form an alias list array. / / of course, if you don't define it, it is empty. / / I briefly introduce the configuration of ID and name in the appendix. You can have a look. It takes 20 seconds if (stringutils. Haslength (nameattr)) {string [] namearray = stringutils.tokenizetostringarray (nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases. addAll(Arrays.asList(nameArr)); } String beanName = id; // If no ID is specified, use the first name in the alias list as the beanname if (! Stringutils. Hastext (beanname) & &! aliases. isEmpty()) { beanName = aliases.remove(0); if (logger.isDebugEnabled()) { logger.debug("No XML 'id' specified - using '" + beanName + "' as Bean name and " + aliases + " as aliases"); } } if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // According to < Bean... ></ Create a BeanDefinition according to the configuration in Bean >, and then set all the information in the configuration to the instance. / / see AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean) later for details// Here, even if the parsing of the whole < Bean / > tag is completed, a BeanDefinition is formed. If (BeanDefinition! = null) {/ / if neither ID nor name is set, the beanname will be null. Enter the following code to generate it. / / if the reader is not interested, I don't think I need to care about this code. For the source code analysis of this article, these things are not important if (! Stringutils. Hastext (beanname)) {try {if (containingbean! = null) {/ / according to our idea, containingbean here is null. Beanname = beandefinitionreaderutils. Generatebeanname (BeanDefinition, this. Readercontext. Getregistry(), true);} Else {/ / if we don't define ID and name, the example in the introduction: / / 1. beanName is: com.javadoop.example.messageserviceimpl#0 / / 2. Beanclassname is: com.javadoop.example.messageserviceimpl beanname = this. Readercontext. Generatebeanname (BeanDefinition) ; String beanClassName = BeanDefinition. getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName. length() > beanClassName. length() && ! this. readerContext. getRegistry(). Isbeannameinuse (beanclassname)) {/ / set beanclassname to alias. Add (beanclassname);}} if (logger.isDebugEnabled()) { logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated Bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils. toStringArray(aliases); // Return beandefinitionholder return new beandefinitionholder (BeanDefinition, beanname, aliasesarray);} return null;}
See how to create a BeanDefinition according to the configuration:
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } // Create BeanDefinition and then set class information. It's very simple. No code is pasted. AbstractBeanDefinition bd = createBeanDefinition(className, parent)// Set a bunch of attributes of BeanDefinition, which are defined in parsebeandefinitionattributes (ELE, beanname, containingbean, BD) in AbstractBeanDefinition; bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); /** * The following pile is parsing < bean ></ For the sub elements inside the bean, * the parsed information is put into the BD attribute * / / / parse < meta / > parsemetaelements (ELE, BD)// Parse < lookup method / > parselookupoverridesubelements (ELE, bd.getmethodoverrides())// Resolve < replaced method / > parsereplaced methodsubelements (ELE, bd.getmethodoverrides())// Parse < constructor Arg / > parseconstructorargelements (ELE, BD)// Parse < property / > parsepropertyelements (ELE, BD)// Parse < qualifier / > parsequalifiereelements (ELE, BD); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } return null;}
Here, we have finished creating a BeanDefinitionHolder instance according to the ` ` configuration. Note that it's a.
Let's go back to the entry method of parsing `:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // Convert the < Bean / > node to BeanDefinitionHolder, that is, a bunch of BeanDefinitionHolder bdholder = delegate parseBeanDefinitionElement(ele); If (bdholder! = null) {/ / if there are custom attributes, perform corresponding parsing. First ignore bdholder = delegate.decoratebendefinitionifrequired (ELE, bdholder); try {/ / let's call this step registering Bean bar beandefinitionreaderutils. Registerbeandefinition (bdholder, getreadercontext(). Getregistry());} catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register Bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // After the registration is completed, the event is sent. This article will not expand this getreadercontext() fireComponentRegistered(new BeanComponentDefinition(bdHolder)); }}
Let's take a closer look at this one. We won't talk about this later. Here, an instance of BeanDefinitionHolder has been generated according to a ` ` tag. In this instance, there is an instance of BeanDefinition and its beanName and aliases. Note that our focus is always on BeanDefinition:
public class BeanDefinitionHolder implements BeanMetadataElement { private final BeanDefinition beanDefinition; private final String beanName; private final String[] aliases;...
Then we are ready to register the BeanDefinition. Finally, we send the registration event.
Now, let's start with registering beans.
Register Bean
// BeanDefinitionReaderUtils 143
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { String beanName = definitionHolder.getBeanName(); // Register this Bean registry registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // If there are aliases, we should also register them all according to the aliases, otherwise we can't find the Bean according to the aliases, which makes us unhappy. String [] aliases = definitionholder getAliases(); If (aliases! = null) {for (string alias: aliases) {/ / alias - > beanname saves their alias information. This is very simple. Just save it with a map. / / when you get it, you will first convert alias to beanname, and then find registry.registerAlias(beanName, alias);}}}
Put aside the alias registration. After all, it's very simple. Let's see how to register beans.
// DefaultListableBeanFactory 793
@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(...); } } // old? Remember the "allow Bean overrides" configuration? allowBeanDefinitionOverriding BeanDefinition oldBeanDefinition; // After that, you will see that all beans will be put into the beandefinitionmap after registration. Oldbeandefinition = this beanDefinitionMap. get(beanName); // If (oldbeandefinition! = null) {if (! Isallowbeandefinitionoverriding()) {/ / throw new beandefinitionstoreexception (beandefinition. Getresourcedescription()...} if overriding is not allowed else if (oldBeanDefinition.getRole() < beanDefinition. Getrole()) {/ / log... Overwrite the user-defined Bean with the Bean defined by the framework} else if (! Beandefinition. Equals (oldbeandefinition)) {/ / log... Overwrite the old Bean with the new Bean} else {/ / log... Overwrite the old Bean with the same Bean. This refers to the Bean returned true by the equals method} //Override this beanDefinitionMap. put(beanName, beanDefinition); } Else {/ / judge whether other beans have started initialization. / / note that the action of "register Bean" ends, and the Bean has not yet been initialized. We will talk about the initialization process later. / / at the end of the spring container startup, all singleton beans if (hasbeancreationstarted()) will be pre initialized { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll (this.beanDefinitionNames); updatedDefinitions. add(beanName); this. beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } Else {/ / the most normal way is to enter here. / / put the beandefinition into this map, which saves all beandefinition this.beandefinitionmap.put (beanname, beandefinition) ; // This is an ArrayList, so the name of each registered Bean, this. Is saved in the order of Bean configuration beanDefinitionNames. add(beanName); // This is a linkedhashset, which represents a manually registered singleton bean. / / note that this is the remove method. Of course, the Bean here is not manually registered. / / manually refers to the Bean registered by calling the following method: / / registerSingleton(String beanName, Object singletonObject) / / this is not the point, The explanation is just to avoid confusion. Spring will "manually" register some beans later, such as "environment", "systemProperties" and so on manualSingletonNames. remove(beanName); } // This is not important. It will be used during pre initialization. Don't worry about it. this.frozenBeanDefinitionNames = null; } if (oldBeanDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); }}
To sum up, the Bean container has been initialized here, and the ` ` configuration has been transformed into beandefinitions accordingly. Then, each BeanDefinition has been registered to the registry, and the registration event has been sent.
After Bean container instantiation is completed
At this point, let's go back to the refresh() method, and I'll paste the code again to see where we're talking. Yes, we have just finished the obtainFreshBeanFactory() method.
Considering the length, we begin to greatly reduce the parts that do not need to be introduced in detail. Just look at the comments in the code below.
@Overridepublic void refresh() throws BeansException, IllegalStateException { // Give a lock, otherwise refresh() is not over, and you start or destroy the container again, synchronized (this.startupShutdownMonitor) {/ / preparation. Record the container startup time, mark the "started" status, and handle the placeholder prepareRefresh() in the configuration file ; // This is a key step. After this step is completed, the configuration file will be parsed into Bean definitions and registered in BeanFactory. / / of course, the Beans mentioned here have not been initialized, but the configuration information has been extracted, //Registration only saves these information to the registration center (in the final analysis, the core is a map of beanname - > beandefinition). Configurablelistablebeanfactory BeanFactory = obtainfreshbeanfactory()// Set the class loader of BeanFactory, add several beanpostprocessors, and manually register several special Beans. / / the preparebeanfactory (BeanFactory) will be introduced later; Try {/ / [here you need to know the knowledge of BeanFactory postprocessor. If the Bean implements this interface, / / after the container is initialized, spring will be responsible for calling the postProcessBeanFactory method inside.]// Here is the extension point provided to subclasses. When you get there, all Beans have been loaded and registered, but they have not yet been initialized. / / for specific subclasses, you can add some special implementation classes of beanfactoryprocessor or or do something postProcessBeanFactory(beanFactory)// Call the postProcessBeanFactory(factory) method invokeBeanFactoryPostProcessors(beanFactory) of each implementation class of beanfactoryprocessor// Register the implementation class of BeanPostProcessor. Pay attention to the difference between beanfactoryprocessor and beanfactoryprocessor. / / there are two methods in this interface: postProcessBeforeInitialization and postProcessAfterInitialization. / / the two methods are executed before and after Bean initialization respectively. Note that the Bean has not yet initialized registerBeanPostProcessors(beanFactory)// Initialize the MessageSource of the current ApplicationContext. Internationalization will not be expanded here, otherwise it will be endless initMessageSource()// Initialize the event broadcaster of the current ApplicationContext, and initapplicationeventmulticast() is not expanded here// You can know from the method name that for typical template methods (hook methods), / / specific subclasses can initialize some special Beans here (before initializing singleton beans) onRefresh()// Register the event listener. The listener needs to implement the ApplicationListener interface. This is not our focus, too. We have used registerListeners()// Focus, focus, focus / / initialize all singleton beans / / (except lazy init) finishBeanFactoryInitialization(beanFactory)// Finally, broadcast the event, and the ApplicationContext initialization is completed. 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. // Destroy the initialized singleton beans to avoid that some Beans will always occupy resources destroybeans()// Reset 'active' flag. cancelRefresh(ex); // Throw out the exception throw ex;} finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } }}
Prepare Bean container: prepareBeanFactory
Here is a brief introduction to the prepareBeanFactory(factory) method:
/** * Configure the factory's standard context characteristics, * such as the context's ClassLoader and post-processors. * @param beanFactory the BeanFactory to configure */protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Set the class loader of beanfactory. We know that beanfactory needs to load classes, so it needs a class loader. / / set here as the class loader beanfactory of the current ApplicationContext setBeanClassLoader(getClassLoader()); // Set beanexpressionresolver beanfactory setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); // beanFactory. addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // Add a BeanPostProcessor, which is relatively simple, / / implements several special beans of Aware interface. During initialization, this processor is responsible for calling back beanfactory addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); // The following lines mean that if a bean depends on the implementation classes of the following interfaces and ignores them during automatic assembly, / / Spring will handle these dependencies in other ways. beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); /** * The following lines are to assign values to special beans. If a bean depends on the following, the corresponding values will be injected here. * as we said before, "the current ApplicationContext holds a beanfactory". Here we explain that the first line * ApplicationContext inherits resourceloader, applicationeventpublisher and MessageSource *, It can be assigned as this. Note that this is an ApplicationContext * so why don't you see the assignment for MessageSource here? That's because MessageSource is registered as an ordinary bean * / beanfactory registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory. registerResolvableDependency(ResourceLoader.class, this); beanFactory. registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory. registerResolvableDependency(ApplicationContext.class, this); // The BeanPostProcessor is also very simple. After the bean is instantiated, if it is a subclass of ApplicationListener, / / add it to the listener list, which can be understood as: register the event listener beanfactory addBeanPostProcessor(new ApplicationListenerDetector(this)); // Detect a loadTimeWeaver and prepare for weaving, if found. // This involves a special bean called loadTimeWeaver, which is not our focus, Ignore it if (beanfactory.containsbean (load_time_weaver_bean_name)) {beanfactory.addbeanpostprocessor (New loadtimeweaverawareprocessor (beanfactory)); / / set a temporary classloader for type matching. Beanfactory.settempclassloader (New contexttypematchclassloader (beanfactory. Getbeanclassloader());} / * ** From the following lines of code, we can know that Spring is often "smart" because it will help us register some useful beans by default, * we can also choose to override * / / / if the bean "environment" is not defined, Then Spring will "manually" register an if (! Beanfactory. Containslocalbean (environment_bean_name)) {beanfactory. Registersingleton (environment_bean_name, getenvironment());}// If the bean "systemProperties" is not defined, Spring will "manually" register an if (! Beanfactory. Containslocalbean (system_properties_bean_name)) {beanfactory. Registersingleton (system_properties_bean_name, getenvironment(). Getsystemproperties());}// If the bean "systemEnvironment" is not defined, Spring will "manually" register an if (! Beanfactory. Containslocalbean (system_environment_bean_name)) {beanfactory. Registersingleton (system_environment_bean_name, getenvironment(). Getsystemenvironment());}}
In the above code, Spring handles some special bean s. It doesn't matter if the reader can't digest them for the time being. Look down slowly.
Initialize all singleton beans
Our focus is, of course, finishBeanFactoryInitialization(beanFactory); This giant will be responsible for initializing all singleton beans.
Note that in the following description, I will use initialization or pre initialization to represent this stage. Spring needs to instantiate all singleton beans at this stage.
Let's summarize. So far, it should be said that BeanFactory has been created, and all beans that implement the BeanFactory postprocessor interface have been initialized, and the postProcessBeanFactory(factory) method has been executed. All beans that implement the BeanPostProcessor interface have also completed initialization.
The rest is to initialize other uninitialized singleton beans. We know that they are singleton. If lazy loading is not set, Spring will initialize all singleton beans next.
// AbstractApplicationContext.java 834
// Initialize the remaining singleton beansprotected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {/ / first, initialize the Bean named ConversionService. In the spirit of sending Buddha to the west, I briefly introduce ConversionService in the appendix because it is so practical. / / what, look at the code. There is no initialization Bean! / / note that the initialization action is wrapped in beanFactory.getBean(...) If (beanfactory. Containsbean (conversion_service_Bean_name) & & beanfactory isTypeMatch(CONVERSION_SERVICE_Bean_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_Bean_NAME, ConversionService.class)); } // Register a default embedded value resolver if no Bean post-processor // (such as a PropertyPlaceholderConfigurer Bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(new StringValueResolver() { @Override public String resolveStringValue(String strVal) { return getEnvironment().resolvePlaceholders(strVal); } }); } // First initialize a Bean of loadtimeweaveaware type. / / it is generally used to weave in third-party modules. It is dynamically woven in when the class file is loaded into the JVM. Do not expand here. Say string [] weaveawarenames = beanfactory getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } // Stop using the temporary ClassLoader for type matching. beanFactory. setTempClassLoader(null); // There's no other purpose, because Spring has started pre initializing singleton beans by this step. / / I certainly don't want Bean definition parsing, loading and registration at this time. beanFactory.freezeConfiguration(); // Start initializing the remaining beanfactory preInstantiateSingletons();}
From the last line above, we return to the DefaultListableBeanFactory class, which should be familiar to everyone.
preInstantiateSingletons
// DefaultListableBeanFactory 728
@Overridepublic void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("Pre-instantiating singletons in " + this); } List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames); // Trigger the initialization of all non lazy loaded singleton beans for (string beanname: beannames) {/ / merge the configuration in the parent Bean. Note that the parent in < Bean id = "" class = "" parent = "" / > is not used much. / / considering that this may affect your understanding, I explained "Bean inheritance" in the appendix , please move to RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName)// Non abstract, non lazy loaded singletons. If 'abstract = true' is configured, initialization is not required if (! Bd.isabstract() & & bd.issingleton() & &! Bd.islazyinit()) {/ / process FactoryBean (if the reader is not familiar with FactoryBean, please move to the appendix area to understand) if (isfactorybean (beanname)) {/ / if the FactoryBean is used, add '&' before the beanname. Then call getBean. The getBean method is not urgent. Final FactoryBean <? > factory = (FactoryBean <? >) getBean (FACTORY_Bean_PREFIX + beanName); // Judge whether the current FactoryBean is the implementation of smartfactorybean. Ignore it here and skip the Boolean iseagerinit directly; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { @Override public Boolean run() { return ((SmartFactoryBean<?>) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } Else {/ / ordinary beans can be initialized by calling getBean (beanname);}}}}// This shows that all non lazy loaded singleton beans have been initialized. / / if the Bean we defined implements the smartinitializingsingsingleton interface, we get a callback here, Ignore for (string beanname: beannames) {object singletoninstance = getsingleton (beanname); if (singletoninstance instanceof SmartInitializingSingleton) {final SmartInitializingSingleton smartsingleton = (SmartInitializingSingleton) singletoninstance; if (system. Getsecuritymanager()! = null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { smartSingleton.afterSingletonsInstantiated(); return null; } }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } }}
Next, we will enter the getBean(beanName) method, which is often used to obtain a Bean from BeanFactory, and the initialization process is encapsulated in this method.
getBean
Before moving on, readers should have the knowledge of FactoryBean. If they are not familiar with FactoryBean, please move to the appendix to understand FactoryBean.
// AbstractBeanFactory 196
@Overridepublic Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false);} // We are analyzing the process of initializing beans, but the getBean method is often used to obtain beans from the container. Pay attention to switching ideas, / / after initialization, return directly from the container, Otherwise, initialize it first, and then return @ suppresswarnings ("unchecked") protected < T > t dogetbean (final string name, final class < T > requiredtype, final object [] args, Boolean typecheckonly) throws beansexception {/ / get an "orthodox" beanname, and handle two cases. One is the FactoryBean (with "&" in front) , / / one is the alias problem, because this method is used to get beans. If you pass in an alias, it is completely OK. Final string beanname = transformaedbeanname (name)// Note that this is followed by the return value Object bean// Check whether Object sharedInstance = getSingleton(beanName) has been created// Let's talk about args here, although it doesn't seem important at all. In the past, we all came in getBean(beanName), / / so args is actually null, but if args is not empty, it means that the caller does not want to get the Bean, Instead, create a Bean if (sharedinstance! = null & & args = = null) {if (logger. Isdebuginabled()) {if (issingletoncurrent yincreation (beanname)) {logger. Debug ("...");} else { logger.debug("Returning cached instance of singleton Bean '" + beanName + "'"); } } // The following method: if it is an ordinary Bean, directly return sharedinstance, / / if it is a FactoryBean, return the instance object it created / / (FactoryBean knowledge, please move to the appendix if you don't know) Bean = getobjectforbeaninstance (sharedinstance, name, beanname, null);} Else {if (isprototypecurrentlyincreation (beanname)) {/ / if the current thread has created a prototype Bean of this beanname, throw the exception throw new BeanCurrentlyInCreationException(beanName);}// Check whether the BeanDefinition has beanfactory parentbeanfactory = getparentbeanfactory(); If (parentbeanfactory! = null & &! Containsbeandefinition (beanname)) {/ / if this BeanDefinition does not exist in the current container, try whether there is string nametolookup = originalbeanname (name); if (args! = null) {/ / the query result of the parent container is returned. Return (T) parentbeanfactory.getbean (nameToLookup, args); } else { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } } If (! Typecheckonly) {/ / if typecheckonly is false, put the current beanname into an alreadyCreated Set set. markBeanAsCreated(beanName);} / ** To sum up a little: * at this point, you should be ready to create a Bean. For singleton beans, this Bean has not been created in the container* For a prototype Bean, it is necessary to create a new Bean*/ Try {final rootbeandefinition MBD = getmergedlocalbeandefinition (beanname); checkmergedbeandefinition (MBD, beanname, args); / / initialize all dependent beans first, which is easy to understand. / / note that dependencies here refer to dependencies defined in dependencies on string [] dependson = MBD. Getdependson(); if (dependson! = null) {for (string dep: dependson) {/ / check whether there is a circular dependency. The circular dependency here is different from the circular dependency we mentioned earlier. It is definitely not allowed here. Otherwise, it will be messy. The reader will know if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } // Register the dependency relationship registerDependentBean(dep, beanName)// Initialize the dependent getBean(dep);}}// Create singleton instance if (MBD. Issingleton()) {sharedinstance = getsingleton (beanname, new objectfactory < Object > () {@ override public object getobject() throws beansexception {try {/ / execute Bean creation, and then return createbean (beanname, MBD, args);} catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); Bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // Create an instance of prototype else if (MBD. Isprotype()) {/ / it's a prototype - > create a new instance. Object prototypeinstance = null; try {beforeprototypecreation (beanname); / / create Bean prototypeinstance = createbean (beanname, MBD, args) ; } finally { afterPrototypeCreation(beanName); } Bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } // If it wasn't singleton and prototype, Delegate to the corresponding implementation class to handle 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, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { beforePrototypeCreation(beanName); try {/ / execute Bean return createbean (beanname, MBD, args);} finally { afterPrototypeCreation(beanName); } } }); Bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this Bean if you intend to refer to it from a singleton", ex) ; } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // Finally, check whether the type is correct. If it is wrong, throw an exception. If it is correct, return if (requiredtype! = null & & Bean! = null & &! Requiredtype. Isinstance (Bean)) {try {return gettypeconverter(). Convertifnecessary (Bean, requiredtype);} catch (TypeMismatchException ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to convert Bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, Bean.getClass()); } } return (T) Bean;}
As you can guess, the next step is to analyze the createBean method:
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException;
The third parameter args array represents the parameters required to create an instance, which is not the parameters used for the construction method or the parameters of the factory Bean. However, it should be noted that args is null in our initialization phase.
This time we are going to a new class AbstractAutowireCapableBeanFactory. Look at the class name, AutowireCapable? Does the class name also explain some problems.
The @ Autowired annotation is mainly used to inject attribute values for the following scenarios:
public class MessageServiceImpl implements MessageService { @Autowired private UserService userService; public String getMessage() { return userService.getMessage(); }} <bean id="messageService" class="com.javadoop.example.MessageServiceImpl" />
Well, readers need to know that. Just move on.
// AbstractAutowireCapableBeanFactory 447
/** * Central method of this class: creates a bean instance, * populates the bean instance, applies post-processors, etc. * @see #doCreateBean */@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean '" + beanName + "'"); } RootBeanDefinition mbdToUse = mbd; // Ensure that the Class in the BeanDefinition is loaded Class <? > resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd. getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } // Preparing method overrides involves another concept: MethodOverrides, which comes from < lookup method / > / / and < replaced method / > in the bean definition. If the reader is interested, go back to the bean parsing place to see the parsing of these two tags// I also introduced the relevant knowledge points of these two tags in the appendix. Readers can take a step-by-step look at try {mbdtouse. Preparemethodoverrides();} catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } Try {/ / let the beanpostprocessor have the opportunity to return the proxy instead of the bean instance in this step. / / to fully understand this, you need to look at the instantiaawarebeanpostprocessor interface. Don't expand here. Object bean = resolvebeforeinstance (beanname, mbdtouse); if (bean! = null) {return bean;}} catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } // Highlight: create bean object beaninstance = docreatebean (beanname, mbdtouse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } return beanInstance;}
Create Bean
Look inside at the doCreateBean method:
/** * Actually create the specified bean. Pre-creation processing has already happened * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks. * <p>Differentiates between default bean instantiation, use of a * factory method, and autowiring a constructor. * @param beanName the name of the bean * @param mbd the merged bean definition for the bean * @param args explicit arguments to use for constructor or factory method invocation * @return a new instance of the bean * @throws BeanCreationException if the bean could not be created * @see #instantiateBean * @see #instantiateUsingFactoryMethod * @see #autowireConstructor */protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) {/ / the description is not a FactoryBean. The bean is instantiated here, which is very important. After the details, instanceWrapper = createBeanInstance(beanName, mbd, args);}// This is the instance of the class we defined in bean. In many places, I describe it as "bean instance" final object bean = (instancewrapper! = null? Instancewrapper. Getwrappedinstance(): null)// Type class <? > beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); mbd. resolvedTargetType = beanType; // I suggest you skip it, Interface involved: mergedbeandefinitionpostprocessor synchronized (MBD. Postprocessinglock) {if (! MBD. Postprocessed) {try {/ / mergedbeandefinitionpostprocessor. I won't expand this. Just skip it. Rarely used applymergedbeandefinitionpostprocessors (MBD, beantype, beanname);} catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd. postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. // The following code is to solve the problem of circular dependency. I will analyze the problem of circular dependency later. Boolean earlysingletonexposure = (MBD. Issingleton() & & this allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } // Initialize the bean instance. Object exposedObject = bean; Try {/ / this step is also very critical. This step is responsible for property assembly, because the previous instance is only instantiated without setting a value. Here is the set value populatebean (beanname, MBD, instancewrapper); if (exposedobject! = null) {/ / remember the init method? The InitializingBean interface? The BeanPostProcessor interface? / / here are the callbacks that handle bean initialization. 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<String>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example. "); } } } } // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject;}
So far, we have analyzed the doCreateBean method. In general, we have finished the whole initialization process.
Next, let's pick out three details in doCreateBean. One is the createBeanInstance method for creating Bean instances, the other is the populateBean method for dependency injection, and the other is the callback method initializeBean.
Note that the next three methods are extremely complex. I'll stop at many places. Interested readers can look inside by themselves. It's best to write their own code to debug what they don't understand.
Create Bean instance
Let's first look at the createBeanInstance method. It should be noted that if each branch is analyzed, this method must be extremely complex and lengthy. Let's pick the point. The purpose of this method is to instantiate the class we specify.
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { // Make sure this class <? > beanClass = resolveBeanClass(mbd, beanName); // Check the access permission of this class 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()); } if (mbd.getFactoryMethodName() != Null) {/ / factory method instantiation is adopted. Readers unfamiliar with this concept can see the appendix. Note that it is not factorybean return instantiateusingfactorymethod (beanname, MBD, args);}// If it is not created for the first time, for example, the prototype bean is created for the second time// In this case, we can know from the first creation whether to use parameterless constructor or constructor dependency injection to complete instantiation. 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) {/ / constructor dependency injection return autowireconstructor (beanname, MBD, null, null);} Else {/ / parameterless constructor return instantiateBean(beanName, mbd);}}// Judge whether to use the constructor with parameters <? > [] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition. AUTOWIRE_ CONSTRUCTOR || mbd. hasConstructorArgumentValues() || ! ObjectUtils. Isempty (args)) {/ / constructor dependency injection return autowireConstructor(beanName, mbd, ctors, args);}// Call the parameterless constructor return instantiateBean(beanName, mbd);}
Take a simple nonparametric constructor construction example to see:
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { try { Object beanInstance; final BeanFactory parent = this; if (System.getSecurityManager() != null) { beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { return getInstantiationStrategy().instantiate(mbd, beanName, parent); } }, getAccessControlContext()); } else { // Instantiate beaninstance = getinstantiationstrategy() instantiate(mbd, beanName, parent); } // Wrap it and return beanwrapper BW = new beanwrapperimpl (beaninstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); }}
We can see that the key points are:
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
The actual instantiation process will be carried out here. Let's go in and have a look:
// SimpleInstantiationStrategy 59
@Overridepublic Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) { // If there is no method override, use java reflection for instantiation. Otherwise, use CGLIB, / / for method override, see the introduction if (bd.getmethodoverrides()) of lookup method and replaced method in the appendix "method injection" isEmpty()) { Constructor<?> constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class<?> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() { @Override public Constructor<?> run() throws Exception { return clazz.getDeclaredConstructor((Class[]) null); } }); } else { constructorToUse = clazz.getDeclaredConstructor((Class[]) null); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } // Instantiate return BeanUtils using the construction method instantiateClass(constructorToUse); } Else {/ / there is a method override. CGLIB is used to complete instantiation. You need to rely on CGLIB to generate subclasses. return instantiateWithMethodInjection(bd, beanName, owner) is not expanded here;}}
At this point, we're done instantiating. Let's start with how to inject attributes.
bean attribute injection
// AbstractAutowireCapableBeanFactory 1203
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { // All properties of the bean instance are here propertyvalues PVS = MBD getPropertyValues(); if (bw == null) { if (!pvs.isEmpty()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { // Skip property population phase for null instance. return; } } // At this step, the bean instantiation is completed (through the factory method or construction method), but the property setting has not started yet. / / the implementation class of instantiaawarebeanpostprocessor can modify the state of the bean here. / / I can't find any practical use, so let's ignore this one for the time being. Boolean continuewithpropertypopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // If false is returned, it means that no subsequent property setting or processing by other beanpostprocessor is required. If (! IBP. Postprocessafterinstance (BW. Getwrappedinstance(), beanname)) {continuewithpropertypopulation = false; break;}}}}} if (!continueWithPropertyPopulation) { return; } if (mbd.getResolvedAutowireMode() == RootBeanDefinition. AUTOWIRE_ BY_ NAME || mbd. getResolvedAutowireMode() == RootBeanDefinition. AUTOWIRE_ BY_ Type) {mutablepropertyvalues newpvs = new mutablepropertyvalues (PVS); / / find all property values by name. If it is a bean dependency, initialize the dependent bean first. Record the dependency if (MBD. Getresolvedautowiremode() = = rootbeandefinition. Autowire_by_name) {autowirebyname (beanname, MBD, BW, newpvs);}// Assemble by type. More complicated if (MBD. Getresolvedautowiremode() = = rootbeandefinition AUTOWIRE_ BY_ TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition. DEPENDENCY_ CHECK_ NONE); if (hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // Here is a very useful beanpostprocessor: AutowiredAnnotationBeanPostProcessor / / set the dependency with @ Autowired and @ Value annotations. The content here is also very rich, but this article will not expand. Interested readers please study PVS = IBP postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); } } // Set the property Value of bean instance applyPropertyValues(beanName, mbd, bw, pvs);}
initializeBean
After the property injection is completed, this step is actually dealing with various callbacks.
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { // If the bean implements the BeanNameAware, BeanClassLoaderAware or beanfactory aware interfaces, call back invokeawaremethods (beanname, bean);} Object wrappedBean = bean; If (MBD = = null |! MBD. Issynthetic()) {/ / postProcessBeforeInitialization callback of beanpostprocessor wrappedbean = applybeanpopostprocessorsbeforeinitialization (wrappedbean, beanname);} Try {/ / process the init method defined in the bean, / / or if the bean implements the InitializingBean interface, call the afterpropertieset() method invokeinitialmethods (beanname, wrappedbean, MBD);} catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } If (MBD = = null |! MBD. Issynthetic()) {/ / postProcessAfterInitialization callback of beanpostprocessor wrappedbean = applybeanpostprocessorsafterinitialization (wrappedbean, beanname);} return wrappedBean;}
Have you found that the two callbacks of the BeanPostProcessor occur here, but the init method is processed in the middle. Is it a little different from the original cognition of the readers?
appendix
id and name
Each Bean has a unique name and 0 or more aliases in the Spring container.
When we get beans from the Spring container, we can use beanName or alias.
beanFactory.getBean("beanName or alias");
In the process of configuring ` ` we can configure id and name. We can see what's going on with a few examples.
<bean id="messageService" name="m1, m2, m3" class="com.javadoop.example.MessageServiceImpl">
The result of the above configuration is that beanName is messageService and there are three aliases, m1, m2 and m3 respectively.
<bean name="m1, m2, m3" class="com.javadoop.example.MessageServiceImpl" />
The result of the above configuration is that beanName is m1 and there are 2 aliases, m2 and m3 respectively.
<bean class="com.javadoop.example.MessageServiceImpl">
beanName: com javadoop. example. MessageServiceImpl#0,
1 alias: com javadoop. example. MessageServiceImpl
<bean id="messageService" class="com.javadoop.example.MessageServiceImpl">
The result of the above configuration is that beanName is messageService and there is no alias.
Configure whether Bean overrides and circular dependencies are allowed
As we said, by default, the allowBeanDefinitionOverriding property is null. If the Bean id or name is repeated in the same configuration file, an error will be thrown, but if it is not in the same configuration file, an overwrite will occur.
However, sometimes we want to strictly prevent Bean coverage during system startup, because in case of such a situation, it will increase the cost of troubleshooting.
Circular dependency means that A depends on B, and B depends on A. Or A depends on B, B depends on C, and C depends on A. The default allowCircularReferences is also null.
The two attributes appear together and can be configured together in the same place.
The author Juergen Hoeller who added these two properties in this jira How to configure these two properties is explained in the discussion of.
public class NoBeanOverridingContextLoader extends ContextLoader { @Override protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) { super.customizeContext(servletContext, applicationContext); AbstractRefreshableApplicationContext arac = (AbstractRefreshableApplicationContext) applicationContext; arac.setAllowBeanDefinitionOverriding(false); }} public class MyContextLoaderListener extends org.springframework.web.context.ContextLoaderListener { @Override protected ContextLoader createContextLoader() { return new NoBeanOverridingContextLoader(); } } <listener> <listener-class>com.javadoop.MyContextLoaderListener</listener-class> </listener>
If the above methods can not meet your needs, please refer to this link: Solve the problems that may be caused by bean s with the same name or id in different configuration files in spring
profile
We can configure the configurations of different environments into separate files, for example:
<beans profile="development" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xsi:schemaLocation="..."> <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/> </jdbc:embedded-database></beans> <beans profile="production" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="..."> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/></beans>
There should be no need to explain too much. Look at the profile = "" on the first line of each file.
Of course, we can also use it in a configuration file:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="..."> <beans profile="development"> <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/> </jdbc:embedded-database> </beans> <beans profile="production"> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> </beans></beans>
It's easy to understand.
The next question is, how to use a specific profile? During the startup process, Spring will look for the attribute value of "spring.profiles.active", and it will be based on this attribute value. How do you configure this value?
Spring will look for spring in these places profiles. Active attribute values: operating system environment variable, JVM system variable, web Parameters defined in XML, JNDI.
The simplest way is to specify when the program starts:
-Dspring.profiles.active="profile1,profile2"
profile can activate multiple
Of course, we can also set the profile from the Environment in the form of code:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();ctx.getEnvironment().setActiveProfiles("development");ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);ctx.refresh(); // restart
If it's Spring Boot, it's easier. We usually create an application Properties, application-dev.properties, application-prod.properties, etc., where application Properties configure the configuration common to each environment, application - {profile} Configure the configuration of a specific environment in properties, and then specify the profile at startup:
java -Dspring.profiles.active=prod -jar JavaDoop.jar
If it is used in unit testing, use @ ActiveProfiles to specify it in the test class. It will not be expanded here.
Factory pattern generation Bean
Please pay attention to the difference between factory Bean and FactoryBean. This section refers to the former, which refers to static factory or instance factory, while the latter is a special interface in Spring and represents a special class of beans. The following section of the appendix will introduce factorybeans.
In the design pattern, the factory method pattern is divided into static factory and instance factory. Let's take a look at how to configure these two in Spring, and we'll know everything by a code example.
Static plant:
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/> public class ClientService { private static ClientService clientService = new ClientService(); private ClientService() {} // Static method public static clientservice createinstance() {return clientservice;}}
Example factory:
<bean id="serviceLocator" class="examples.DefaultServiceLocator"> <!-- inject any dependencies required by this locator bean --></bean> <bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/> <bean id="accountService" factory-bean="serviceLocator" factory-method="createAccountServiceInstance"/> public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private static AccountService accountService = new AccountServiceImpl(); public ClientService createClientServiceInstance() { return clientService; } public AccountService createAccountServiceInstance() { return accountService; }}
FactoryBean
FactoryBean is applicable to scenes where the creation process of Bean is complex, such as the creation of database connection pool.
public interface FactoryBean<T> { T getObject() throws Exception; Class<T> getObjectType(); boolean isSingleton();} public class Person { private Car car ; private void setCar(Car car){ this.car = car; } }
Let's assume that we need to create a Person Bean. First, we need an instance of Car. Let's assume that the creation of Car instance is troublesome, so we can package the complex process of creating Car:
public class MyCarFactoryBean implements FactoryBean<Car>{ private String make; private int year ; public void setMake(String m){ this.make =m ; } public void setYear(int y){ this.year = y; } public Car getObject(){ // Here, we assume that the instantiation process of car is very complex. Anyway, it is not the kind of car builder CB = car builder that can be written in a few lines of code car(); if(year!=0) cb. setYear(this.year); if(StringUtils.hasText(this.make)) cb. setMake( this.make ); return cb. factory(); } public Class<Car> getObjectType() { return Car.class ; } public boolean isSingleton() { return false; }}
Let's see how it is configured during assembly:
<bean class = "com.javadoop.MyCarFactoryBean" id = "car"> <property name = "make" value ="Honda"/> <property name = "year" value ="1984"/></bean><bean class = "com.javadoop.Person" id = "josh"> <property name = "car" ref = "car"/></bean>
See the difference? The bean with id "car" actually specifies a FactoryBean, but when configuring, we can directly make the bean configuring Person directly depend on this FactoryBean. The middle process has been encapsulated by Spring.
At this point, let's have some dry goods. We know that there are fewer and fewer dependencies on configuring beans with xml. More often, we may use java config to configure beans. What's the difference here?
@Configuration public class CarConfiguration { @Bean public MyCarFactoryBean carFactoryBean(){ MyCarFactoryBean cfb = new MyCarFactoryBean(); cfb.setMake("Honda"); cfb.setYear(1984); return cfb; } @Bean public Person aPerson(){ Person person = new Person(); // Notice the different person here setCar(carFactoryBean(). getObject()); return person; } }
At this time, in fact, our idea is also very simple. Just regard MyCarFactoryBean as a simple Bean. We don't have to pay attention to any FactoryBean. Whether it is a FactoryBean has nothing to do with us.
Initialize callback for Bean
There are four options:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/> public class AnotherExampleBean implements InitializingBean { public void afterPropertiesSet() { // do some initialization work }} @Bean(initMethod = "init")public Foo foo() { return new Foo();} @PostConstructpublic void init() { }
Callback to destroy Bean
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/> public class AnotherExampleBean implements DisposableBean { public void destroy() { // do some destruction work (like releasing pooled connections) }} @Bean(destroyMethod = "cleanup")public Bar bar() { return new Bar();} @PreDestroypublic void cleanup() { }
ConversionService
Since this is mentioned in the article, by the way.
The most useful scenario is when it is used to bind the parameters passed from the front end to the parameters on the back-end controller method.
For example, it is easy to convert strings and integers passed from the front end to strings and integers from the back end. However, if the controller method needs an enumeration value or non basic type (including basic type wrapper class) values such as Date, we can consider using ConversionService for conversion.
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="com.javadoop.learning.utils.StringToEnumConverterFactory"/> </list> </property></bean>
The ConversionService interface is very simple, so it is also very simple to customize a convert.
Another simple way to implement this conversion is to implement the Converter interface.
Let's take a very simple example. It works better than anything.
public class StringToDateConverter implements Converter<String, Date> { @Override public Date convert(String source) { try { return DateUtils.parseDate(source, "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "HH:mm:ss", "HH:mm"); } catch (ParseException e) { return null; } }}
Just register the Bean. In this way, the time description string passed from the front end to the back end can be easily bound into Date type without any other operation.
Bean inheritance
We said this when initializing beans:
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
The parent attribute in ` ` is involved here. Let's see what Spring uses this for.
First of all, we should understand that inheritance here has nothing to do with inheritance in java syntax, but the ideas are the same. The child bean inherits all the configurations of the parent bean, and can override some configurations. Of course, additional configurations can also be added.
Spring provides ChildBeanDefinition inherited from AbstractBeanDefinition to represent child bean s.
Take the following example:
<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean"> <property name="name" value="parent"/> <property name="age" value="1"/></bean> <bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBean" init-method="initialize"> <property name="name" value="override"/></bean>
The parent bean is set with abstract="true", so it will not be instantiated. The child bean inherits the two properties of the parent bean, but overrides the name property.
Child beans inherit scope, constructor parameter values, attribute values, init method, destroy method, and so on.
Of course, I'm not saying that abstract = true in the parent bean is necessary here, but that if it is added, Spring will ignore this bean when instantiating singleton beans in the future.
For example, the following extreme parent bean does not specify a class, so there is no doubt that the function of this bean is to act as a parent bean for templates. Here, abstract = true must be added.
<bean id="inheritedTestBeanWithoutClass" abstract="true"> <property name="name" value="parent"/> <property name="age" value="1"/></bean>
Method injection
Generally speaking, most beans in our applications are singleton. Singleton depends on singleton, or prototype depends on prototype. You can directly set the attribute dependency.
But what if singleton relies on prototype? Attribute dependency cannot be used at this time, because if attribute dependency is used, we actually get the bean at the first initialization every time.
One solution is not to use attribute dependency. Each time you get the dependent bean, you get it from BeanFactory. This is also the most commonly used way. I won't introduce how to get it. Most Spring projects will define such a tool class.
Another solution is introduced here by using the Lookup method.
lookup-method
Let's take a look at an example provided in the Spring Reference:
package fiona.apple; // no more Spring imports! public abstract class CommandManager { public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method? protected abstract Command createCommand();}
xml configuration ` `:
<!-- a stateful bean deployed as a prototype (non-singleton) --><bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype"> <!-- inject dependencies here as required --></bean> <!-- commandProcessor uses statefulCommandHelper --><bean id="commandManager" class="fiona.apple.CommandManager"> <lookup-method name="createCommand" bean="myCommand"/></bean>
Spring uses CGLIB to generate bytecode to generate a subclass. The class we define cannot be defined as final class, and final cannot be added to abstract methods.
The configuration on the lookup method can also be completed by annotation, so that the configuration ` ` can not be used, and others remain unchanged:
public abstract class CommandManager { public Object process(Object commandState) { MyCommand command = createCommand(); command.setState(commandState); return command.execute(); } @Lookup("myCommand") protected abstract Command createCommand();}
Note that since annotations are used, annotation scanning should be configured:``
Even, we can do something like this:
public abstract class CommandManager { public Object process(Object commandState) { MyCommand command = createCommand(); command.setState(commandState); return command.execute(); } @Lookup protected abstract MyCommand createCommand();}
The above return value uses MyCommand. Of course, if Command has only one implementation class, the return value can also be written to Command.
replaced-method
Remember that its function is to replace some methods in the bean.
public class MyValueCalculator { public String computeValue(String input) { // some real code... } // some other methods...}
For method override, pay attention to implementing the methodreplace interface:
public class ReplacementComputeValue implements org.springframework.beans.factory.support.MethodReplacer { public Object reimplement(Object o, Method m, Object[] args) throws Throwable { // get the input value, work with it, and return a computed result String input = (String) args[0]; ... return ...; }}
The configuration is also simple:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator"> <!-- definition computeValue This method needs to be replaced --> <replaced-method name="computeValue" replacer="replacementComputeValue"> <arg-type>String</arg-type> </replaced-method></bean> <bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
Arg type is obviously not necessary, unless there is a method overload, so you must judge which method to override here through the parameter type list.
BeanPostProcessor
It should be said that the concept of BeanPostProcessor is also important in Spring. Let's look at the interface definition:
public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
Looking at the names of the two methods in this interface, we can guess that the bean will execute the postProcessBeforeInitialization method before initialization, and the postProcessAfterInitialization method after initialization. However, this understanding is very one-sided.
First of all, we should understand that in addition to our own defined BeanPostProcessor implementation, the Spring container automatically adds several to us at startup. For example, in prepareBeanFactory(factory) after obtaining the obtainFactory() method of BeanFactory, you will find that Spring has added these two beanpostprocessors to the container: ApplicationContextAwareProcessor and ApplicationListenerDetector.
Let's go back to the interface itself. Please look at the first method. The first parameter accepted by this method is the bean instance, and the second parameter is the bean name. The focus is that the return value will be used as a new bean instance. Therefore, if it's okay, you can't return null here.
What does that mean? It's easy to think that we can do something here for some bean instances we want to modify. But for the Spring framework, it will decide whether to return the proxy of the bean instance in this method, so there is more room for imagination.
Finally, if we define a bean to implement BeanPostProcessor, when will it be executed?
If you look at the code analysis carefully, it is easy to know that the callback method will be executed after bean instantiation and attribute injection. For details, see class AbstractAutowireCapableBeanFactory#initBean method.
First, several beans implementing Aware interface will be called back, and then the postProcessBeforeInitialization method of BeanPostProcessor will be called back, followed by init method, and then the postProcessAfterInitialization method of BeanPostProcessor.
summary
Normally, the summary should be written in front of the appendix, so I don't pay attention to it.
After spending so much time, this article has finally been basically completed. When everyone is amazed that Spring has done so many things for us, we should look at the essence through the phenomenon, understand what Spring has written well, and understand its design idea.
The defect of this paper lies in the insufficient analysis of the process of Spring pre initializing singleton beans, mainly because the amount of code is really large and there are many branch bypasses. At the same time, although there are many appendix items, the huge Spring really leads to a lot of concepts. I hope I can add some slowly in the future.
last:
I think there may be many people who keep a wait-and-see situation in the past four spring moves of gold, silver and silver this year. They are afraid that their ability is not enough, or they are content with the status quo. They think they can accept it with thousands of monthly salary, so you should pay attention. This is very dangerous!
As technicians, we are most afraid of being content with the status quo and staying where we are. Then you may usher in your own career crisis at the age of 30, because you work so long and only improve your age, and your technology will remain unchanged for thousands of years!
If you want to make a breakthrough and realize your dream in the future, maybe you need to read the above Java learning materials, hoping to help your career development.
How to get it: just * * like + follow * * and enter [Java architecture resource exchange group] , find the administrator to get Oh -!
Method. For details, see class AbstractAutowireCapableBeanFactory#initBean method.
First, several beans implementing Aware interface will be called back, and then the postProcessBeforeInitialization method of BeanPostProcessor will be called back, followed by init method, and then the postProcessAfterInitialization method of BeanPostProcessor.
summary
Normally, the summary should be written in front of the appendix, so I don't pay attention to it.
After spending so much time, this article has finally been basically completed. When everyone is amazed that Spring has done so many things for us, we should look at the essence through the phenomenon, understand what Spring has written well, and understand its design idea.
The defect of this paper lies in the insufficient analysis of the process of Spring pre initializing singleton beans, mainly because the amount of code is really large and there are many branch bypasses. At the same time, although there are many appendix items, the huge Spring really leads to many concepts. I hope I can add some slowly in the future.
last:
I think there may be many people who keep a wait-and-see situation in the past four spring moves of gold, silver and silver this year. They are afraid that their ability is not enough, or they are content with the status quo. They think they can accept it with thousands of monthly salary, so you should pay attention. This is very dangerous!
As technicians, we are most afraid of being content with the status quo and staying where we are. Then you may usher in your own career crisis at the age of 30, because you work so long and only improve your age, and your technology will remain unchanged for thousands of years!
If you want to make a breakthrough and realize your dream in the future, maybe you need to read the above Java learning materials, hoping to help your career development.
How to get it: just * * like + follow * * and enter [Java architecture resource exchange group] , find the administrator to get Oh -!