IOC container initialization overview
IOC container initialization is started by the refresh() method, which marks the official start of IOC container. Spring separates the IOC container startup process and uses different modules to complete it, such as ResourceLoader, BeanDefinition and other modules. The IOC container startup mainly includes three processes:
- Resource location process:
Resource positioning refers to the resource positioning of beandefinition, which is completed by ResourceLoader through a unified resource interface. This resource provides a unified interface for the use of various forms of beandefinition. This process is similar to the process of the container looking for data.
- Loading of BeanDefinition:
The loading process of BeanDefinition refers to representing the user-defined beans as the data structure inside the container. The data structure of the container is actually BeanDefinition. In fact, BeanDefinition is the abstraction of POJO objects in the container. The data structure defined by BeanDefinition is like the abstraction of everything in the world in java, and the abstraction of java objects in the container is BeanDefinition.
- Register BeanDefinition with container:
This registration process is completed by calling the BeanDefinitionRegistry interface, which is to register the BeanDefinition parsed during loading with the IOC container. We can get from the following source code that the registration process is to inject BeanDefinition into a HashMap in the IOC container, and the IOC container holds BeanDefinition data through this HashMap.
Write the initialization of an IOC container by yourself
//Create the abstract resource of IOC configuration file, which contains the definition information of BeanDefinition ClassPathResource resource = new ClassPathResource("bean.xm"); //Create a bean factory DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); //Create a reader, where XmlBeanDefinitionReader is used to load the Bean factory in the form of XML file XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); //Xmlbean factory uses reader to read configuration information reader.loadBeanDefinitions(resource);
1. Resource positioning of BeanDefinition
When using the defaultlistablebeanfactory programmatically above, you first need to define a Resource to locate the container BeanDefinition. Defaultlistablebeanfactory cannot directly use resources. It needs a BeanDefinition to process these information. In ApplicationContext, Spring has provided the implementation of a series of different Resource readers.
Common Resource types are as follows:
- FileSystemResource: access resources in the absolute path of a File, and the effect is similar to that of File in Java;
- Classpathresource: access resources by classpath. The effect is similar to this getClass(). getResource("/"). getPath();
- ServletContextResource: web application accesses resources in the way of root directory. The effect is similar to request getServletContext(). getRealPath("");
- UrlResource: implementation class for accessing network resources. For example, resource objects with prefixes such as file: http: ftp:;
- ByteArrayResource: implementation class for accessing byte array resources
Let's specifically describe the positioning process of BeanDefinition taking FileSystemXmlApplicationContext as an example.
1. Class relationship of FileSystemXmlApplicationContext
This is a graph:
From the above inheritance relationship, FileSystemXmlApplicationContext inherits from AbstractApplicationContext. The base class of AbstractApplicationContext is DefaultResourceLoader, which inherits the capabilities of ResourceLoader.
First, let's take a look at the specific implementation of FileSystemXmlApplicationContext
public FileSystemXmlApplicationContext() { } public FileSystemXmlApplicationContext(ApplicationContext parent) { super(parent); } public FileSystemXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } public FileSystemXmlApplicationContext(String... configLocations) throws BeansException { this(configLocations, true, null); } public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException { this(configLocations, true, parent); } public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException { this(configLocations, refresh, null); } public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } } @Override protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }
The refresh method is invoked in the construction method. This method marks the initialization of the IOC container. We point in and go to the base class AbstractApplicationContext of FileSystemXmlApplicationContext.
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
This Refresh method is actually related to the Bean life cycle. I haven't looked at the Bean declaration cycle here. I'll explain it later. Here, focus on the obtainFreshbeanFactory() method, which is the entry for IOC container initialization.
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
The refreshBeanFactory method is invoked in the obtainFreshBeanFactory method. This method is abstract. The concrete implementation is based on the specific conditions of its subclasses. As we are now looking at the FileSystemXmlApplicationContext process, we enter the refreshBeanFactory method in the AbstractFreshableApplicationContext class.
@Override protected final void refreshBeanFactory() throws BeansException { //Here, you can judge whether there is a BeanFactory. If there is, destroy and close the BeanFactory if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } //Here to create a new Beanfactory, the DefaultListableBeanFactory is created try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //Load Bean loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
Continue to click into the loadbean definition method, which has many overloads.
The loadBeanDefinition method clicked in is also an abstract method, which needs to be viewed in the implementation class. We still choose the AbstractXmlApplicationContext class related to FileSystemXmlApplicationContext
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { //Create a configuration reader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //Set relevant properties of BeanDefinition beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); //Incoming reader initBeanDefinitionReader(beanDefinitionReader); //Load get BeanDefinition location loadBeanDefinitions(beanDefinitionReader); }
Continue clicking in the loadBeanDefinition method
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //Initialization will not go one way because there is no Resource object yet Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } //Get as a String file String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
Click in the second loadBeanDefinition method.
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int counter = 0; String[] var3 = locations; int var4 = locations.length; //Loop load profile for(int var5 = 0; var5 < var4; ++var5) { String location = var3[var5]; counter += this.loadBeanDefinitions(location); }
The loadBeanDefinition method continues to click in to view the loading process
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return this.loadBeanDefinitions(location, (Set)null); } public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = this.getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } else { int loadCount; if (!(resourceLoader instanceof ResourcePatternResolver)) { Resource resource = resourceLoader.getResource(location); loadCount = this.loadBeanDefinitions((Resource)resource); if (actualResources != null) { actualResources.add(resource); } if (this.logger.isDebugEnabled()) { this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } else { try { //Get the specific location of the Resource Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location); loadCount = this.loadBeanDefinitions(resources); if (actualResources != null) { Resource[] var6 = resources; int var7 = resources.length; for(int var8 = 0; var8 < var7; ++var8) { Resource resource = var6[var8]; actualResources.add(resource); } } if (this.logger.isDebugEnabled()) { this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); } return loadCount; } catch (IOException var10) { throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10); } } } }
Take a direct look at the getResource method of DefaultResourceLoader
public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); Iterator var2 = this.protocolResolvers.iterator(); Resource resource; do { if (!var2.hasNext()) { if (location.startsWith("/")) { return this.getResourceByPath(location); } if (location.startsWith("classpath:")) { return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader()); } try { URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException var5) { return this.getResourceByPath(location); } } ProtocolResolver protocolResolver = (ProtocolResolver)var2.next(); resource = protocolResolver.resolve(location, this); } while(resource == null); return resource; }
If the Resource is not a Url or classpath Resource, call the getResourceByPath method of FileSystemXmlApplicationContext to return a FileSystemResource and locate the Resource. The next step is to load BeanDefinition