天天看點

SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理

嵌入式Servlet容器

之前我們寫一個web項目的時候,需要将這個項目打包成war包,然後
将這個war包放在配置好servlet容器(Tomcat)裡面運作
但是現在我們編寫SpringBoot項目的時候發現啟動的時候我們
并沒有配置什麼Tomcat,但是使用的卻是Tomcat容器
SpringBoot預設使用Tomcat作為嵌入式的Servlet容器;
從pom配置檔案裡面可以看出來      
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
對于嵌入式的東西,我們關注的問題大概是:      

修改Servlet容器的相關配置

修改和server有關的配置

修改和server有關的配置
(ServerProperties裡面的屬性
【可以發現它也是EmbeddedServletContainerCustomizer
也是使用customize方法将配置修改的】);
//通用的Servlet容器設定
server.xxx
//Tomcat的設定
server.tomcat.xxx      
隻要是配置檔案裡面能配置的屬性,都是對應一個配置類
進入ServerProperties類進行檢視,
可以發現裡面有port端口号進行設定,
contextPath項目通路路徑進行設定
tomcat容器進行設定
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties
    implements EmbeddedServletContainerCustomizer, EnvironmentAware, Ordered {
  private Integer port;
  private String contextPath;
  private final Tomcat tomcat = new Tomcat();
  @Override
  public void customize(ConfigurableEmbeddedServletContainer container) {
    if (getPort() != null) {
      container.setPort(getPort());
    }
  }

點進Tomcat類裡面進行檢視
public static class Tomcat 
{
  private String protocolHeaderHttpsValue = "https";
  private Charset uriEncoding;
}      
比如:
server.context-path=/crud
server.tomcat.uri-encoding=utf-8      

編寫一個EmbeddedServletContainerCustomizer

EmbeddedServletContainerCustomizer:
嵌入式的Servlet容器的定制器;來修改Servlet容器的配置      
@Bean //要将這個定制器加入到容器中
public EmbeddedServletContainerCustomizer myEmbeddedServletContainerCustomizer()
{
    return new EmbeddedServletContainerCustomizer()
    {
        //定制嵌入式的Servlet容器相關的規則
        @Override
        public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer)
        {
            configurableEmbeddedServletContainer.setPort(8083);
        }
    };
}      

注冊servlet三大元件

SpringBoot預設是以jar包的方式啟動嵌入式的Servlet容器
來啟動SpringBoot的web應用,沒有web.xml檔案。
注冊三大元件用以下方式      
//注冊三大元件
    //注冊Servlet
    public ServletRegistrationBean Myservlet()
    {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new MyServlet(),"/myservlet");
        return servletRegistrationBean;
    }
    //注冊filter
    @Bean
    public FilterRegistrationBean MyFilter()
    {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new MyFilter());
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/hello","/myservlet"));
        return filterRegistrationBean;
    }
    //注冊listener
    @Bean
    public ServletListenerRegistrationBean MyListener()
    {
        ServletListenerRegistrationBean<MyListener> myListenerServletListenerRegistrationBean = new ServletListenerRegistrationBean<>(new MyListener());
        return myListenerServletListenerRegistrationBean;
    }      
package jane.test.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author jane
 * @create 2021-01-28 12:46
 */
public class MyServlet extends HttpServlet
{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    {
        resp.getWriter().write("MyServlet...");
    }
}      
package jane.test.filter;

import javax.servlet.*;
import java.io.IOException;

/**
 * @author jane
 * @create 2021-01-29 12:49
 */
public class MyFilter implements Filter
{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException
    {
        System.out.println("MyFilter process...");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy()
    {

    }
}      
package jane.test.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * @author jane
 * @create 2021-01-29 12:59
 */
public class MyListener implements ServletContextListener
{
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent)
    {
        System.out.println("contextInitialized...web應用啟動");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent)
    {
        System.out.println("contextDestroyed...web項目銷毀");
    }
}      

例子

