Spring framework source code learning (1) BEAN loading of IOC

The main idea of ​​Spring is reflected in IOC and AOP; this article is some records of LZ himself when he is looking at the source code of Spring. You can also follow this idea to take a look at some source code of Spring and understand its principle from the main line;
The main line of this article: it is the process in which Spring generates a BeanFactory by loading a configuration file;
This is the basis for implementing IOC, reading configuration, and generating BeanFactory. So I don't care about some small details or classes that are not important to the main line, otherwise it will be very confusing to look at the source code. Because the inheritance system is complex, there are many methods for subclasses to reuse the parent class, and there will be more jumps in each class. Now from the source code, how Spring implements IOC;
IOC is to hand over the creation and dependencies of objects to the container, that is, to reverse the creation and dependencies of the object BEAN to the framework;
The following example is a small method that we generally write when testing Spring:
public class MainApp {、
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
}
There are also the following forms to generate BeanFactory
BeanFactory beanFactory=newFileSystemXmlApplicationContext("classpath:applicationContext.xml");

       The above ApplicationContext is a subclass of BeanFactory, so its essence is the same; so our main line is to use this FileSystemXmlApplicationContext class to see how it loads and configures the container for generating beans.

 

First of all, we need to introduce each important class or interface:
BeanFactory class source code:
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
boolean containsBean(String name); boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException;
Class<?> getType(String name) throws NoSuchBeanDefinitionException; String[] getAliases(String name);
}
BeanFactory is the top-level interface, also known as the ancestor of the IOC container. Various IOC containers are just its implementation or extended implementation to meet special needs, including the ApplicationContext that we use the most. As can be seen from the above methods, the biggest role of the implementation of these factories is to return an instance of a bean according to the name or type of the bean, etc.
BeanDefinition interface
   As the name suggests, this is the bean definition interface in spring, so in fact, the bean definition held in our factory is a bunch of this or its implementation classes and sub-interfaces. This interface is not a direct ancestor interface. One of the two interfaces it inherits is AttributeAccessor under core. Inheriting this interface means that our bean definition interface also has the ability to process attributes, and the other is BeanMetadataElement under beans, literally translated This interface is the bean's metadata element, which can obtain an element of the bean's configuration definition. In the XML file, it will hold a bean tag.
Knowing BeanFactory and BeanDefinition, one is the core factory interface of IOC, and the other is the bean definition interface of IOC. As mentioned in the previous chapter, we cannot let BeanFactory hold a Map<String, Object> to complete the function of bean factory, because spring The initialization of the bean can be controlled, and the bean can be instantiated for developers to use when it is used, unless we set the bean's lazy-init property to true, and use lazy loading when initializing the bean factory.
          Knowing the above two interfaces, I believe that many people have already guessed how spring does it without even looking at the source code. That's right, let the bean factory hold a Map<String, BeanDefinition>, so that whenever we want to use which bean, get its bean definition, we can create a fresh instance.
          Of course, it is impossible for an interface to hold such an object, so this object must be held in an implementation class or abstract implementation class of BeanFactory, see DefaultListableBeanFactory.
The DefaultListableBeanFactory class is a very important class;
Its source code holds a beanDefinitionMap to store each bean read in the configuration. The various overloaded methods of its getBean are used to read data from the map, and then return or instantiate it into an object through a series of complex judgments to return the real Bean. This is a follow-up matter, so don't look at it now.
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
Seeing this Map, it should be clear about everything, of course, Let's take a look at how Spring implements the configuration initialization into a BeanFactory from the perspective of code.
1. Starting from the FileSystemXmlApplicationContext class, I recorded the main method and recorded the context of the source code. In its construction method, a Bean container is generated through the incoming configuration document path, and the refresh() method is executed.
The refresh() method is specifically implemented in In AbstractApplicationContext , the current class is inherited. Spring's inheritance system is very complicated, but it can be found through the inheritance relationship and class diagram. The refresh source code is as follows:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
 
prepareRefresh();
 
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
prepareBeanFactory(beanFactory);
try {
 
postProcessBeanFactory(beanFactory);
 
invokeBeanFactoryPostProcessors(beanFactory);
 
registerBeanPostProcessors(beanFactory);
 
initMessageSource();
 
initApplicationEventMulticaster();
 
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
 
finally {
resetCommonCaches();
}
}
}
Continue to follow up to see the obtainFreshBeanFactory() method, the specific implementation is still In the AbstractApplicationContext class, to get the updated BeanFactory, this method should update the beans in the original container according to the new configuration, and then return to the BeanFactory;
as follows:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
refreshBeanFactory() in the AbstractRefreshableApplicationContext class; update the beanFactory and then get the beanFactory(), and then see how to update the container;
in its subclasses The specific implementation in AbstractRefreshableApplicationContext is as follows:
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(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);
}
}
If there is a beanFactory, destroy it first, then clear the reference; then createBeanFactory(); create a beanFactory, then make some settings and load BeanDefinitions(). Now it seems that the most important thing is how to load BeanDefinition. It is an instance of which BeanFactory class is created, and then subdivides each method;
What is returned in the createBeanFactory method is an instance of DefaultListableBeanFactory, and then this instance is filled;
beanFactory.setSerializationId(getId()); The method is to PUT this instance into the static MAP;
The specific implementation of loadBeanDefinitions is in AbstractXmlApplicationContext.java, which is the parent class of FileSystemXmlApplicationContext. The following is the source code:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
 
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
Tracking code, finally implement loadBeanDefinitions(EncodedResource encodedResource) in XmlBeanDefinitionReader class is the loading implementation;
 
Finally, load beanDefinition in the following method
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
 
With this class DefaultBeanDefinitionDocumentReader.java implements the registerBeanDefinitions method, which calls
doRegisterBeanDefinitions(root) method to load DOM files; custom and default files can be loaded in subsequent methods;
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(it);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(he);
}
}
Node elements are divided into four categories: import, alias, bean, nested_beans
 
In the BeanDefinitionReaderUtils.java class, the BEAN configuration is loaded. Let's see how it is implemented as follows;
 
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
 
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
 
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
 
The registry is actually the previous beanFactory instance;
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
Implement the registration of the BeanDefinition corresponding to the beanName into the Beanfactory;
The implementation of this method is in DefaultListableBeanFactory.java, and this beanFactory implements many important methods;
The final bean is placed in the MAP as follows;
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
Therefore, as analyzed above, in an application, there can be multiple beanFactory, and there is a MAP in a beanFactory to store BEAN, the beanName cannot be repeated, and it can be configured to override the bean;

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326310762&siteId=291194637