天天看点

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容器的使用和原理