springboot【web启动流程-1】

       想必大家都体验过springboot的便捷,以前想要运行web项目,我们首先需要将项目打成war包,然后再运行Tomcat启动项目,不过自从有了springboot,我们可以像启动jar包一样简单的启动一个web项目,今天我们就来分析下springboot启动web项目整个流程。

 

分析springboot,万变不离其中,一样从启动方法作为入口

public ConfigurableApplicationContext run(String... args) {
                context = createApplicationContext();//创建上下文
	            ... 略
			refreshContext(context);//上下文刷新
			afterRefresh(context, applicationArguments);
			... 略
}

 这里我们引出了启动相关的两个核心方法 createApplicationContext 和 refreshContext 

首先看下createApplicationContext实现:

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				contextClass = Class.forName(this.webEnvironment
						? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, "
								+ "please specify an ApplicationContextClass",
						ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
	}

我们可以看到context的类型判断逻辑,本文我们分析web容器,所以contextClass 为 DEFAULT_WEB_CONTEXT_CLASS 

public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";

故springboot 的web环境context类 为 AnnotationConfigEmbeddedWebApplicationContext

接着看refreshContext方法:

private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
                  ... 略
	}

AbstractApplicationContext的refresh方法

protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}

可以看到这里最终会调用到context的refresh方法,而我们的AnnotationConfigEmbeddedWebApplicationContext的集成关系图如下:

PS:集成关系过于复杂,这里截取了部分重点关系

而context的refresh则在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();//容器刷新完成之后 启动容器
			}
}

而onRefresh的具体实现在 EmbeddedWebApplicationContext中:

protected void onRefresh() {
		super.onRefresh();
		try {
              //创建内嵌的servlet容器
			createEmbeddedServletContainer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start embedded container",
					ex);
		}
	}
private void createEmbeddedServletContainer() {
		EmbeddedServletContainer localContainer = this.embeddedServletContainer;
		ServletContext localServletContext = getServletContext();
         // 首次启动 容器为空
		if (localContainer == null && localServletContext == null) {
			EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();//获取创建容器工厂类
			this.embeddedServletContainer = containerFactory
					.getEmbeddedServletContainer(getSelfInitializer());//创建容器
		}
		else if (localServletContext != null) {
			try {
				getSelfInitializer().onStartup(localServletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context",
						ex);
			}
		}
		initPropertySources();
	}

容器创建getEmbeddedServletContainer方法实现如下:

public EmbeddedServletContainer getEmbeddedServletContainer(
			ServletContextInitializer... initializers) {
		Tomcat tomcat = new Tomcat();//新建tomcat
		File baseDir = (this.baseDirectory != null ? this.baseDirectory
				: createTempDir("tomcat"));
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);//新建tomcat容器连接器
		tomcat.getService().addConnector(connector);//将connector添加至tomcat
		customizeConnector(connector);
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			tomcat.getService().addConnector(additionalConnector);
		}
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatEmbeddedServletContainer(tomcat);//返回tomcat内嵌容器
	}

可以看出 通过onRefresh方法最终创建了类型为TomcatEmbeddedServletContainer的web容器,接着我们再来看容器的启动

finishRefresh由EmbeddedWebApplicationContext重载实现:

protected void finishRefresh() {
		super.finishRefresh();
		EmbeddedServletContainer localContainer = startEmbeddedServletContainer();//启动内嵌servlet容器
		if (localContainer != null) {
			publishEvent(
					new EmbeddedServletContainerInitializedEvent(this, localContainer));
		}
	}
private EmbeddedServletContainer startEmbeddedServletContainer() {
		EmbeddedServletContainer localContainer = this.embeddedServletContainer;
		if (localContainer != null) {
			localContainer.start();
		}
		return localContainer;
	}

到此,springboot启动web容器流程,下一篇我们将分析tomcat的启动流程

敬请期待...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章