咱们晓得,springBoot我的项目只需运行main函数即可,那么tomcat是怎么启动的,springmvc又是如何和tomcat绑定的呢?
1.如何启动tomcat
从SpringBoot主类run办法进入,看context = createApplicationContext();
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { long startTime = System.nanoTime(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); //创立web环境 context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } listeners.started(context, timeTakenToStartup); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; } /** * Strategy method used to create the {@link ApplicationContext}. By default this * method will respect any explicitly set application context class or factory before * falling back to a suitable default. * @return the application context (not yet refreshed) * @see #setApplicationContextFactory(ApplicationContextFactory) */ protected ConfigurableApplicationContext createApplicationContext() { return this.applicationContextFactory.create(this.webApplicationType); } private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;@FunctionalInterfacepublic interface ApplicationContextFactory { /** * A default {@link ApplicationContextFactory} implementation that will create an * appropriate context for the {@link WebApplicationType}. */ ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { // 从spring.factories中获取ApplicationFactory的实现类 for (ApplicationContextFactory candidate : SpringFactoriesLoader .loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) { //获取到AnnotationConfigServletWebServerApplicationContext对象 ConfiguraAnnotationConfigServletWebServerApplicationContext对象bleApplicationContext context = candidate.create(webApplicationType); if (context != null) { return context; } } return new AnnotationConfigApplicationContext(); } catch (Exception ex) { throw new IllegalStateException("Unable create a default ApplicationContext instance, " + "you may need a custom ApplicationContextFactory", ex); } };
能够看到ApplicationContextFactory是一个函数式接口,那么applicationContextFactory.create的时候会调用lambda表达式,lamdba表达式里从spring.factories中获取ApplicationFactory的实现类,那么candidate.create(webApplicationType)只能获取到AnnotationConfigServletWebServerApplicationContext
再回到后面,看refreshContext(context)->refresh(context)->applicationContext.refresh();;
AnnotationConfigServletWebServerApplicationContext没有refresh办法,调用的是父类ServletWebServerApplicationContext的refresh办法:
@Override public final void refresh() throws BeansException, IllegalStateException { try { super.refresh(); } catch (RuntimeException ex) { WebServer webServer = this.webServer; if (webServer != null) { webServer.stop(); } throw ex; } }
ServletWebServerApplicationContext调用的也是父类AbstractApplicationContext的refresh办法:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // 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); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // 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(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); contextRefresh.end(); } } }
看onRefresh()办法:
/** * Template method which can be overridden to add context-specific refresh work. * Called on initialization of special beans, before instantiation of singletons. * <p>This implementation is empty. * @throws BeansException in case of errors * @see #refresh() */ protected void onRefresh() throws BeansException { // For subclasses: do nothing by default. }
空办法,交由子类实现,也就是ServletWebServerApplicationContext:
@Override protected void onRefresh() { //父类办法初始化了web我的项目的ui主题 super.onRefresh(); try { //创立web容器 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); //进入这里 if (webServer == null && servletContext == null) { //创立一个DefaultStartupStep StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create"); //获取TomcatServletWebServerFactory ServletWebServerFactory factory = getWebServerFactory(); createWebServer.tag("factory", factory.getClass().toString()); //获取TomcatWebServer this.webServer = factory.getWebServer(getSelfInitializer()); createWebServer.end(); getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer)); getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer)); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
首先看getWebServerFactory是如何获取TomcatServletWebServerFactory的:
/** * Returns the {@link ServletWebServerFactory} that should be used to create the * embedded {@link WebServer}. By default this method searches for a suitable bean in * the context itself. * @return a {@link ServletWebServerFactory} (never {@code null}) */ protected ServletWebServerFactory getWebServerFactory() { // Use bean names so that we don't consider the hierarchy //从beanFactory获取ServletWebServerFactory的名称 String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);//只能有一个 if (beanNames.length == 0) { throw new MissingWebServerFactoryBeanException(getClass(), ServletWebServerFactory.class, WebApplicationType.SERVLET); } if (beanNames.length > 1) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); }//获取该beanFactory return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }
能够看到ServletWebServerFactory的实现类有很多个,springBoot默认的实现是TomcatServletWebServerFactory,
它是什么时候退出bean工厂的呢?通过搜寻得悉它是在ServletWebServerFactoryConfiguration中被注册的:
咱们再看this.webServer = factory.getWebServer(getSelfInitializer());
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } Tomcat tomcat = new Tomcat(); File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); for (LifecycleListener listener : this.serverLifecycleListeners) { tomcat.getServer().addLifecycleListener(listener); } Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); 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 getTomcatWebServer(tomcat); } /** * Factory method called to create the {@link TomcatWebServer}. Subclasses can * override this method to return a different {@link TomcatWebServer} or apply * additional processing to the Tomcat server. * @param tomcat the Tomcat server. * @return a new {@link TomcatWebServer} instance */ protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { return new TomcatWebServer(tomcat, getPort() >= 0); } /** * Create a new {@link TomcatWebServer} instance. * @param tomcat the underlying Tomcat server * @param autoStart if the server should be started */ public TomcatWebServer(Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; initialize(); } private void initialize() throws WebServerException { logger.info("Tomcat initialized with port(s): " + getPortsDescription(false)); synchronized (this.monitor) { try { addInstanceIdToEngineName(); Context context = findContext(); context.addLifecycleListener((event) -> { if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) { // Remove service connectors so that protocol binding doesn't // happen when the service is started. removeServiceConnectors(); } }); // Start the server to trigger initialization listeners // 启动tomcat this.tomcat.start(); // We can re-throw failure exception directly in the main thread rethrowDeferredStartupExceptions(); try { ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); } catch (NamingException ex) { // Naming is not enabled. Continue } // Unlike Jetty, all Tomcat threads are daemon threads. We create a // blocking non-daemon to stop immediate shutdown startDaemonAwaitThread(); } catch (Exception ex) { stopSilently(); throw new WebServerException("Unable to start embedded Tomcat", ex); } } }
初始化了一个tomcat并启动。
2.tomcat如何注册DispatchServlet
回到this.webServer = factory.getWebServer(getSelfInitializer());
这行代码,看getSelfInitializer();
/** * Returns the {@link ServletContextInitializer} that will be used to complete the * setup of this {@link WebApplicationContext}. * @return the self initializer * @see #prepareWebApplicationContext(ServletContext) */ private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() { return this::selfInitialize; } private void selfInitialize(ServletContext servletContext) throws ServletException { prepareWebApplicationContext(servletContext); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes( beanFactory); WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, getServletContext()); existingScopes.restore(); WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, getServletContext()); // 获取ServletContextInitializer实现类 for (ServletContextInitializer beans : getServletContextInitializerBeans()) {// 注册dispatchServlet beans.onStartup(servletContext); } } @SafeVarargs public ServletContextInitializerBeans(ListableBeanFactory beanFactory, Class<? extends ServletContextInitializer>... initializerTypes) { this.initializers = new LinkedMultiValueMap<>(); this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes) : Collections.singletonList(ServletContextInitializer.class); // 这里实例化了dispatchServlet addServletContextInitializerBeans(beanFactory); addAdaptableBeans(beanFactory); List<ServletContextInitializer> sortedInitializers = this.initializers.values() .stream() .flatMap((value) -> value.stream() .sorted(AnnotationAwareOrderComparator.INSTANCE)) .collect(Collectors.toList()); this.sortedList = Collections.unmodifiableList(sortedInitializers); } private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {// 实例化ServletContextInitializer的实现类 for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType( beanFactory, ServletContextInitializer.class)) { addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory); } }
ServletContextInitializer的实现类中有个dispatcherServletRegistration,就是讲dispatchServlet注册到tomcat的;
再回到initializer.onStartup办法:
@Override public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException { try { for (ServletContextInitializer initializer : this.initializers) { //注册dispatchServlet initializer.onStartup(servletContext); } } catch (Exception ex) { this.startUpException = ex; // Prevent Tomcat from logging and re-throwing when we know we can // deal with it in the main thread, but log for information here. if (logger.isErrorEnabled()) { logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: " + ex.getMessage()); } } }
这里的initializer就是dispatcherServletRegistration,然而他没有onStartup办法,它调用的是它的父类RegistrationBean这个实现类:
@Override public final void onStartup(ServletContext servletContext) throws ServletException { String description = getDescription(); if (!isEnabled()) { logger.info(StringUtils.capitalize(description) + " was not registered (disabled)"); return; } //注册 register(description, servletContext); }
由子类DynamicRegistrationBean实现:
@Override protected final void register(String description, ServletContext servletContext) { //注册 D registration = addRegistration(description, servletContext); if (registration == null) { logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)"); return; } configure(registration); }
addRegistration由子类ServletRegistrationBean实现:
能够看到,这里将dispatchServlet注册进了tomcat的servlet。