SpringBoot幫我們自動配置SpringMVC的時候,
自動注冊SpringMVC的前端控制器;DIspatcherServlet;
在DispatcherServletAutoConfiguration中:      
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean dispatcherServletRegistration(
    DispatcherServlet dispatcherServlet) {
  ServletRegistrationBean registration = new ServletRegistrationBean(
      dispatcherServlet, this.serverProperties.getServletMapping());
  //預設攔截: / 所有請求;包靜态資源,但是不攔截jsp請求; /*會攔截jsp
  //可以通過server.servletPath來修改SpringMVC前端控制器預設攔截的請求路徑
  registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
  registration.setLoadOnStartup(
      this.webMvcProperties.getServlet().getLoadOnStartup());
  if (this.multipartConfig != null) {
    registration.setMultipartConfig(this.multipartConfig);
  }
  return registration;
}      

修改成其他的servlet容器

SpringBoot還支援Jetty和Undertow這些容器
Jetty主要是用于長連接配接的,比如web聊天之類的
Undertow不支援jsp      
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
SpringBoot預設使用的是嵌入式Tomcat,因為
spring-boot-starter-web預設導入的是spring-boot-starter-tomcat      
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理

修改成jetty

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
     <exclusions>
         <exclusion>
             <artifactId>spring-boot-starter-tomcat</artifactId>
             <groupId>org.springframework.boot</groupId>
         </exclusion>
     </exclusions>
 </dependency>
 <!--導入其他的servlet容器-->
 <dependency>
     <artifactId>spring-boot-starter-jetty</artifactId>
     <groupId>org.springframework.boot</groupId>
 </dependency>      

修改成undertow

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   <exclusions>
       <exclusion>
           <artifactId>spring-boot-starter-tomcat</artifactId>
           <groupId>org.springframework.boot</groupId>
       </exclusion>
   </exclusions>
</dependency>
<!--導入其他的servlet容器-->
<dependency>
   <artifactId>spring-boot-starter-undertow</artifactId>
   <groupId>org.springframework.boot</groupId>
</dependency>      

原理

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {

  /**
   * Nested configuration if Tomcat is being used.
   */
  @Configuration
  //判斷是否引入了tomcat依賴
  @ConditionalOnClass({ Servlet.class, Tomcat.class })
  @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
  //判斷目前容器中有沒有自己定義的EmbeddedServletContainerFactory(嵌入式servlet容器工廠)
  //EmbeddedServletContainerFactory的作用:建立嵌入式servlet容器
  public static class EmbeddedTomcat {

    @Bean
    public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
      return new TomcatEmbeddedServletContainerFactory();
    }

  }      
EmbeddedServletContainerFactory
public interface EmbeddedServletContainerFactory {

  /**
   * Gets a new fully configured but paused {@link EmbeddedServletContainer} instance.
   * Clients should not be able to connect to the returned server until
   * {@link EmbeddedServletContainer#start()} is called (which happens when the
   * {@link ApplicationContext} has been fully refreshed).
   * @param initializers {@link ServletContextInitializer}s that should be applied as
   * the container starts
   * @return a fully configured and started {@link EmbeddedServletContainer}
   * @see EmbeddedServletContainer#stop()
   */
   //擷取嵌入式的servlet容器
  EmbeddedServletContainer getEmbeddedServletContainer(
      ServletContextInitializer... initializers);

}      
工廠也有三種      
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
EmbeddedServletContainer(嵌入式servlet容器)
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
如何做到切換自如
看下面可以看出,當Tomcat導入了依賴,就給容器放進一個TomcatEmbeddedServletContainerFactory
當導入了jetty就給容器放進一個JettyEmbeddedServletContainerFactory
undertow也一樣      
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
//BeanPostProcessorsRegistrar是給容器導入一些元件的
//這裡是導入EmbeddedServletContainerCustomizerBeanPostProcessor:
//後置處理器:bean初始化前後(建立完對象,還沒指派指派)執行初始化工作
public class EmbeddedServletContainerAutoConfiguration {

  /**
   * Nested configuration if Tomcat is being used.
   */
  @Configuration
  @ConditionalOnClass({ Servlet.class, Tomcat.class })
  @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
  public static class EmbeddedTomcat {

    @Bean
    public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
      return new TomcatEmbeddedServletContainerFactory();
    }

  }

  /**
   * Nested configuration if Jetty is being used.
   */
  @Configuration
  @ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
      WebAppContext.class })
  @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
  public static class EmbeddedJetty {

    @Bean
    public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
      return new JettyEmbeddedServletContainerFactory();
    }

  }

  /**
   * Nested configuration if Undertow is being used.
   */
  @Configuration
  @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
  @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
  public static class EmbeddedUndertow {

    @Bean
    public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
      return new UndertowEmbeddedServletContainerFactory();
    }

  }      
