首页 > 代码库 > spring容器启动原理分析1
spring容器启动原理分析1
在项目的web.xml中配置
1 <listener> 2 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 3 </listener>
此配置为spring容器加载入口,因为其javax.servlet.ServletContextListener接口。
下面代码为ServletContextListener的源码:
public interface ServletContextListener extends EventListener { /** ** Notification that the web application initialization ** process is starting. ** All ServletContextListeners are notified of context ** initialization before any filter or servlet in the web ** application is initialized. */ public void contextInitialized ( ServletContextEvent sce ); /** ** Notification that the servlet context is about to be shut down. ** All servlets and filters have been destroy()ed before any ** ServletContextListeners are notified of context ** destruction. */ public void contextDestroyed ( ServletContextEvent sce ); }
其中contextInitialized方法为web应用加载入口,实现此方法即可在其中加载自定义初始化web应用。
回过头看ContextLoaderListener中的初始化工作:
1 /** 2 * Initialize the root web application context. 3 */ 4 public void contextInitialized(ServletContextEvent event) { 5 this.contextLoader = createContextLoader(); 6 if (this.contextLoader == null) { 7 this.contextLoader = this; 8 } 9 this.contextLoader.initWebApplicationContext(event.getServletContext()); 10 }
其具体初始化工作在其父类org.springframework.web.context.ContextLoader中的initWebApplicationContext方法中进行:
/** * Initialize Spring‘s web application context for the given servlet context, * using the application context provided at construction time, or creating a new one * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params. * @param servletContext current servlet context * @return the new WebApplicationContext * @see #ContextLoader(WebApplicationContext) * @see #CONTEXT_CLASS_PARAM * @see #CONFIG_LOCATION_PARAM */ public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { 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"); } long startTime = System.currentTimeMillis(); 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; } 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; } }
下面我们来分析一下上面的方法中的具体工作:
1)方法determineContextClass其返回一个WebApplicationContext的实现类,要么是默认的XmlWebApplicationContext,要么是一个指定的自定义的类。
先看方法源码:
1 /** 2 * Return the WebApplicationContext implementation class to use, either the 3 * default XmlWebApplicationContext or a custom context class if specified. 4 * @param servletContext current servlet context 5 * @return the WebApplicationContext implementation class to use 6 * @see #CONTEXT_CLASS_PARAM 7 * @see org.springframework.web.context.support.XmlWebApplicationContext 8 */ 9 protected Class<?> determineContextClass(ServletContext servletContext) { 10 String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); 11 if (contextClassName != null) { 12 try { 13 return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); 14 } 15 catch (ClassNotFoundException ex) { 16 throw new ApplicationContextException( 17 "Failed to load custom context class [" + contextClassName + "]", ex); 18 } 19 } 20 else { 21 contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); 22 try { 23 return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); 24 } 25 catch (ClassNotFoundException ex) { 26 throw new ApplicationContextException( 27 "Failed to load default context class [" + contextClassName + "]", ex); 28 } 29 } 30 }
先查看是否有用户自定义的context类,其配置方式是在web.xml中配置初始化属性其name为contextClass,原因是:
/** * Config param for the root WebApplicationContext implementation class to use: {@value} * @see #determineContextClass(ServletContext) * @see #createWebApplicationContext(ServletContext, ApplicationContext) */ public static final String CONTEXT_CLASS_PARAM = "contextClass";
在web.xml中的配置例如:
<context-param> <param-name>contextClass</param-name> <param-value>xxxxxxxxxx</param-value> </context-param>
如果没有自定义配置的话,实现加载其默认WebApplicationContext实现类org.springframework.web.context.support.XmlWebApplicationContext。
2)方法createWebApplicationContext将determineContextClass方法返回的Class类实例化。并将其实例类向上转型为ConfigurableWebApplicationContext类型。
默认的返回应该是ConfigurableWebApplicationContext的子类org.springframework.web.context.support.XmlWebApplicationContext实例。
下面是方法的源码:
1 /** 2 * Instantiate the root WebApplicationContext for this loader, either the 3 * default context class or a custom context class if specified. 4 * <p>This implementation expects custom contexts to implement the 5 * {@link ConfigurableWebApplicationContext} interface. 6 * Can be overridden in subclasses. 7 * <p>In addition, {@link #customizeContext} gets called prior to refreshing the 8 * context, allowing subclasses to perform custom modifications to the context. 9 * @param sc current servlet context 10 * @return the root WebApplicationContext 11 * @see ConfigurableWebApplicationContext 12 */ 13 protected WebApplicationContext createWebApplicationContext(ServletContext sc) { 14 Class<?> contextClass = determineContextClass(sc); 15 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 16 throw new ApplicationContextException("Custom context class [" + contextClass.getName() + 17 "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); 18 } 19 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 20 }
3)方法configureAndRefreshWebApplicationContext配置web应用
源码为:
1 protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { 2 if (ObjectUtils.identityToString(wac).equals(wac.getId())) { 3 // The application context id is still set to its original default value 4 // -> assign a more useful id based on available information 5 String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); 6 if (idParam != null) { 7 wac.setId(idParam); 8 } 9 else { 10 // Generate default id... 11 if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { 12 // Servlet <= 2.4: resort to name specified in web.xml, if any. 13 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + 14 ObjectUtils.getDisplayString(sc.getServletContextName())); 15 } 16 else { 17 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + 18 ObjectUtils.getDisplayString(sc.getContextPath())); 19 } 20 } 21 } 22 23 wac.setServletContext(sc); 24 String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); 25 if (initParameter != null) { 26 wac.setConfigLocation(initParameter); 27 } 28 customizeContext(sc, wac); 29 wac.refresh(); 30 }
其中String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);获取在web.xml中配置的contextConfigLocation参数来确定spring配置文件的位置。
其中的ConfigurableWebApplicationContext类型参数实际上是其子类org.springframework.web.context.support.XmlWebApplicationContext的类型参数,
其中方法customizeContext是定制化conext的方法,如果没有在web.xml中配置contextInitializerClasses初始化参数则此方法不做任何工作。
其中wac.refresh();方法其是在org.springframework.web.context.support.XmlWebApplicationContext的父类org.springframework.context.support.AbstractApplicationContext中实现。它才开始真正的spring配置工作。
spring容器启动原理分析1