Spring startup process (1)

    The startup process of Spring is the startup process of its IoC container. The essence is the factory (BeanFactory) that creates and initializes beans. The BeanFactory is actually the core of the entire SpringIoc. Spring uses the BeanFactory to instantiate, configure and manage beans.

    For a web program, the IoC container startup process is the process of establishing a context. In a web application, the web container provides a global ServletContext context, which provides a host environment for Spring IoC.

    The whole process of starting a Spring application in a web container is as follows:

    

First, let's introduce the friends who appear in the source code:

  • Resource: It is an abstraction of resources by spring, because the sources of resources may be very rich, which is conducive to the unified encapsulation of File, Class Path Resource, Url Resource, etc., exposing getInputStream for unified reading and parsing
  • Document: This is nothing to talk about, XML document object
  • EncodedResource: Encapsulates the Resource, specifying the encoding of the Resource
  • ReaderContext: The context object in the Bean Definition parsing process, which encapsulates Resource, ProblemReporter, EventListener, SourceExtractor, etc.
  • Element: element node object in XML
  • BeanDefinition: This interface and its implementation class are very important. It describes all the properties of a bean node and its child nodes in XML, and converts the description in XML into internal field information, for example: scope, lazyinit, ConstructorArgumentValues ​​(description constructor ), PropertyValues ​​(describes property values), etc., is a Paul Vientiane interface, and its subclass implementations include GenericBeanDefinition, RootBeanDefinition, ChildBeanDefinition, etc.
  • BeanDefinitionHolder: As the name implies, it contains a BeanDefinition, and it contains beanName and aliases, which is better encapsulated once
  • BeanDefinitionReader: defines the interface for reading BeanDefinition, the main function is to read Bean definitions from Resource, XmlBeanDefinitionReader is its specific implementation class
  • BeanDefinitionDocumentReader: Defines the interface designed to parse BeanDefinition from the Document object and register it in the Registry. Its default implementation class is DefaultBeanDefinitionDocumentReader, which is mainly delegated by XmlBeanDefinitionReader to process Document objects
  • BeanDefinitionParserDelegate: When you see Delegate, you know that this buddy is a suffering object and a person who finally works. The official definition is "Stateful delegate class used to parse XML bean definitions.* Intended for use by both the main parser and any extension* { @ link BeanDefinitionParser BeanDefinitionParsers} or* { @link BeanDefinitionDecorator BeanDefinitionDecorators}." is the person who is used to finally process the XML bean definition, it can do all the dirty work, import/alias/bean and other elements and element's child nodes and attributes It is parsed and populated into the BeanDefinition and then uses the Registry in the ReaderContext (actually DefaultListableBeanFactory) to register the BeanDefinition

 

Using Spring in web projects is done by configuring in web.xml:

org.springframework.web.context.ContextLoaderListener to initialize the IOC container.

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

To be precise, the listener object of ContextLoaderListener listens to the ServletContext. When the web container is initialized and the ServletContext changes, corresponding events will be triggered.

public class ContextLoaderListener extends ContextLoader implements ServletContextListener

ContextLoaderListener inherits ContextLoader and implements the ServletContextListener interface. When the web container is initialized, the contextInitialized() method in the ServletContextListener interface will be triggered. Similarly, when the container is closed, the corresponding contextDestroyed() method will be triggered.

Among them, the initWebApplicationContext() method is the method in the parent class ContextLoader:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext)

Since the source code of the initWebApplicationContext() method is long, we split it for reading.

if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
	throw new IllegalStateException(
			"Cannot initialize context because there is already a root application context present - " +
			"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
//日志记录
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
	logger.info("Root WebApplicationContext: initialization started");
}
//启动startTime,记录启动耗时
long startTime = System.currentTimeMillis();

First determine whether a WebApplicationContext is created. Normally, after a WebApplicationContext is created, the context is set to the ServletContext. The essence of setAttribute is actually to set the value in LinkedHashMap.

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

If the WebApplicationContext object is obtained from the servletContext according to the key, it means that the root context WebApplicationContext has been created before, and an exception is thrown at this time, prompting to check the configuration in web.xml, to avoid repeated creation of the root context, and to ensure that there is only one Spring container.

Look at the following code again:

try {
	// Store context in local instance variable, to guarantee that
	// it is available on ServletContext shutdown.
	if (this.context == null) {
		this.context = createWebApplicationContext(servletContext);
	}
	if (this.context instanceof ConfigurableWebApplicationContext) {
		ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
		if (!cwac.isActive()) {
			// The context has not yet been refreshed -> provide services such as
			// setting the parent context, setting the application context id, etc
			if (cwac.getParent() == null) {
				// The context instance was injected without an explicit parent ->
				// determine parent for root web application context, if any.
				ApplicationContext parent = loadParentContext(servletContext);
				cwac.setParent(parent);
			}
			configureAndRefreshWebApplicationContext(cwac, servletContext);
		}
	}
	servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
	ClassLoader ccl = Thread.currentThread().getContextClassLoader();
	if (ccl == ContextLoader.class.getClassLoader()) {
		currentContext = this.context;
	}
	else if (ccl != null) {
		currentContextPerThread.put(ccl, this.context);
	}
	if (logger.isDebugEnabled()) {
		logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
				WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
	}
	if (logger.isInfoEnabled()) {
		long elapsedTime = System.currentTimeMillis() - startTime;
		logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
	}
	return this.context;
}

The code snippet is long, step by step, there are two key places in try:

createWebApplicationContext()和configureAndRefreshWebApplicationContext()

Look down step by step

The first is to judge this.context == null and create one through the createWebApplicationContext method

WebApplicationContext, the specific code is as follows:

	protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		Class<?> contextClass = determineContextClass(sc);
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

determineContextClass, which literally means to detect the class type of Context, will read the initialization parameter contextClass of servletContext. In most cases, we will not configure this parameter. If it is not configured, Spring will go to the org.springframework.web.context package. The ContextLoader.properties configuration file reads the default configuration:

It is reflected through the ClassUtil provided by Spring, reflects the XmlWebApplicationContext class, and then reflects through BeanUtils, calls the no-parameter constructor, instance out a WebApplicationContext and returns the ConfigurableWebApplicationContext.

if (this.context instanceof ConfigurableWebApplicationContext) {
	ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
	if (!cwac.isActive()) {
		// The context has not yet been refreshed -> provide services such as
		// setting the parent context, setting the application context id, etc
		if (cwac.getParent() == null) {
			// The context instance was injected without an explicit parent ->
			// determine parent for root web application context, if any.
			ApplicationContext parent = loadParentContext(servletContext);
			cwac.setParent(parent);
		}
		configureAndRefreshWebApplicationContext(cwac, servletContext);
	}
}

After obtaining the WebApplicationContext, the next step is to determine whether the obtained context is

An instance of ConfigurableWebApplicationContext, the default XmlWebApplicationContext meets the judgment conditions.

Determine whether the parent context is active. If active is false, you need to determine whether the parent context is null. If the parent context is null, execute loadParentContext(). The loadParentContext() method is a default template implementation method used to load/get ApplicationContext, which is the parent context of the root WebApplicationContext.

Next, go to configureAndRefreshWebApplicationContext

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc)

Initialize the XmlWebApplicationContext created above.

if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
	// The application context id is still set to its original default value
	// -> assign a more useful id based on available information
	String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
	if (idParam != null) {
		wac.setId(idParam);
	}
	else {
		// Generate default id...
		wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
				ObjectUtils.getDisplayString(sc.getContextPath()));
	}
}

Mainly create a default id, org.springframework.web.context.WebApplicationContext:+ project'sContextPath

//设置sc到wac中,便于Spring获得ServletContext
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
	wac.setConfigLocation(configLocationParam);
}
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
	((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
wac.refresh();

After creating the id, put the servletContext into the created XmlWebApplicationContext, so that spring can obtain the servletContext context later.

Then it will read the configuration of <param-name>contextConfigLocation</param-name> in web.xml

If it is not configured in web.xml, it will read the applicationContext.xml configuration file under WEB-INF,

That is, the default value of the attribute DEFAULT_CONFIG_LOCATION in the XmlWebApplicationContext

/** Default config location for the root context */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

After reading the configuration path of the contextConfigLocation related file, set it to the XmlWebApplicationContext to load the configuration file of the specified path.

Next customizeContext(sc, wac);

It is mainly used to customize the context-related configuration, such as defining whether the bean can be referenced circularly, whether the bean definition can be overridden, etc. Usually, no operation is performed.

last step:

wac.refresh();

The core method of starting the entire container, in this refresh() method, will complete the core tasks such as resource file loading, configuration file parsing, bean definition registration, and component initialization.

The relevant logic of the refresh() method will continue to be studied in the next article .

 

 

Reference link: https://www.jianshu.com/p/375ef7095139

 

Guess you like

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