以TomcatEmbeddedServletContainerFactory為例
public class TomcatEmbeddedServletContainerFactory
    extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
  @Override
  public EmbeddedServletContainer getEmbeddedServletContainer(
      ServletContextInitializer... initializers) {
    //建立一個Tomcat
    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.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);
    //将配置好的Tomcat傳入進去,傳回一個EmbeddedServletContainer;
    //并且啟動Tomcat伺服器
    return getTomcatEmbeddedServletContainer(tomcat);
  }
}      
進入到getTomcatEmbeddedServletContainer()檢視
  protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
      Tomcat tomcat) {
    return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
  }
繼續跟蹤進去檢視,就是建立一個Tomcat嵌入式servlet容器,并且将Tomcat啟動
  /**
   * Create a new {@link TomcatEmbeddedServletContainer} instance.
   * @param tomcat the underlying Tomcat server
   * @param autoStart if the server should be started
   */
  public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    initialize();
  }

  private void initialize() throws EmbeddedServletContainerException {
    TomcatEmbeddedServletContainer.logger
        .info("Tomcat initialized with port(s): " + getPortsDescription(false));
    synchronized (this.monitor) {
      try {
        addInstanceIdToEngineName();
        try {
          // Remove service connectors to that protocol binding doesn't happen
          // yet
          removeServiceConnectors();

          // Start the server to trigger initialization listeners
          this.tomcat.start();

          // We can re-throw failure exception directly in the main thread
          rethrowDeferredStartupExceptions();

          Context context = findContext();
          try {
            ContextBindings.bindClassLoader(context, getNamingToken(context),
                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) {
          containerCounter.decrementAndGet();
          throw ex;
        }
      }
      catch (Exception ex) {
        throw new EmbeddedServletContainerException(
            "Unable to start embedded Tomcat", ex);
      }
    }
  }      
對嵌入式容器的配置修改如何生效的
之前我們修改容器的配置的時候,是使用ServerProperties裡面的屬性
和使用EmbeddedServletContainerCustomizer
ServerProperties也是EmbeddedServletContainerCustomizer
EmbeddedServletContainerCustomizer定制器修改了Servlet容器的配置
那麼它是如何修改的呢?      
代碼裡面導入了BeanPostProcessorsRegistrar
@Import(BeanPostProcessorsRegistrar.class)
進入檢視,就是導入了嵌入式servlet容器定制器的後置處理器
public static class BeanPostProcessorsRegistrar
      implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
        BeanDefinitionRegistry registry) {
      if (this.beanFactory == null) {
        return;
      }
      registerSyntheticBeanIfMissing(registry,
          "embeddedServletContainerCustomizerBeanPostProcessor",
          EmbeddedServletContainerCustomizerBeanPostProcessor.class);
      registerSyntheticBeanIfMissing(registry,
          "errorPageRegistrarBeanPostProcessor",
          ErrorPageRegistrarBeanPostProcessor.class);
    }
}      
進入EmbeddedServletContainerCustomizerBeanPostProcessor進行檢視      
//初始化之前
public class EmbeddedServletContainerCustomizerBeanPostProcessor
    implements BeanPostProcessor, BeanFactoryAware {
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    //如果目前初始化的是一個ConfigurableEmbeddedServletContainer類型的元件
    if (bean instanceof ConfigurableEmbeddedServletContainer) {
      postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
    }
    return bean;
  }
  
  private void postProcessBeforeInitialization(
    ConfigurableEmbeddedServletContainer bean) {
  //擷取所有的定制器,調用每一個定制器的customize方法來給Servlet容器進行屬性指派;
  for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
    customizer.customize(bean);
    }
  }
  
  private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
  if (this.customizers == null) {
    // Look up does not include the parent context
    this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
        this.beanFactory
    //從容器中擷取所有這葛類型的元件:EmbeddedServletContainerCustomizer
    //定制Servlet容器,給容器中可以添加一個EmbeddedServletContainerCustomizer類型的元件
            .getBeansOfType(EmbeddedServletContainerCustomizer.class,
                false, false)
            .values());
    Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
    this.customizers = Collections.unmodifiableList(this.customizers);
    }
    return this.customizers;
  }
}      
過程
1)、SpringBoot根據導入的依賴情況,給容器中添加相應的
EmbeddedServletContainerFactory,比如TomcatEmbeddedServletContainerFactory
2)、容器中某個元件要建立對象就會驚動後置處理器;
EmbeddedServletContainerCustomizerBeanPostProcessor;
隻要是嵌入式的Servlet容器工廠,後置處理器就工作;
3)、後置處理器,從容器中擷取所有的EmbeddedServletContainerCustomizer,調用定制器的定制方法      

