整体流程
我们知道SpringApplication.run()
时会创建根据推断的环境创建对应的应用上下文
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
DEFAULT_WEB_CONTEXT_CLASS
为AnnotationConfigEmbeddedWebApplicationContext
,其继承了EmbeddedWebApplicationContext
,因此在web环境中创建的应用上下文为AnnotationConfigEmbeddedWebApplicationContext
。
在进行刷新上下文onRefresh()
中,我们可以看下它的代码
protected void onRefresh() {
super.onRefresh();
try {
createEmbeddedServletContainer();
}
}
在调用了Spring的应用上下午刷新后,还进行了createEmbeddedServletContainer
即创建内置的Servlet容器操作,其通过EmbeddedServletContainerFactory
工厂方法来进行具体的创建工作。
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = getServletContext();
if (localContainer == null && localServletContext == null) {
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
//这里通过工厂方法来创建Servlet容器,这里会将当前环境中的ServletContextInitializer找出来
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
}
else if (localServletContext != null) {
try {
//如果已经存在环境则进行初始化
getSelfInitializer().onStartup(localServletContext);
}
}
initPropertySources();
}
ServletContextInitializer
的查找最终由工具类ServletContextInitializerBeans.addServletContextInitializerBeans()
实现
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(
beanFactory, ServletContextInitializer.class)) {
addServletContextInitializerBean(initializerBean.getKey(),
initializerBean.getValue(), beanFactory);
}
}
我们来到TomcatEmbeddedServletContainerFactory
中
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
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实例并进行了一些配置,核心方法为prepareContext(tomcat.getHost(), initializers);
这里会创建Tomcat的上下文环境TomcatEmbeddedContext
并进行一些基础配置,并添加生命周期监听器、资源加载器、设置Servlet并对Servlet环境进行初始化操作。
getTomcatEmbeddedServletContainer
中会通过TomcatEmbeddedServletContainer
的构造器调用其initialize
方法,进行tomcat.start()
的调用启动tomcat。
初始化
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File docBase = getValidDocumentRoot();
docBase = (docBase != null ? docBase : createTempDir("tomcat-docbase"));
final TomcatEmbeddedContext context = new TomcatEmbeddedContext();
context.setName(getContextPath());
context.setDisplayName(getDisplayName());
context.setPath(getContextPath());
context.setDocBase(docBase.getAbsolutePath());
context.addLifecycleListener(new FixContextListener());
context.setParentClassLoader(
this.resourceLoader != null ? this.resourceLoader.getClassLoader()
: ClassUtils.getDefaultClassLoader());
resetDefaultLocaleMapping(context);
addLocaleMappings(context);
try {
context.setUseRelativeRedirects(false);
}
catch (NoSuchMethodError ex) {
// Tomcat is < 8.0.30. Continue
}
SkipPatternJarScanner.apply(context, this.tldSkipPatterns);
WebappLoader loader = new WebappLoader(context.getParentClassLoader());
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
loader.setDelegate(true);
context.setLoader(loader);
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
}
if (shouldRegisterJspServlet()) {
addJspServlet(context);
addJasperInitializer(context);
context.addLifecycleListener(new StoreMergedWebXmlListener());
}
context.addLifecycleListener(new LifecycleListener() {
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
TomcatResources.get(context)
.addResourceJars(getUrlsOfJarsWithMetaInfResources());
}
}
});
//Servlet环境初始化
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
configureContext(context, initializersToUse);
host.addChild(context);
postProcessContext(context);
}
protected void configureContext(Context context,
ServletContextInitializer[] initializers) {
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
// Should be true
((TomcatEmbeddedContext) context).setStarter(starter);
}
//将TomcatStarter塞入环境中供启动后调用进行初始化
context.addServletContainerInitializer(starter, NO_CLASSES);
// 其他略过
}
TomcatStarter
是一个ServletContainerInitializer
,它持有在创建web环境时传过来的当前环境中的所有ServletContextInitializer
数组,并在自己的onStartup
方法中遍历调用所有ServletContextInitializer.onStartup()
。注意后面直接将TomcatStarter
这个ServletContainerInitializer
塞入了容器,就不需要根据SCIs的规范将其放置在META-INF/services/javax.servlet.ServletContainerInitializer
文件中了。
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
而TomcatStarter
是在什么时候被调用的?
我们在StandardContext.startInternal()
中发现了调用轨迹。关于ServletContainerInitializer
的相关我会再开一篇博客专门介绍