欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring啟動(dòng)流程源碼解析

 更新時(shí)間:2024年07月18日 14:16:49   作者:談?wù)?974  
這篇文章主要介紹了Spring啟動(dòng)流程源碼解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

1. Spring 啟動(dòng)配置

Spring的啟動(dòng)是基于 web 容器的,所有 web工程的初始配置都寫(xiě)在 web.xml 中,該文件一般配置了context 參數(shù),servlet 和監(jiān)聽(tīng)器(listener)。

< context-param >是初始化 Context 的配置,< listener >調(diào)用 Spring 包中的 ContextLoaderListener ,用于監(jiān)聽(tīng) web 容器初始化事件,并加載相關(guān)配置

<!-- Spring 啟動(dòng)配置文件 -->
    <context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mybatis.xml</param-value>
	</context-param>
<!-- Spring 啟動(dòng)監(jiān)聽(tīng)器 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

<!-- Spring MVC servlet -->
	<servlet>
		<servlet-name>SpringMVC</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-mvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
		<async-supported>true</async-supported>
	</servlet>

2. Spring 啟動(dòng)流程

Spring 的啟動(dòng)流程可以分為兩部分:

  • 基于 web 容器的全局域 ServletContext 創(chuàng)建 WebApplicationContext作為 RootContext,也就是整個(gè)框架的核心容器
  • 配置的其他 Spring servlet 基于 RootContext 創(chuàng)建自己的 WebApplicationContext,從而持有自己的 bean 空間

2.1 Spring 基于 ServletContext 創(chuàng)建 RootContext

Spring 的啟動(dòng)其實(shí)就是 IoC 容器的啟動(dòng)過(guò)程,其核心監(jiān)聽(tīng)器 ContextLoaderListener 父類是 ContextLoader,實(shí)現(xiàn)了 ServletContextListener 接口,在容器啟動(dòng)時(shí)會(huì)觸發(fā)其 contextInitialized 初始化方法

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
 /**
  * Initialize the root web application context.
  */
 @Override
 public void contextInitialized(ServletContextEvent event) {
 	initWebApplicationContext(event.getServletContext());
 }
}

此處 initWebApplicationContext() 是 ContextLoader 中的方法, 該方法用于對(duì) 整個(gè)Spring 框架的ApplicationContext 進(jìn)行初始化,在這里進(jìn)入了spring IoC的初始化。

這個(gè)方法主要做了三件事:

  • 【1】createWebApplicationContext()實(shí)際創(chuàng)建 XmlWebApplicationContext 作為 RootContext
  • 【2】configureAndRefreshWebApplicationContext()加載 Spring 配置文件中的配置并創(chuàng)建 bean
  • 【3】servletContext.setAttribute()將 WebApplicationContext 放入 ServletContext 全局域
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!");
   }
   
   ······
   
   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);
   	}

   	······
   	
   	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;
   }
}

configureAndRefreshWebApplicationContext(cwac, servletContext)會(huì)從 web.xml 中讀取 contextConfigLocation 配置,也就是spring xml文件配置,將其存入 WebApplicationContext 中,最后調(diào)用refresh() 方法執(zhí)行所有Java對(duì)象的創(chuàng)建工作。

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...
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(sc.getContextPath()));
		}
	}

	wac.setServletContext(sc);
	// 獲取 contextConfigLocation 配置文件
	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(); // Spring IOC 創(chuàng)建 Bean
}

refresh() 方法的實(shí)現(xiàn)在 AbstractApplicationContext類中,其主要方法功能如圖所示。

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();
		}
	}
}

2.2 Spring servlet 基于 RootContext 創(chuàng)建 WebApplicationContext

contextLoaderListener 監(jiān)聽(tīng)器初始化完畢后,開(kāi)始初始化web.xml中配置的 servlet。

servlet可以配置多個(gè),以最常見(jiàn)的DispatcherServlet為例,DispatcherServlet 在初始化的時(shí)候會(huì)建立自己的 IoC context,用以持有spring mvc相關(guān)的bean。

DispatcherServlet 繼承關(guān)系如下圖,web容器啟動(dòng)時(shí) servlet 的調(diào)用鏈如下:

GenericServlet#init()->HttpServletBean#init()->FrameworkServlet#initServletBean()->initWebApplicationContext()

此處 FrameworkServlet # initWebApplicationContext() 方法與 Spring框架創(chuàng)建 RootContext 流程大致相同,分為以下幾步,只不過(guò)設(shè)置的 parent context 不是 ServletConext 而是 Spring 核心容器 RootContext

  • 【1】WebApplicationContextUtils.getWebApplicationContext獲取 RootContext
  • 【2】使用已有或者新建的 WebApplicationContext 加載 SpringMVC 配置文件中的配置并創(chuàng)建 bean
  • 【3】onRefresh()調(diào)用 DispatcherServlet#initStrategies() 進(jìn)行 servlet 初始化
  • 【4】將 servlet 自己的 WebApplicationContext 存入 ServletContext