嵌入式Servlet容器啟動原理

上面的步驟之中,如果想要驚動後置處理器,那麼嵌入式容器工廠必須建立對象
後才能驚動後置處理器,那麼
什麼時候建立嵌入式的Servlet容器工廠?
什麼時候擷取嵌入式的Servlet容器并啟動Tomcat?      
擷取嵌入式的Servlet容器工廠:
  1.SpringBoot應用啟動運作run方法
  2.refreshContext(context);SpringBoot重新整理IOC容器
    建立IOC容器對象,并初始化容器,建立容器中的每一個元件;
    如果是web應用建立AnnotationConfigEmbeddedWebApplicationContext,
    否則:AnnotationConfigApplicationContext
  3.refresh(context);重新整理剛才建立好的ioc容器;
  4.onRefresh(); web的ioc容器重寫了onRefresh方法
    webioc容器會建立嵌入式的Servlet容器;
    createEmbeddedServletContainer();
  5.然後會擷取嵌入式的Servlet容器工廠
    EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
    從ioc容器中擷取EmbeddedServletContainerFactory 元件;
    TomcatEmbeddedServletContainerFactory建立對象,
    後置處理器一看是這個對象,
    就擷取所有的定制器來先定制Servlet容器的相關配置;
  6.使用容器工廠擷取嵌入式的Servlet容器:
    this.embeddedServletContainer = containerFactory
      .getEmbeddedServletContainer(getSelfInitializer());
  7.嵌入式的Servlet容器建立對象并啟動Servlet容器;
  後面是:      

外置的Servlet容器

嵌入式Servlet容器:應用打成可執行的jar
優點:簡單、便攜;
缺點:預設不支援JSP、
優化定制比較複雜(使用定制器【ServerProperties、
自定義EmbeddedServletContainerCustomizer】,
自己編寫嵌入式Servlet容器的建立工廠【EmbeddedServletContainerFactory】);      
外置的Servlet容器:外面安裝Tomcat---應用war包的方式打包;
步驟:
    1.必須建立一個war項目;(利用idea建立好目錄結構)
    2.将嵌入式的Tomcat指定為provided;
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
     3.必須編寫一個SpringBootServletInitializer的子類,并調用configure方法
     
package com.jane.springbootjsp;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer
{

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
    {
        //傳入SpringBoot應用的主程式
        return application.sources(SpringBootJspApplication.class);
    }

}
    
    4.啟動伺服器就可以使用;      

原理

兩種不同的方式的過程:
jar包:
  執行SpringBoot主類的main方法,啟動ioc容器,建立嵌入式的Servlet容器;
war包:
  啟動伺服器,伺服器啟動SpringBoot應用(使用SpringBootServletInitializer),
  啟動ioc容器;      
原因:
  在servlet3.0的8.2.4 Shared libraries / runtimes pluggability裡面有說明:

The ServletContainerInitializer class is looked up via the jar services API.
For each application, an instance of the ServletContainerInitializer is
created by the container at application startup time. The framework providing an
implementation of the ServletContainerInitializer MUST bundle in the
META-INF/services directory of the jar file a file called
javax.servlet.ServletContainerInitializer, as per the jar services API,
that points to the implementation class of the ServletContainerInitializer.
In addition to the ServletContainerInitializer we also have an annotation -
HandlesTypes. The HandlesTypes annotation on the implementation of the
ServletContainerInitializer is used to express interest in classes that may
have annotations (type, method or field level annotations) specified in the value of
the HandlesTypes or if it extends / implements one those classes anywhere in the
class’ super types. The HandlesTypes annotation is applied irrespective of the
setting of metadata-complete.

