springboot源码2---内嵌、外嵌tomcat启动流程

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大组件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>