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的啓動流程

敬請期待...

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