天天看點

自我分析-Spring IOC在Web應用的啟動和銷毀

  Spring IOC容器通過ServletContextListener對servlet容器的生命周期監聽,進而實作了IOC的啟動和銷毀。

    注意:

1.分析架構代碼時,要常使用類繼承、調用關系等快捷鍵,可以更高效的學習,快捷鍵可以設定成你習慣的按鍵;

2.本文重在怎麼自我分析架構代碼,是以對其中解析需自己實際跟蹤代碼實踐方可;

3.spring源代碼版本 spring-framework-3.2.1.RELEASE。

預覽

javax.servlet.ServletContext,Servlet容器接口。

javax.servlet.ServletContextListener,Servlet容器生命周期監聽接口。

org.springframework.web.context.ContextLoaderListener,Spring IOC容器生命周期監聽類。

org.springframework.web.context.ContextLoader,Spring IOC容器啟動和銷毀。

配置-監聽ServletContext生命周期

在web.xml中spring配置了對ServletContext生命周期的監聽,當Web容器啟動和銷毀時,觸發Spring定義的IOC容器的啟動和銷毀,具體配置如下:

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

入口-ContextLoaderListener對Spring IOC初始化和銷毀

當web容器啟動時會觸發contextInitialized方法對spring ioc容器進行初始化,銷毀時會觸發contextDestroyed方法對spring ioc容器進行銷毀,ServletContextListener接口如下:

public void contextInitialized ( ServletContextEvent sce );  // ServletContext啟動時觸發

    public void contextDestroyed ( ServletContextEvent sce );    // ServletContext銷毀時觸發           

Spring IOC啟動

spring ioc容器初始化具體代碼如下:

org.springframework.web.context.ContextLoaderListener
	
	public void contextInitialized(ServletContextEvent event) {
		this.contextLoader = createContextLoader();
		if (this.contextLoader == null) {
			this.contextLoader = this;
		}
		// 對spring ioc容器進行初始化
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}           

IOC容器的初始化是由ContextLoader類執行:

org.springframework.web.context.ContextLoader
	
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		// 是否已經加載IOC容器
		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);
					}
					// 此方法是重新整理初始化IOC容器的地方
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			// IOC容器加載後,将IOC容器儲存于ServletContext容器中
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

		...
	}           

熟悉的refresh()方法的調用:

org.springframework.web.context.ContextLoader  
	
	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		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...
				if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
					// Servlet <= 2.4: resort to name specified in web.xml, if any.
					wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
							ObjectUtils.getDisplayString(sc.getServletContextName()));
				}
				else {
					wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
							ObjectUtils.getDisplayString(sc.getContextPath()));
				}
			}
		}

		wac.setServletContext(sc);
		String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (initParameter != null) {
			wac.setConfigLocation(initParameter);
		}
		customizeContext(sc, wac);
		
		// 熟悉的refresh()對Spring IOC容器進行加載,接下來的步驟就是 "自我分析-Spring IOC"文髒中的内容
		wac.refresh();
	}           

Spring IOC銷毀

對Spring IOC容器和其他Spring環境資訊進行銷毀:

org.springframework.web.context.ContextLoaderListener
	
	public void contextDestroyed(ServletContextEvent event) {
		if (this.contextLoader != null) {
			this.contextLoader.closeWebApplicationContext(event.getServletContext());
		}
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}           

具體清理銷毀了spring的什麼東西,自己再跟蹤下代碼即可。

若文中存在分析錯誤,望留言指出,在此非常感謝。