`
云上太阳
  • 浏览: 131786 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论
阅读更多

一、Spring与WEB容器整合   

web项目中,Spring启动是在web.xml配置监听器,如下所示: 

<!-- 配置Spring上下文监听器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 
  可以看看ContextLoaderListener类,它实现了Tomcat容器的ServletContextListener接口,所以它与普通的Servlet监听是一样的。同样是重写到两个方法:contextInitialized()方法在web容器初始化时执行,contextDestroyed()方法在容器销毁时执行。

    WEB容器启动时会触发初始化事件,ContextLoaderListener监听到这个事件,其contextInitialized()方法会被调用,在这个方法中Spring会初始化一个根上下文,即WebApplicationContext。这是一个接口,其实际默认实现类是XmlWebApplicationContext。这个就是Spring IOC的容器,其对应bean定义的配置信息由web.xml中的context-param来指定

<!-- 配置Spring配置文件路径 -->
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath*:applicationContext.xmlclasspath*:applicationContext-shiro.xml
        </param-value>
</context-param>

 在Spring IOC 容器启动初始化完毕之后,会将其储存到ServletContext中。形式如下:servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, WebApplicationContext context);



 
注:以前我们写Servlet程序的时候,如果在Tomcat容器启动的时候需要绑定一些参数或者做一些全局处理,那么就需要实现ServletContextListener接口,在contextInitialized方法中编写必要的代码。然后在web.xml添加配置listener

 在ContextLoaderListener类中,只是实现了ServletContextListener提供的到两个方法,Spring启动主要的逻辑在父类ContextLoader的方法initWebApplicationContext实现。ContextLoaderListener的作用就是启动web容器时自动装配ApplicationContext的配置信息。更细化一点讲,Spring的启动过程其实就是Spring IOC容器的启动过程。

 

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

	/**
	 * Initialize the root web application context.
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}

	/**
	 * Close the root web application context.
	 */
	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}
}

 

 二、ContextLoader剖析

  从上一部分ContextLoaderListener类可以得知,ContextLoader实际执行Spring容器的初始化,Spring整个的配置工作都是在ContextLoader完成的,这里参数ServletContextEvent由web容器提供,不做说明。ContextLoaderListener很好理解,所以我们主要看ContextLoader类。用Maven引入Spring的源码,打开ContextLoader类,类注释的第一行就是

/**
 * Performs the actual initialization work for the root application context.
  ......
**/

 用google翻译:实际执行根应用上下文的初始化工作。这里的根应用上下文就是上文所写的WebApplicationContext。我们先看看ContextLoader的时序图

 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) {
	//判断ServletContext是否已经存在WebApplication,如果存在则抛出异常
	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) {
			//创建WebApplicationContext(下文有说明)
			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) {
					// 得到根上下文的父上下文,然后设置到根上下文 。一般的web项目parent为空
					ApplicationContext parent = loadParentContext(servletContext);
					cwac.setParent(parent);
				}
				//从web.xml加载参数,初始化跟上下文WebApplicationContext,创建bean工厂和bean对象。
				//这个过程比较麻烦,下一篇文章专门分析
				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;
	}
}

     

    在这个方法中ServletContext是由web容器监听器(ContextLoaderListener)提供。首先判断servlectContext中是否已经存在根上下文,如果存在,则抛出异常;否则通过createWebApplicationContext方法创建新的根上下文。然后通过loadParentContext()方法为其设置父上下文。再通过configureAndRefreshWebApplicationContext为根上下文构建bean工厂和bean对象。 最后把上下文存入servletContext,并且存入currentContextPerThread。至此初始化过程完毕,接下来可以获取WebApplicationContext,进而用getBean("bean name")得到bean。

 

createWebApplicationContext()方法用来创建根上下文:

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
	//从web.xml配置的contextClass参数中获取上下文类名,如果contextClass为空,则使用默认的。
        //下文有说明
	Class<?> contextClass = determineContextClass(sc);
        //根上下文必须是ConfigurableWebApplicationContext的子类,否则抛出异常
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
				"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
	}
	//BeanUtils.instantiateClass工具方法,根据类名创建类
	return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
 

determineContextClass()方法返回根上下文的类名

protected Class<?> determineContextClass(ServletContext servletContext) {
	//从web.xml获得参数contextClass,在一般的web项目中,此参数为null
	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 {
		//获得根上下文WebApplicationContext的默认实现类的类名,defaultStrategies是Properties类型,
		//在CotnextLoader类开头static语句块中初始化
		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);
		}
	}
}
 

WebApplicationContext默认实现类的类名获取

static {
	try {
		//获取当前包下面的ContextLoader.properties文件,文件内容是:
		//org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
		ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
		defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
	}
	catch (IOException ex) {
		throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
	}
}
 
 
 
  • 大小: 13.9 KB
  • 大小: 25.2 KB
分享到:
评论

相关推荐

    Spring启动完整流程图

    Spring启动完整流程图 Spring启动完整流程图 Spring启动完整流程图 Spring启动完整流程图 Spring启动完整流程图 Spring启动完整流程图 Spring启动完整流程图 Spring启动完整流程图 Spring启动完整流程图 Spring启动...

    spring配置和启动方式

    spring配置和启动方式 博客地址:https://blog.csdn.net/u010476739/article/details/76696756

    Spring 自启动项目demo

    本项目是一个关于Spring自启动(Auto-Startup)功能的示例,它基于Spring 3.0版本并结合了MVC(Model-View-Controller)设计模式。通过这个项目,我们可以深入理解Spring如何实现自动初始化和管理Bean,以及如何构建...

    spring容器启动和关闭时事件监听

    spring容器启动和关闭时事件监听;spring容器启动和关闭时事件监听;spring容器启动和关闭时事件监听

    spring boot/spring cloud项目启动脚本,默认名称:start.sh

    linux服务器,springboot,spring cloud、spring cloud alibaba等项目启动脚本 下载脚本, 1,上传脚本至jar包同级目录 2,更改脚本: jar包名称 项目文件路径 日志路径(包含日志名称) 脚本已配置好jvm优化...

    82. Spring Boot – 启动彩蛋【从零开始学Spring Boot】

    Spring Boot – 启动彩蛋"这一主题,这属于Spring Boot框架的一部分,该框架简化了Java应用程序的创建和管理。启动彩蛋是开发人员为了增加趣味性或者隐藏信息而在软件中设置的小秘密,通常需要特定的触发条件才能...

    springboot入口启动类

    Springboot的启动类,用于Springboot开发项目启动的入口。

    Java+Spring+Spring容器启动流程方法调用xmind脑图

    1. Spring容器的启动流程 2. 循环依赖 3. Spring 中Bean的创建 4. Spring 方法xmind脑图

    Linux部署springboot(springcloud)启动多个jar

    本篇文章将深入讲解如何在Linux环境下部署SpringBoot(SpringCloud)项目,并启动多个jar文件,以及如何通过shell脚本来实现日志管理和服务控制。 首先,SpringBoot是一个基于Spring框架的轻量级开发工具,它内置了...

    SpringCloud服务启动脚本

    SpringCloud微服务架构,启动脚本,动态输出日志,并指向启动日志脚本位置。

    spring boot如何指定启动端口

    Spring Boot 指定启动端口 Spring Boot 框架提供了多种方式来指定启动端口,以满足不同的需求。本文将详细介绍如何指定 Spring Boot 的启动端口。 方式一:修改配置文件 在 Spring Boot 项目中,可以通过修改配置...

    Springboot-项目启动找不到启动类

    Spring Boot的设计目标是简化Spring应用程序的初始设置和配置,因此一个可运行的Spring Boot应用通常只有一个明确的启动入口,即启动类。让我们深入探讨这个问题以及如何解决它。 首先,Spring Boot的启动类通常会...

    beetl自动配置Spring启动beetl-spring-boot.zip

    beetl-spring-boot-starter,beetl 自动配置 Spring 启动。使用:从 maven 导入该库  &lt;groupId&gt;com.piggsoft&lt;/groupId&gt;  &lt;artifactId&gt;beetl-spring-boot-starter  &lt;version&gt;0.0.1 配置 application....

    spring和quartz的定时器的启动和停止例子

    Spring提供了一种集成Quartz的方式,使得我们可以方便地在Spring应用中管理和执行定时任务。本篇将深入探讨如何在Spring中启动和停止Quartz定时器。 首先,我们需要理解Spring和Quartz的基本概念。Spring是一个强大...

    spring boot windows 启动脚本

    spring boot windows 启动脚本

    SpringCloud项目

    Zuul是SpringCloud的边缘服务和动态路由服务,它作为所有微服务的统一入口,处理预处理和后处理逻辑,如认证、限流、过滤等。在"renren-security"项目中,Zuul可以对所有微服务的请求进行统一的权限验证和路由转发...

    spring-boot-1-demo

    在 Spring Boot 中,启动类(通常包含 `@SpringBootApplication` 注解)是项目的入口点,它负责初始化 Spring 容器并启动应用。在这个 demo 中,提供了两种不同的结构,即启动类与 Controller 所在的包是否在同一层...

    SpringBoot项目启动时实现调用一次初始化方法.docx

    在Spring Boot应用中,我们经常需要在项目启动时执行一次初始化操作,比如加载配置、预设数据等。这里我们将详细探讨如何实现这个需求,主要涉及`@PostConstruct`注解、`CommandLineRunner`接口以及在启动类中直接...

    Java_Springstartupanalyzer生成一个交互式的spring应用程序启动报告,让您了解是什么因素导.zip

    Java Spring Startup Analyzer是一款强大的工具,专门用于分析Spring应用程序的启动过程,帮助开发者深入理解应用程序启动时间的消耗,找出性能瓶颈,从而优化应用的启动速度。这个工具为开发者提供了一个交互式的...

    启动Spring项目详细过程(小结)

    在Spring容器的启动过程中,会执行以下步骤:首先,会检查是否已经存在根应用程序上下文,如果不存在,则创建一个新的应用程序上下文。然后,会创建一个新的spring容器,并将其添加到ServletContext中。最后,会将...

Global site tag (gtag.js) - Google Analytics