1. Introduction
In the previous article, I introduced you to how Spring MVC handles HTTP requests. When Spring MVC can provide services externally, it indicates that it is already in a ready state. Again, Spring MVC requires a series of initialization operations. The so-called soldiers do not move, fodder first. These operations include creating containers, loading various components used in Dispatcherservlet, and so on. This article will discuss with you the container creation in these initialization operations, the creation of containers is the basis for some other initialization process. The others don't say much, let's go straight to the subject.
2. Container creation Process
In general, we will configure two containers in a WEB application. A container for loading the classes of the WEB layer, such as our interface Controller, handlermapping, Viewresolver, and so on. In this article, we call this container a Web container. Another container is used to load classes related to business logic, such as service, DAO layers. In this article, we call this container a business container. During container initialization, the business container is initialized before the Web container. When the Web container is initialized, the business container is used as the parent container. The reason for this is that some beans in the Web container will depend on the beans in the business container. For example, our controller layer interface typically relies on the service layer's business logic classes. Here is an example to illustrate:
As above, we configure the class of the DAO layer in the Application-dao.xml file to configure the service layer class in the Application-service.xml file. We then import the two configuration files into the Application.xml file via tags. At this point, we can let the business container to load the Application.xml configuration file. On the other hand, we put the Web-related configuration in the Application-web.xml file and gave the file to the Web container to load.
Here we layered the configuration file, which looks much clearer in structure and is easy to maintain. This is actually a code layering is a reason, if we put all the code in the same package, it seems to be more uncomfortable ah. Similarly, we use the business container and the WEB container to load different classes is also a layering of the embodiment of it. Of course, if the application is relatively simple, it is not necessary to load all classes with only the Web container.
2.1 The creation process of the business container
With some background knowledge as a cushion, let's start by analyzing the container creation process. In the order of creation, we will first analyze the process of creating a business container. The creation entry for the business container is the Contextinitialized method of the Contextloaderlistener. As the name implies, Contextloaderlistener is used to listen for ServletContext loading events. When ServletContext is loaded, the listener's Contextinitialized method is called by the Servlet container. The Contextloaderlistener Spring Framework provides the following configuration methods:
<web-app> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:application.xml</param-value> </context-param> <!-- 省略其他配置 --></web-app>
As above, the Contextloaderlistener can be obtained by ServletContext to the contextconfiglocation configuration. This allows the business container to load the Application.xml configuration file. Then let's analyze the source of Contextloaderlistener.
public class Contextloaderlistener extends Contextloader implements Servletcontextlistener {//Omit part of code @Override public void contextinitialized (Servletcontextevent event) {//Initialize Webapplicationcontext Initwebapplicationc Ontext (Event.getservletcontext ()); }}public webapplicationcontext Initwebapplicationcontext (ServletContext servletcontext) {/* * if ServletContext Root_web_application_context_attribute Property value * is not empty, indicates that another listener has set this property. Spring does not think that it is possible to set the property value of * for another listener, so this throws an exception. */if (Servletcontext.getattribute (webapplicationcontext.root_web_application_context_attribute) = null) {thro W New IllegalStateException ("Cannot initialize context because there is already a root application context Present-"+" check whether you had multiple contextloader* definitions in your web.xml! ");} Log logger = Logfactory.getlog (Contextloader.class); ServletContext.log ("Initializing Spring root Webapplicationcontext "); if (logger.isinfoenabled ()) {...} Long startTime = System.currenttimemillis (); try {if (This.context = = null) {//create webapplicationcontext This.context = Createwebapplica Tioncontext (ServletContext); } if (This.context instanceof configurablewebapplicationcontext) {configurablewebapplicationcontext CWA c = (configurablewebapplicationcontext) this.context; if (!cwac.isactive ()) {if (cwac.getparent () = = null) {/* * * Load Parent APP Licationcontext, in general, the business container will not have a parent container, * unless configured */Applicationconte XT parent = Loadparentcontext (ServletContext); Cwac.setparent (parent); }//Configure and Refresh Webapplicationcontext configureandrefreshwebapplicationcontext (CWAC, Servletconte XT); }}//Set ApplicationContext to 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 ()) {...} if (logger.isinfoenabled ()) {...} return this.context; } catch (RuntimeException ex) {logger.error ("Context initialization Failed", ex); Servletcontext.setattribute (Webapplicationcontext.root_web_application_context_attribute, ex); Throw ex; } catch (Error err) {logger.error ("Context initialization Failed", err); Servletcontext.setattribute (Webapplicationcontext.root_web_application_context_attribute, err); throw err; }}
As above, let's look at the creation process above. First Spring detects if the Root_web_application_context_attribute attribute in ServletContext is set and throws an exception if it is set. If not set, the Createwebapplicationcontext method is called to create the container. Once created, call the Configureandrefreshwebapplicationcontext method to configure and refresh the container. Finally, call the SetAttribute method to set the container to ServletContext. After the steps above, the entire creation process is over. The process is not complex and can be summarized simply as create container → configure and refresh container → set container to ServletContext
. In this three-step process, the final step is not analyzed, next analysis of the first step and the second process corresponding source code. as follows:
Protected Webapplicationcontext Createwebapplicationcontext (ServletContext SC) {//Determine what type of container to create, default type is Xmlwebapplicationcontext class<?> contextclass = Determinecontextclass (SC); if (! ConfigurableWebApplicationContext.class.isAssignableFrom (Contextclass)) {throw new Applicationcontextexception ("C Ustom Context class ["+ Contextclass.getname () +"] is not of type ["+ Configurablewebapplicationcontext. Class.getname () + "]"); }//Create container return via Reflection (Configurablewebapplicationcontext) Beanutils.instantiateclass (Contextclass);} Protected class<?> Determinecontextclass (ServletContext servletcontext) {/* * Read user-defined configuration such as: * <contex t-param> * <param-name>contextClass</param-name> * <param-value>xxxconfigwebapplicati oncontext</param-value> * </context-param> * * String contextclassname = Servletcontext.getinitparam Eter (Context_class_param); if (contextclassname! = null) { try {return classutils.forname (Contextclassname, Classutils.getdefaultclassloader ()); } catch (ClassNotFoundException ex) {throw new Applicationcontextexception ("Failed To load custom context class ["+ Contextclassname +"] ", ex); }} else {/* * If there is no custom configuration, get the default container type, the default type is Xmlwebapplicationcontext. * Defaultstrategies Read the configuration file is contextloader.properties, * The configuration file contents are as follows: * Org.springframework.web.context.WebA Pplicationcontext = * Org.springframework.web.context.support.XmlWebApplicationContext * */Conte Xtclassname = Defaultstrategies.getproperty (WebApplicationContext.class.getName ()); try {return classutils.forname (Contextclassname, ContextLoader.class.getClassLoader ()); } catch (ClassNotFoundException ex) {throw new Applicationcontextexception ("Failed To load default context class ["+Contextclassname + "]", ex); } }}
Simply say the flow of the Createwebapplicationcontext method, which first calls Determinecontextclass to determine what type of container to create, default to Xmlwebapplicationcontext. The Instantiateclass method is then called to create a container instance by reflection. Instantiateclass method does not follow in the analysis, we can go to see their own, relatively simple.
Continue to analyze, then analyze the source code of the Configureandrefreshwebapplicationcontext method. As follows:
protected void Configureandrefreshwebapplicationcontext (Configurablewebapplicationcontext WAC, ServletContext SC) { if (Objectutils.identitytostring (WAC). Equals (Wac.getid ())) {//Gets the ContextID property of the user configuration from ServletContext strin G Idparam = Sc.getinitparameter (Context_id_param); if (idparam! = null) {//Set container ID wac.setid (idparam); } else {//user is not configured with ContextID, set a default container ID Wac.setid (configurablewebapplicationcontext.applica Tion_context_id_prefix + objectutils.getdisplaystring (Sc.getcontextpath ())); }} wac.setservletcontext (SC); Get contextconfiglocation configuration String configlocationparam = Sc.getinitparameter (Config_location_param); if (Configlocationparam! = null) {wac.setconfiglocation (Configlocationparam); } configurableenvironment env = Wac.getenvironment (); if (env instanceof configurablewebenvironment) {((configurablewebenvironment) env). initpropertysources (SC, NULL); } customizecontext (SC, WAC); Refresh Container Wac.refresh ();}
The above source code is not very long, logic is not very complex. The following is a brief summary of the Configureandrefreshwebapplicationcontext method of doing the main things, as follows:
- Set Container ID
- Get the Contextconfiglocation configuration and set it to the container
- Refresh Container
In this case, the process of creating a business container has been analyzed, and we continue to analyze the creation of the Web container.
2.2 The creation process of the WEB container
Earlier, the business container was created, and the business container was passed Contextloaderlistener. What is the Web container created from? The answer is through Dispatcherservlet. When I introduced the Httpservletbean abstract class in my previous article, I said that the class covered the Init method in the parent class HttpServlet. This method is to create the portal of the Web container, and we'll start with this method. As follows:
-☆-org.springframework.web.servlet.HttpServletBeanpublic final void init () throws Servletexception {if (LOGGER.ISD Ebugenabled ()) {...} Get configuration information in ServletConfig propertyvalues PVs = new Servletconfigpropertyvalues (Getservletconfig (), This.requiredproper Ties); if (!pvs.isempty ()) {try {/* * * Creates a beanwrapper for the current object, such as a Dispatcherservlet object), * Easy to read/write Object properties. */Beanwrapper BW = propertyaccessorfactory.forbeanpropertyaccess (this); Resourceloader Resourceloader = new Servletcontextresourceloader (Getservletcontext ()); Bw.registercustomeditor (Resource.class, New Resourceeditor (Resourceloader, Getenvironment ())); Initbeanwrapper (BW); Sets the configuration information to the target object in Bw.setpropertyvalues (PVs, true); } catch (Beansexception ex) {if (logger.iserrorenabled ()) {...} Throw ex; }}//For subsequent initialization of Initservletbean (); if (Logger.isdebuGenabled ()) {...}} protected void Initservletbean () throws Servletexception {}
The above source of the main thing is to set the configuration information in the ServletConfig to Httpservletbean sub-class objects (such as Dispatcherservlet), we did not find from the above source of the creation of the container traces. However, if you look at the source code, you will find Initservletbean This method slightly strange, is an empty method. The access level for this method is protected, which can be overridden by subclasses. Httpservletbean sub-Class Frameworkservlet This method, let's explore in Frameworkservlet.
-☆-org.springframework.web.servlet.FrameworkServletprotected final void Initservletbean () throws Servletexception {getservletcontext (). log ("Initializing Spring frameworkservlet" + getservletname () + "'"); if (this.logger.isInfoEnabled ()) {...} Long startTime = System.currenttimemillis (); try {//Initialize container This.webapplicationcontext = Initwebapplicationcontext (); Initframeworkservlet (); } catch (Servletexception ex) {this.logger.error ("Context initialization Failed", ex); Throw ex; } catch (RuntimeException ex) {this.logger.error ("Context initialization Failed", ex); Throw ex; } if (this.logger.isInfoEnabled ()) {...}} Protected Webapplicationcontext Initwebapplicationcontext () {//Get container from ServletContext, that is, container created by Contextloaderlistener Webapplicationcontext Rootcontext = Webapplicationcontextutils.getwebapplicationcontext (GetServletContext () ); Webapplicationcontext WAC = null; /* * If the following conditions are true, you will need to set webapplicationcontext from outside. There are two ways to set * Webapplicationcontext, take Dispatcherservlet for example: * 1. The Webapplicationcontext Object * 2 is passed through the Dispatcherservlet method. Configure Dispatcherservlet to other containers, set by other containers through the * Setapplicationcontext method * * Route 1 refer to Abstractdispatch Erservletinitializer in the * Registerdispatcherservlet method source code. In general, the code executes here, * this.webapplicationcontext is NULL, and you can debug it yourself for verification. */if (this.webapplicationcontext! = null) {WAC = This.webapplicationcontext; if (WAC instanceof Configurablewebapplicationcontext) {Configurablewebapplicationcontext CWAC = (ConfigurableW Ebapplicationcontext) WAC; if (!cwac.isactive ()) {if (cwac.getparent () = = null) {//Set Rootcontext as parent container Cwac.setparent (Rootcontext); }//configure and Refresh the container configureandrefreshwebapplicationcontext (CWAC); }}} if (WAC = = null) {//try to get the container from ServletContext WAC = Findwebapplicationcontext (); } if (WAC = = null) {//Create container and Rootcontext as parent container WAC = Createwebapplicationcontext (Rootcontext); } if (!this.refresheventreceived) {Onrefresh (WAC); } if (This.publishcontext) {String attrname = Getservletcontextattributename (); Set the created container to ServletContext getservletcontext (). SetAttribute (Attrname, WAC); if (this.logger.isDebugEnabled ()) {...} } return WAC;} Protected Webapplicationcontext Createwebapplicationcontext (applicationcontext parent) {//Get container type, default to Xmlwebapplicationcontext.class class<?> contextclass = Getcontextclass (); if (this.logger.isDebugEnabled ()) {...} if (! ConfigurableWebApplicationContext.class.isAssignableFrom (Contextclass)) {throw new Applicationcontextexception ( "Fatal initialization error in Servlets with Name '" + getservletname () + "': cusTom Webapplicationcontext class ["+ Contextclass.getname () +"] is not of type Configurablewebapplicationc Ontext "); }//Instantiate the container by reflection configurablewebapplicationcontext WAC = (configurablewebapplicationcontext) beanutils.inst Antiateclass (Contextclass); Wac.setenvironment (Getenvironment ()); Wac.setparent (parent); Wac.setconfiglocation (Getcontextconfiglocation ()); Configure and refresh the container configureandrefreshwebapplicationcontext (WAC); return WAC;} protected void Configureandrefreshwebapplicationcontext (Configurablewebapplicationcontext WAC) {if ( Objectutils.identitytostring (WAC). Equals (Wac.getid ())) {//Set container ID if (This.contextid! = null) { Wac.setid (This.contextid); } else {//Generate default ID Wac.setid (configurablewebapplicationcontext.application_context_id_prefix + objectutils.getdisplaystring (Getservletcontext (). Getcontextpath ()) + '/' + getservletname ()); } } Wac.setservletcontext (Getservletcontext ()); Wac.setservletconfig (Getservletconfig ()); Wac.setnamespace (GetNamespace ()); Wac.addapplicationlistener (New Sourcefilteringlistener (WAC, New Contextrefreshlistener ())); Configurableenvironment env = wac.getenvironment (); if (env instanceof configurablewebenvironment) {((configurablewebenvironment) env). Initpropertysources (GetServletC Ontext (), Getservletconfig ()); }//post-processing, subclasses can override some of the custom actions. Not used in Spring MVC, is an empty method. Postprocesswebapplicationcontext (WAC); Applyinitializers (WAC); Refresh Container Wac.refresh ();}
The above is the creation of the Web container source code, the following summarizes the process of the container creation. As follows:
- Get the container created by Contextloaderlistener from ServletContext
- If this.webapplicationcontext! = NULL condition is set, only the parent container and refresh container can be
- Attempt to get a container from ServletContext, no need to perform step 4 if the container is not empty
- Create a container and use Rootcontext as the parent container
- Set the container to ServletContext
Here, the process of creating a Web container is finished. In general, the process of creating a Web container is roughly the same as the creation of a business container, but there are differences that cannot be ignored.
3. Summary
This article provides a detailed analysis of the creation process of the Spring MVC two containers, in general, the creation process of the two containers is not very complex. When we analyze the creation of these two containers, it is very helpful to understand the logic of the code to see where it is not understood. Of course, reading the source code for the Spring MVC section is best based on the knowledge of the Servlet and spring IOC containers, which is the basis on which spring MVC is built.
Limited to personal ability, the article is wrong to describe, but also hope you specify. Please give me a lot of advice, thank you here. Well, this article is here. Thank you for reading.
Reference
- "See Spring MVC"-Han Lubiao
Appendix: Spring Source Analysis article List Ⅰ. IOC
Update Time |
title |
2018-05-30 |
Spring IOC Container Source Analysis Series Articles Guide |
2018-06-01 |
Spring IOC Container Source code Analysis-Get singleton bean |
2018-06-04 |
Spring IOC Container Source code Analysis-the process of creating a singleton bean |
2018-06-06 |
Spring IOC Container Source code Analysis-Creating Raw bean objects |
2018-06-08 |
Spring IOC Container Source Analysis-a solution for cyclic dependencies |
2018-06-11 |
Spring IOC Container Source code Analysis-fill attribute to bean original object |
2018-06-11 |
Spring IOC Container Source analysis-the rest of the initialization work |
Ⅱ. AOP
Update Time |
title |
2018-06-17 |
Spring AOP Source Analysis Series Articles Guide |
2018-06-20 |
Spring AOP Source Analysis-Filter the appropriate notifier |
2018-06-20 |
Spring AOP Source Analysis-Create proxy objects |
2018-06-22 |
Spring AOP Source Analysis-The execution process of the interceptor chain |
Ⅲ. MVC
Update Time |
title |
2018-06-29 |
Spring MVC Principle Quest-a request for a travel process |
2018-06-30 |
Spring MVC Principle Quest-Container creation process |
This article is published under the Knowledge Sharing License Agreement 4.0, the reprint must indicate the source in the obvious position
Coolblog.xyz
This document is posted in my personal blog: http://www.coolblog.xyz
This work is licensed under the Creative Commons Attribution-NonCommercial use-no derivative of the 4.0 International License Agreement.
Spring MVC Principle Quest-Container creation process