想必大家都體驗過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的啓動流程
敬請期待...