1)、伺服器啟動(web應用啟動)會建立目前web應用裡面
  每一個jar包裡面ServletContainerInitializer執行個體:
2)、ServletContainerInitializer的實作放在jar包的META-INF/services檔案夾下,
  有一個名為javax.servlet.ServletContainerInitializer的檔案,
  内容就是ServletContainerInitializer的實作類的全類名
3)、還可以使用@HandlesTypes,在應用啟動的時候加載我們感興趣的類;      
是以war包的詳細過程是:
1.啟動Tomcat
2.然後會建立org\springframework\spring-web\4.3.14.RELEASE\
  spring-web-4.3.14.RELEASE.jar!\METAINF\services\
  javax.servlet.ServletContainerInitializer:這個檔案裡面的寫的類的執行個體
  Spring的web子產品裡面有這個檔案:内容是:
  org.springframework.web.SpringServletContainerInitializer
3.SpringServletContainerInitializer
  将@HandlesTypes(WebApplicationInitializer.class)
  标注的所有這個類型的類都傳入到onStartup方法的Set>;
  為這些WebApplicationInitializer類型的類建立執行個體;
  然後為每一個WebApplicationInitializer調用自己的onStartup;
  相當于我們的SpringBootServletInitializer的類會被建立對象,
  并執行onStartup方法
4.SpringBootServletInitializer執行個體執行onStartup的時候會
  createRootApplicationContext;建立容器      
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
  //1、建立SpringApplicationBuilder
  SpringApplicationBuilder builder = createSpringApplicationBuilder();
  StandardServletEnvironment environment = new StandardServletEnvironment();
  environment.initPropertySources(servletContext, null);
  builder.environment(environment);
  builder.main(getClass());
  ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
  if (parent != null) {
  this.logger.info("Root context already created (using as parent).");
  servletContext.setAttribute(
  WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
  builder.initializers(new ParentContextApplicationContextInitializer(parent));
  }
  builder.initializers(
  new ServletContextApplicationContextInitializer(servletContext));
  builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
  //調用configure方法,子類重寫了這個方法,将SpringBoot的主程式類傳入了進來
  builder = configure(builder);
  //使用builder建立一個Spring應用
  SpringApplication application = builder.build();
  if (application.getSources().isEmpty() && AnnotationUtils
  .findAnnotation(getClass(), Configuration.class) != null) {
  application.getSources().add(getClass());
  }
  Assert.state(!application.getSources().isEmpty(),
  "No SpringApplication sources have been defined. Either override the "
  + "configure method or add an @Configuration annotation");
  // Ensure error pages are registered
  if (this.registerErrorPageFilter) {
  application.getSources().add(ErrorPageFilterConfiguration.class);
  }
  //啟動Spring應用
  return run(application);
}      
5.Spring的應用就啟動并且建立IOC容器
public ConfigurableApplicationContext run(String... args) {
  StopWatch stopWatch = new StopWatch();
  stopWatch.start();
  ConfigurableApplicationContext context = null;
  FailureAnalyzers analyzers = null;
  configureHeadlessProperty();
  SpringApplicationRunListeners listeners = getRunListeners(args);
  listeners.starting();
  try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(
  args);
  ConfigurableEnvironment environment = prepareEnvironment(listeners,
  applicationArguments);
  Banner printedBanner = printBanner(environment);
  context = createApplicationContext();
  analyzers = new FailureAnalyzers(context);
  prepareContext(context, environment, listeners, applicationArguments,
  printedBanner);
  //重新整理IOC容器
  refreshContext(context);
  afterRefresh(context, applicationArguments);
  listeners.finished(context, null);
  stopWatch.stop();
  if (this.logStartupInfo) {
  new StartupInfoLogger(this.mainApplicationClass)
  .logStarted(getApplicationLog(), stopWatch);
  }
  return context;
  }
  catch (Throwable ex) {
  handleRunFailure(context, listeners, analyzers, ex);
  throw new IllegalStateException(ex);
  }
}      
WebApplicationInitializer的繼承      
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理

idea建立好目錄結構(也可以自己手寫)

SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
然後ok就行了      

idea整合Tomcat伺服器

SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理
SpringBoot架構:嵌入式Servlet容器的修改配置和啟動原理,注冊servlet三大元件,修改成其他的servlet容器的使用和原理,外置的Servlet容器的使用和原理