博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring web 项目启动过程分析
阅读量:6904 次
发布时间:2019-06-27

本文共 5866 字,大约阅读时间需要 19 分钟。

本文分析了Spring Web 项目的启动过程,从 /webapps/web.xml 为入口。

目录

Web项目中的启动配置文件 web.xml

web.xml 文件是用来初始化工程配置信息的。比如说welcome页面,filter,listener,servlet, servlet-mapping,启动加载级别等等。我们可以关注到在web.xml 文件中有以下两个标签的配置。

// servlet容器启动监听器
org.springframework.web.context.ContextLoaderListener
// 初始化参数 - 用于容器启动所需要的读取的配置的文件位置
contextConfigLocation
classpath:applicationContext.xml
复制代码
容器启动的监听器 ContextLoaderListener

spring的启动是依赖于 ContextLoaderListener#contextInitialized的父类方法 ContextLoader#initWebApplicationContext 进行。

/*** ServletContext 监听器*/public class ContextLoaderListener extends ContextLoader implements ServletContextListener {    // ServletContext初始化之前执行    @Override	public void contextInitialized(ServletContextEvent event) {		initWebApplicationContext(event.getServletContext());	}	    // ServletContext初始化之前执行	@Override	public void contextDestroyed(ServletContextEvent event) {		// 关闭WEB容器		closeWebApplicationContext(event.getServletContext());		ContextCleanupListener.cleanupAttributes(event.getServletContext());	}}public class ContextLoader {// Web 容器@Nullableprivate WebApplicationContext context;   // 省略其他代码   ...public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {        // 如果容器已经存在,抛出异常 避免重复创建root上下文,保证只有一个Spring容器		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 {		    // 如果当前容器为空则创建一个 WebApplicationContext		    			if (this.context == null) {				this.context = createWebApplicationContext(servletContext);			}						// 如果容器已经存在,并且是一个可配置的WebApplicationContext			// ConfigurableWebApplicationContext是个接口,而且顾名思义:可配置的WebApplicationContext						if (this.context instanceof ConfigurableWebApplicationContext) {				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;								// 容器是否已经被激活 -- prepareRefresh(){this.active.set(true); ....}				if (!cwac.isActive()) {										// 设置父级上下文 上下文ID  环境										if (cwac.getParent() == null) {						ApplicationContext parent = loadParentContext(servletContext);						cwac.setParent(parent);					}					// 配置和刷新 WebApplContext容器					configureAndRefreshWebApplicationContext(cwac, servletContext);				}			}						// 将配置并且刷新过的容器存入servlet上下文中,并以WebApplicationContext的类名作为key值			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 | Error ex) {		...		}	}}复制代码
如何创建 WebApplicationContext
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); } protected Class
determineContextClass(ServletContext servletContext) { // 查询用户是否自定义了 context String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { // 通过contextClassName 和 反射获取类 return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } // 从ContextLoader.properties获取默认配置 默认容器是XmlWebApplicationContext else { 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); } } } 复制代码
如何配置并刷新容器 configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {                // Unique id for this context 唯一的ID 		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {			String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);			if (idParam != null) {				wac.setId(idParam);			}			else {				// 生成默认ID				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +						ObjectUtils.getDisplayString(sc.getContextPath()));			}		}        // 将servlet上下文 注入 webApplicationContext		wac.setServletContext(sc);				// 获取在web.xml 中配置的application.xml 路径		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);		}		// 主要用于自定义context相关配置,比如定义bean是否可以循环引用,bean定义是否可以被覆盖等,通常情况下不做任何操作。		customizeContext(sc, wac);				// 刷新 WebApplicationContext 容器		wac.refresh();	}复制代码
将容器初始化到 Servlet 上下文
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);复制代码
Web 容器初始化 时序图

转载于:https://juejin.im/post/5cc9aacee51d453afb40d813

你可能感兴趣的文章
centos6.8服务器配置之编译安装PHP、配置nginx
查看>>
ddddddd
查看>>
Android 开发之 ---- 底层驱动开发(一)
查看>>
分享到朋友圈实现
查看>>
SQL Server 用链接服务器 同步SqlServer与MySQL
查看>>
Android程序的打包和安装
查看>>
Android内存优化6 了解Android是如何管理App内存
查看>>
SpringBoot2 添加应用拦截器
查看>>
es删除文档或者删除索引
查看>>
swift可选值总结
查看>>
深入理解Java虚拟机06--虚拟机字节码执行引擎
查看>>
C# 委托和事件,简单示例说明问题
查看>>
『转载』转过来的Xpath语法
查看>>
编码:隐匿在计算机软硬件背后的语言
查看>>
object-c中NSString字符串匹配操作
查看>>
iOS - (TableView中利用系统的 cell 设置 cell.textlabel 位置和大小)
查看>>
SqlBulkCopy(批量复制)使用方法
查看>>
OracleHelper类
查看>>
UIImageView 浅析
查看>>
Linux Shell编程三
查看>>