启动流程
onRefresh
ServletWebServerApplicationContext
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
//内嵌tomcat逻辑
if (webServer == null && servletContext == null) {
//从容器中获取ServletWebServerFactory
ServletWebServerFactory factory = this.getWebServerFactory();
//传入了1个回调初始化器
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
} else if (servletContext != null) {
//外嵌tomcat逻辑
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var4) {
throw new ApplicationContextException("Cannot initialize servlet context", var4);
}
}
this.initPropertySources();
}
private ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
内嵌tomcat
TomcatServletWebServerFactory
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//创建tomcat,设置tomcat的connector、engine
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
//把tomcat封装成TomcatWebServer并启动
return this.getTomcatWebServer(tomcat);
}
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = this.getValidDocumentRoot();
//创建container
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
if (documentRoot != null) {
context.setResources(new TomcatServletWebServerFactory.LoaderHidingResourceRoot(context));
}
context.setName(this.getContextPath());
context.setDisplayName(this.getDisplayName());
context.setPath(this.getContextPath());
File docBase = documentRoot != null ? documentRoot : this.createTempDir("tomcat-docbase");
context.setDocBase(docBase.getAbsolutePath());
context.addLifecycleListener(new FixContextListener());
context.setParentClassLoader(this.resourceLoader != null ? this.resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader());
this.resetDefaultLocaleMapping(context);
this.addLocaleMappings(context);
context.setUseRelativeRedirects(false);
try {
context.setCreateUploadTargets(true);
} catch (NoSuchMethodError var8) {
;
}
this.configureTldSkipPatterns(context);
WebappLoader loader = new WebappLoader(context.getParentClassLoader());
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
loader.setDelegate(true);
context.setLoader(loader);
if (this.isRegisterDefaultServlet()) {
//给container添加1个默认子container
this.addDefaultServlet(context);
}
if (this.shouldRegisterJspServlet()) {
this.addJspServlet(context);
this.addJasperInitializer(context);
}
context.addLifecycleListener(new TomcatServletWebServerFactory.StaticResourceConfigurer(context));
ServletContextInitializer[] initializersToUse = this.mergeInitializers(initializers);
//把container添加到host中
host.addChild(context);
//给container添加回调initializer
this.configureContext(context, initializersToUse);
this.postProcessContext(context);
}
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext)context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
//往container中添加ServletContainerInitializer,即TomcatStarter
context.addServletContainerInitializer(starter, NO_CLASSES);
Iterator var7 = this.contextLifecycleListeners.iterator();
while(var7.hasNext()) {
LifecycleListener lifecycleListener = (LifecycleListener)var7.next();
context.addLifecycleListener(lifecycleListener);
}
var7 = this.contextValves.iterator();
while(var7.hasNext()) {
Valve valve = (Valve)var7.next();
context.getPipeline().addValve(valve);
}
var7 = this.getErrorPages().iterator();
while(var7.hasNext()) {
ErrorPage errorPage = (ErrorPage)var7.next();
org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
tomcatErrorPage.setLocation(errorPage.getPath());
tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
context.addErrorPage(tomcatErrorPage);
}
var7 = this.getMimeMappings().iterator();
while(var7.hasNext()) {
Mapping mapping = (Mapping)var7.next();
context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
}
this.configureSession(context);
(new DisableReferenceClearingContextCustomizer()).customize(context);
var7 = this.tomcatContextCustomizers.iterator();
while(var7.hasNext()) {
TomcatContextCustomizer customizer = (TomcatContextCustomizer)var7.next();
customizer.customize(context);
}
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, this.getPort() >= 0);
}
TomcatStarter
class TomcatStarter implements ServletContainerInitializer {
private static final Log logger = LogFactory.getLog(TomcatStarter.class);
private final ServletContextInitializer[] initializers;
private volatile Exception startUpException;
TomcatStarter(ServletContextInitializer[] initializers) {
this.initializers = initializers;
}
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
ServletContextInitializer[] var3 = this.initializers;
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
ServletContextInitializer initializer = var3[var5];
//回调selfInitialize
initializer.onStartup(servletContext);
}
} catch (Exception var7) {
this.startUpException = var7;
if (logger.isErrorEnabled()) {
logger.error("Error starting Tomcat context. Exception: " + var7.getClass().getName() + ". Message: " + var7.getMessage());
}
}
}
}
TomcatWebServer
public class TomcatWebServer implements WebServer {
private static final Log logger = LogFactory.getLog(TomcatWebServer.class);
private static final AtomicInteger containerCounter = new AtomicInteger(-1);
private final Object monitor;
private final Map<Service, Connector[]> serviceConnectors;
private final Tomcat tomcat;
private final boolean autoStart;
private volatile boolean started;
public TomcatWebServer(Tomcat tomcat) {
this(tomcat, true);
}
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
this.monitor = new Object();
this.serviceConnectors = new HashMap();
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.initialize();
}
private void initialize() throws WebServerException {
//控制台看到的日志
logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
synchronized(this.monitor) {
try {
this.addInstanceIdToEngineName();
Context context = this.findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && "start".equals(event.getType())) {
this.removeServiceConnectors();
}
});
this.tomcat.start();
this.rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
} catch (NamingException var5) {
}
//开启daemon线程,保证springboot主程序不退出
this.startDaemonAwaitThread();
} catch (Exception var6) {
this.stopSilently();
this.destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", var6);
}
}
}
}
a.从容器获取ServletWebServerFactory
b.利用ServletWebServerFactory创建webServer,传入1个回调初始化方法selfInitialize
c.创建Tomcat,设置核心组件connector、engine
d.创建container,即TomcatEmbeddedContext
e.创建ServletContainerInitializer,即TomcatStarter,tomcat启动过程会调用 container初始化器,即TomcatStarter#onStartUp进一步回调selfInitialize
f.创建TomcatWebServer(tomcat, port),这个过程中会启动tomcat,同时启动1个非daemon线程保证springbooot程序不退出(每隔10s循环判断stopAwait变量)
外嵌tomcat
只注册DispatcherServlet。
注册DispatcherServlet
selfInitialize
private void selfInitialize(ServletContext servletContext) throws ServletException {
this.prepareWebApplicationContext(servletContext);
this.registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(this.getBeanFactory(), servletContext);
//从容器获取到所有的ServletContextInitializer,即DispatcherServletRegistrationBean
Iterator var2 = this.getServletContextInitializerBeans().iterator();
while(var2.hasNext()) {
ServletContextInitializer beans = (ServletContextInitializer)var2.next();
beans.onStartup(servletContext);
}
}
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(this.getBeanFactory(), new Class[0]);
}
public ServletContextInitializerBeans(ListableBeanFactory beanFactory, Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializerTypes = initializerTypes.length != 0 ? Arrays.asList(initializerTypes) : Collections.singletonList(ServletContextInitializer.class);
this.addServletContextInitializerBeans(beanFactory);
this.addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = (List)this.initializers.values().stream().flatMap((value) -> {
return value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE);
}).collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
this.logMappings(this.initializers);
}
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
Iterator var2 = this.initializerTypes.iterator();
while(var2.hasNext()) {
Class<? extends ServletContextInitializer> initializerType = (Class)var2.next();.
//从容器中获取ServletContextInitializer,即DispatcherServletRegistrationBean
Iterator var4 = this.getOrderedBeansOfType(beanFactory, initializerType).iterator();
while(var4.hasNext()) {
Entry<String, ? extends ServletContextInitializer> initializerBean = (Entry)var4.next();
this.addServletContextInitializerBean((String)initializerBean.getKey(), (ServletContextInitializer)initializerBean.getValue(), beanFactory);
}
}
}
DispatcherServletRegistrationBean
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = this.getDescription();
if (!this.isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
} else {
this.register(description, servletContext);
}
}
protected final void register(String description, ServletContext servletContext) {
D registration = this.addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
} else {
this.configure(registration);
}
}
protected Dynamic addRegistration(String description, ServletContext servletContext) {
String name = this.getServletName();
return servletContext.addServlet(name, this.servlet);
}
1.tomcat启动过程中会调用ServletContainerInitializer#onStartUp,而它会进一步回调selfInitialize
2.从容器获取ServletContextInitializer,即DispatcherServletRegistrationBean
3.调用DispatcherServletRegistrationBean#onStartup,把DispatcherServelet注册到tomcat中
初始化DispatcherServlet
ServletWebServerApplicationContext
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = this.startWebServer();
if (webServer != null) {
this.publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
private WebServer startWebServer() {
//即TomcatWebServer
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.start();
}
return webServer;
}
TomcatWebServer
public void start() throws WebServerException {
synchronized(this.monitor) {
if (!this.started) {
boolean var10 = false;
try {
var10 = true;
this.addPreviouslyRemovedConnectors();
Connector var2 = this.tomcat.getConnector();
if (var2 != null && this.autoStart) {
this.performDeferredLoadOnStartup();
}
this.checkThatConnectorsHaveStarted();
this.started = true;
//控制台看到的日志
logger.info("Tomcat started on port(s): " + this.getPortsDescription(true) + " with context path '" + this.getContextPath() + "'");
var10 = false;
}
}
}
}
private void performDeferredLoadOnStartup() {
try {
//获取container,即TomcatEmbeddedContext
Container[] var1 = this.tomcat.getHost().findChildren();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
Container child = var1[var3];
if (child instanceof TomcatEmbeddedContext) {
((TomcatEmbeddedContext)child).deferredLoadOnStartup();
}
}
}
...
}
TomcatEmbeddedContext
void deferredLoadOnStartup() throws LifecycleException {
this.doWithThreadContextClassLoader(this.getLoader().getClassLoader(), () -> {
this.getLoadOnStartupWrappers(this.findChildren()).forEach(this::load);
});
}
private Stream<Wrapper> getLoadOnStartupWrappers(Container[] children) {
Map<Integer, List<Wrapper>> grouped = new TreeMap();
Container[] var3 = children;
int var4 = children.length;
//这儿能获取到2个子container,1个是前面添加的默认子container,1个是注册DispatcherServlet时封装的子container
for(int var5 = 0; var5 < var4; ++var5) {
Container child = var3[var5];
Wrapper wrapper = (Wrapper)child;
int order = wrapper.getLoadOnStartup();
//收集loadOnStartup>=0的,重要
if (order >= 0) {
((List)grouped.computeIfAbsent(order, (o) -> {
return new ArrayList();
})).add(wrapper);
}
}
return grouped.values().stream().flatMap(Collection::stream);
}
private void load(Wrapper wrapper) {
try {
wrapper.load();
}
...
}
StandardWrapper
public synchronized void load() throws ServletException {
this.instance = this.loadServlet();
if (!this.instanceInitialized) {
this.initServlet(this.instance);
}
}
private synchronized void initServlet(Servlet servlet) throws ServletException {
if (!this.instanceInitialized || this.singleThreadModel) {
//回调spring mvc框架HttpServletBean#init
servlet.init(this.facade);
this.instanceInitialized = true;
}
}
HttpServletBean
public final void init() throws ServletException {
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException var4) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
}
throw var4;
}
}
this.initServletBean();
}
FrameworkServlet
protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring " + this.getClass().getSimpleName() + " '" + this.getServletName() + "'");
if (this.logger.isInfoEnabled()) {
//控制台看到的日志
this.logger.info("Initializing Servlet '" + this.getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = this.initWebApplicationContext();
this.initFrameworkServlet();
} catch (RuntimeException | ServletException var4) {
this.logger.error("Context initialization failed", var4);
throw var4;
}
if (this.logger.isInfoEnabled()) {
//控制台看到的日志
this.logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
protected WebApplicationContext initWebApplicationContext() {
//这儿返回的是AnnotationConfigServletWebServerApplicationContext,外嵌的tomcat
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
this.configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = this.findWebApplicationContext();
}
if (wac == null) {
wac = this.createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
synchronized(this.onRefreshMonitor) {
//重要,这儿就调到了DispatherServlet#onRefresh
this.onRefresh(wac);
}
}
if (this.publishContext) {
String attrName = this.getServletContextAttributeName();
this.getServletContext().setAttribute(attrName, wac);
}
return wac;
}
DispatcherServlet
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
外嵌tomcat完整流程
SPI机制
tomcat在启动过程中加载类路径META-INF/services下的javax.servlet.ServletContainerInitializer文件,实例化并调用onStartup方法,即SpringServletContainerInitializer#onStartup
SpringServletContainerInitializer
//@HandlesTypes,tomcat会把所有WebApplicationInitializer类型的class找到传入onStartup
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public SpringServletContainerInitializer() {
}
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList();
Iterator var4;
if (webAppInitializerClasses != null) {
var4 = webAppInitializerClasses.iterator();
while(var4.hasNext()) {
Class<?> waiClass = (Class)var4.next();
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
//反射创建实例对象
initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());
} catch (Throwable var7) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
} else {
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
var4 = initializers.iterator();
while(var4.hasNext()) {
WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
//调用WebApplicationInitializer#onStartup
initializer.onStartup(servletContext);
}
}
}
}
SpringBootServletInitializer
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(this.getClass());
WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
public void contextInitialized(ServletContextEvent event) {
}
});
} else {
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
}
}
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
//用于构建SpringApplication对象的
SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
builder.main(this.getClass());
ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
}
builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
//设置ApplicationContext=AnnotationConfigServletWebServerApplicationContext
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
//留给子类重写
builder = this.configure(builder);
builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)});
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && MergedAnnotations.from(this.getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) {
application.addPrimarySources(Collections.singleton(this.getClass()));
}
Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
if (this.registerErrorPageFilter) {
application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
}
return this.run(application);
}
}
1.tomcat利用SPI机制调用WebApplicationInitializer#onStartup,即SpringBootServletInitializer
2.创建SpringApplicationBuilder想,用于构建SpringApplication
3.添加ServletContextApplicationContextInitializer初始化器,它用于把servletContext设置到ioc容器,这样在onRefresh时就不用创建tomcat了
4.设置ioc容器类型为AnnotationConfigServletWebServerApplicationContext
5.利用重写的configure(builder)设置主类,主类会被注册为BeanDefinition,如果它上面有@SpringbootApplication还会开启自动配置功能
6.利用builder构建SpringApplication对象,运行起来
7.创建ioc容器并refresh
8.后面的过程和前面的onRefresh、finishRefresh完全一样。
总结
内嵌tomcat:
1.内嵌tomcat需要程序员手动调用SpringApplication.run()
2.onRefresh时要创建tomcat,然后注册DispatcherServlet
3.finishRefresh时开始初始化DispatcherServlet的9大组件
外嵌tomcat:
1.tomcat在启动过程中利用spi机制来执行的SpringApplication.run()
2.主类需要继承SpringBootServletInitializer,重写configure方法
3.onRefresh时不再创建tomcat,直接注册dispatcherServlet
4.finishRefresh时开始初始化DispatcherServlet的9大组件