Spring免配置启动过程

1.启动时,tomcat调用SpringServletContainerInitializer的onStartup函数,传当前可被调用的启动类。

public void onStartup(Set> webAppInitializerClasses, ServletContext servletContext){
   	for (Class waiClass : webAppInitializerClasses) {
		// Be defensive: Some servlet containers provide us with invalid classes,
		// no matter what @HandlesTypes says...
		if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
				WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
			try {
				initializers.add((WebApplicationInitializer) waiClass.newInstance());
			}
			catch (Throwable ex) {
				throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
			}
		}
	}
	for (WebApplicationInitializer initializer : initializers) {
		initializer.onStartup(servletContext);
	}
}


2.如果有RootConfigClasses配置,则会注册ContextLoaderListener,如AdminInitializer类,多个AbstractAnnotationConfigDispatcherServletInitializer中,有且仅有一个可以配置RootConfigClasses,其余的如果配置了则会抛异常。

AdminInitializer类的Root配置。
protected Class[] getRootConfigClasses() {
        return new Class[] { ApplicationConfig.class };
    }
WebInitializer和ApiInitializer都是返回null.
protected Class[] getRootConfigClasses() {
        return null;
    }
protected void registerContextLoaderListener(ServletContext servletContext) {
	WebApplicationContext rootAppContext = createRootApplicationContext();
	if (rootAppContext != null) {
		ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
		listener.setContextInitializers(getRootApplicationContextInitializers());
                //在onStartUp函数结束后,Tomcat会主动调ContextLoaderListener类的contextInitialized函数。
		servletContext.addListener(listener);
	}
	else {
		logger.debug("No ContextLoaderListener registered, as " +
				"createRootApplicationContext() did not return an application context");
	}
}

3.创建DispatcherServlet对象并注册。setLoadOnStartup(1)参数为1,表示Tomcat在执行完ContextLoaderListener的相关beanmap处理后,将依次调用Servlet的loadOnStartup函数。

protected void registerDispatcherServlet(ServletContext servletContext) {
	FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
	ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
	//setLoadOnStartup(1),将继ContextLoaderListener之后被调用。
	registration.setLoadOnStartup(1);
	registration.addMapping(getServletMappings());
	registration.setAsyncSupported(isAsyncSupported());
}

4.经上述三个步骤,程序将会返回至Tomcat内部,至此完成了onStartup的调用。该过程Tomcat只是完成了HttpServlet的注册和ContextLoaderListener的注册,而beanFactory扫描Controller/Service/Conponent/Repository注解建立beanmap及相应的单实例,以及涉及到的Controller的成员注入,仍没有开始。
5.因为是异步调用的关系,Tomcat接着调用ContextLoaderListener完成WebApplicationContext配置。

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
	if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
	  //这段异常,表示有且只有一个getRootConfigClasses函数能被返回全局配置。其它均会共享使用它的全局beanmap。
		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 {
		if (this.context instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
			if (!cwac.isActive()) {
			  //将扫描指定的目录下的所有Component组件,并形成beanmap,并依据autowired注解自动完成每一个bean对象的单实例化。
				configureAndRefreshWebApplicationContext(cwac, servletContext);
			}
		}
                //保存到servletContext的属性中,方便后续DispatcherContext的bean调用及合并。
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, 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;
	}
}



6.因为setLoadOnStartup的关系,Tomcat调用Servlet的bean初始化。

protected final void initServletBean() throws ServletException {
	getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
	if (this.logger.isInfoEnabled()) {
		this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
	}
	long startTime = System.currentTimeMillis();

	try {
	  //此处是initWebAppicationContext与5步骤中的initWebApplicationContext不是同一个函数。
	  //但函数的用途是一致的,都是用于依据bean配置构建beanmap及单实例化。
	  //此处的配置文件是由getServletConfigClasses提供。
		this.webApplicationContext = initWebApplicationContext();
		initFrameworkServlet();
	}
	catch (ServletException ex) {
		this.logger.error("Context initialization failed", ex);
		throw ex;
	}
	catch (RuntimeException ex) {
		this.logger.error("Context initialization failed", ex);
		throw ex;
	}

	if (this.logger.isInfoEnabled()) {
		long elapsedTime = System.currentTimeMillis() - startTime;
		this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
				elapsedTime + " ms");
	}
}

protected WebApplicationContext initWebApplicationContext() {
  //获取getRootConfigClasses设置的全局配置,由此可知每个DispatchServlet对象均会继承全局Root配置。
	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);
				}
				//和Root配置一样的搜索过程,建立DispatchServlet的beanmap列表。
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	
	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);
	}

	return wac;
}

7.到这步骤,已经完成Spring的初始化进程了,如果不报错的话,应该可以访问网站了。