spring启动流程--原创

1.spring的配置

<web-app>

  <display-name>Web Application</display-name>

  <!--全局变量配置-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext-*.xml</param-value>
  </context-param>

  <!--监听器-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <!--解决乱码问题的filter-->
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!--Restful前端控制器-->
  <servlet>
    <servlet-name>springMVC_rest</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>
  </servlet>

  <servlet-mapping>
    <servlet-name>springMVC_rest</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

spring的执行流程

.

从spring的启动流程可以看出,先启动web容器,再启动spring。这里我们以tomcat为例

tomcat启动时会先加载自身conf文件夹下的文件context.xml,catalina.properties,server.xml,web.xml,,之后加载找到spring的web.xml,执行其中的内容。

 在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。

  当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理。在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法。

public interface ServletContextListener extends EventListener {


    public void contextInitialized(ServletContextEvent sce);

    public void contextDestroyed(ServletContextEvent sce);
}
}

从这里可以看出,想要通过tomcat启动springmvc,就需要sping实现这个监听,tomcat的启动时,就会自动执行接口中内容。在spring中,这个接口监听就是web.xml中的ContextLoaderListener监听,spring的ContextLoaderListener类,继承了spring的ContextLoader类,实现了ServletContextListener类。tomcat启动时,会执行contextInitialized方法,这个方法的实现就在ContextLoader类中。


public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

	
	public ContextLoaderListener() {
	}


	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}


	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}

}

让我们看看启动的方法

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

在这个方法中,参数servletContext是tomcat传递过来的事件中的参数,方法中最主要的就是createWebApplicationContext方法,和configureAndRefreshWebApplicationContext方法,createWebApplicationContext方法创建了WebApplicationContext,configureAndRefreshWebApplicationContext方法配置并刷新WebApplicationContext。

我们主要看configureAndRefreshWebApplicationContext最后一行的刷新方法refresh() ,默认实现在类AbstractApplicationContext中,

@Override
public void refresh()抛出BeansException, IllegalStateException {
同步(this.startupShutdownMonitor){
//为刷新做好准备。
prepareRefresh();
//告诉子类刷新内部bean工厂。
ConfigurableListableBeanFactory = obtainFreshBeanFactory();
//准备在此上下文中使用的bean工厂。
prepareBeanFactory(beanFactory);
尝试{
//允许在上下文子类中对bean工厂进行后处理。
postProcessBeanFactory(beanFactory);
//调用在上下文中注册为bean的工厂处理器。
invokeBeanFactoryPostProcessors(beanFactory);
//注册拦截bean创建的bean处理器。
registerBeanPostProcessors(beanFactory);
//初始化此上下文的消息源。
initMessageSource();
//为此上下文初始化事件多播器。
initApplicationEventMulticaster();
//初始化特定上下文子类中的其他特殊bean。
onRefresh();
//检查侦听器bean并注册它们。
有关registerlistener();
//实例化所有剩余的(非延迟初始化)单例。
finishBeanFactoryInitialization(beanFactory);
//最后一步:发布相应的事件。
finishRefresh();
}
catch(BeansException ex) {
if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

//销毁已经创建的单例,以避免挂起资源。
destroyBeans();
//重置“活动”标志。
cancelRefresh(ex);
//将异常传播给调用方。
	throw ex;
}
finally{
//重置Spring内核中的常用自检缓存,因为我们
//可能再也不需要单例bean的元数据了……
resetCommonCaches();
}
}
}

这个方法是不是很熟悉,很多讲解spring启动过程的文章都会说道这个方法,所以具体内容请自行百度

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值