protected WebApplicationContext initWebApplicationContext() {
		// 獲取 root context
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				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 -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

3. 總結(jié)

一個(gè)web應(yīng)用部署在 web 容器中,web容器為其提供一個(gè)全局域 ServletContext,作為spring IoC容器 WebApplicationContext 的宿主環(huán)境

在web.xml 中配置的 contextLoaderListener 會(huì)在容器啟動(dòng)時(shí)初始化 一個(gè)WebApplicationContext 作為 RootContext。

這是一個(gè)接口類,其實(shí)際的實(shí)現(xiàn)類是 XmlWebApplicationContext (在 ContextLoader# determineContextClass()方法中決定)。

這個(gè)就是 spring 的核心 IoC 容器,其對(duì)應(yīng) bean 定義的配置由web.xml 中的 context-param 標(biāo)簽指定,并通過(guò) refresh()方法完成 bean 創(chuàng)建。

IoC容器初始化完畢后,Spring 將以 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE作為 Key,將創(chuàng)建的 XmlWebApplicationContext 對(duì)象存儲(chǔ)到 ServletContext 中,便于之后作為 RootContext 使用

protected Class<?> determineContextClass(ServletContext servletContext) {
    // 如果在 web.xml 中直接指定了 ContextClass
 	String contextClassName = servletContext.getInitParameter(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 { 
 	// 沒(méi)有直接指定,則讀取屬性文件 ContextLoader.properties 的配置
 	/** org.springframework.web.context.WebApplicationContext=
     org.springframework.web.context.support.XmlWebApplicationContext
     */
 		contextClassName = 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);
 		}
 	}
 }

contextLoaderListener 監(jiān)聽(tīng)器初始化完畢后,開(kāi)始初始化 web.xml 中配置的servlet。

以DispatcherServlet為例,DispatcherServlet 在初始化的時(shí)候會(huì)建立自己的 context,用以持有spring mvc相關(guān)的 bean,并完成 bean 的創(chuàng)建。

初始化時(shí)設(shè)置其 parent context 為 Spring 的核心容器 RootContext,這樣每個(gè) servlet 都擁有自己的 context,即擁有自己獨(dú)立的bean空間,同時(shí)又共享 RootContext 中定義的那些bean。

當(dāng) Spring 組件在執(zhí)行 getBean 時(shí),如果在自己的 context 中找不到對(duì)應(yīng)的bean,則會(huì)在父ApplicationContext (通常為Spring 核心容器 RootContext)中去找,這也就解釋了在 DispatcherServlet 中為何可以獲取到由 ContextLoaderListener 創(chuàng)建的ApplicationContext中的bean。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java面向?qū)ο蟪绦蛟O(shè)計(jì)多態(tài)性示例

    Java面向?qū)ο蟪绦蛟O(shè)計(jì)多態(tài)性示例

    這篇文章主要介紹了Java面向?qū)ο蟪绦蛟O(shè)計(jì)多態(tài)性,結(jié)合實(shí)例形式分析了java多態(tài)性的概念、原理、定義與使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2018-03-03
  • Java實(shí)現(xiàn)圖片裁剪功能的示例詳解

    Java實(shí)現(xiàn)圖片裁剪功能的示例詳解

    這篇文章主要介紹了如何利用Java實(shí)現(xiàn)圖片裁剪功能,可以將圖片按照自定義尺寸進(jìn)行裁剪,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-01-01
  • 深入了解Java中的類加載機(jī)制

    深入了解Java中的類加載機(jī)制

    通常,在關(guān)于Java的類加載部分會(huì)遇到以上疑問(wèn),本文將對(duì)類加載重要部分做詳細(xì)介紹,包括重要的基礎(chǔ)概念和應(yīng)用場(chǎng)景,在編寫(xiě)過(guò)程中也幫助作者重新熟悉并加固了知識(shí)點(diǎn),希望在看完后對(duì)大家能有所幫助
    2022-11-11
  • JAVA虛擬機(jī)中 -D, -X, -XX ,-server參數(shù)使用

    JAVA虛擬機(jī)中 -D, -X, -XX ,-server參數(shù)使用

    本文主要介紹了JAVA虛擬機(jī)中 -D, -X, -XX ,-server參數(shù)使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • 三種SpringBoot中實(shí)現(xiàn)異步調(diào)用的方法總結(jié)

    三種SpringBoot中實(shí)現(xiàn)異步調(diào)用的方法總結(jié)

    Spring Boot 提供了多種方式來(lái)實(shí)現(xiàn)異步任務(wù),這篇文章主要為大家介紹了常用的三種實(shí)現(xiàn)方式,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2023-05-05
  • Java程序執(zhí)行的全流程

    Java程序執(zhí)行的全流程

    這篇文章主要介紹了Java程序執(zhí)行的全流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 基于SpringBoot實(shí)現(xiàn)用戶身份驗(yàn)證工具

    基于SpringBoot實(shí)現(xiàn)用戶身份驗(yàn)證工具

    這篇文章主要介紹了基于SpringBoot實(shí)現(xiàn)的用戶身份驗(yàn)證工具,非常不錯(cuò),具有參考借鑒價(jià)值 ,需要的朋友可以參考下
    2018-04-04
  • SpringBoot 集成 Nebula的操作過(guò)程

    SpringBoot 集成 Nebula的操作過(guò)程

    這篇文章主要介紹了SpringBoot 集成 Nebula的操作過(guò)程,通過(guò)示例代碼介紹了java 環(huán)境下如何對(duì) Nebula Graph 進(jìn)行操作,感興趣的朋友跟隨小編一起看看吧
    2024-05-05
  • spring @Scheduled注解的使用誤區(qū)及解決

    spring @Scheduled注解的使用誤區(qū)及解決

    這篇文章主要介紹了spring @Scheduled注解的使用誤區(qū)及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Spring Boot整合郵件發(fā)送與注意事項(xiàng)

    Spring Boot整合郵件發(fā)送與注意事項(xiàng)

    這篇文章主要給大家介紹了關(guān)于Spring Boot整合郵件發(fā)送與注意事項(xiàng)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07

最新評(píng)論