Skip to content

Latest commit

 

History

History
1374 lines (941 loc) · 46.3 KB

12.第12章-SpringBoot源码-自动配置原理和内嵌Tomcat启动原理.md

File metadata and controls

1374 lines (941 loc) · 46.3 KB

第12章-SpringBoot源码-自动配置原理和内嵌Tomcat启动原理

嵌入式Tomcat与Spring整合

测试类

测试项目目录

springboot-first
├── common_usetree.txt
├── pom.xml
├── springboot-first.iml
├── src/
|  ├── main/
|  |  ├── java/
|  |  |  └── cn/
|  |  |     └── imlql/
|  |  |        └── boot/
|  |  |           ├── config/
|  |  |           |  ├── SpringConfig.java
|  |  |           |  └── SpringMVCConfig.java
|  |  |           ├── controller/
|  |  |           |  └── HelloController.java
|  |  |           ├── Main.java
|  |  |           ├── QuickAppStarter.java
|  |  └── resources/
|  └── test/
|     └── java/
└── work/
   └── Tomcat/
      └── localhost/
         └── boot/

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>springboot-first</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.5</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>8.5.64</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>8.5.64</version>
        </dependency>


    </dependencies>

</project>

Main

package cn.imlql.boot;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;

public class Main {

    public static void main(String[] args) throws LifecycleException {
        //自己写Tomcat的启动源码
        Tomcat tomcat = new Tomcat();

        tomcat.setPort(8888);
        tomcat.setHostname("localhost");
        tomcat.setBaseDir(".");
        // user.dir代表当前工作目录
        Context context = tomcat.addWebapp("/boot", System.getProperty("user.dir") + "/src/main");
        tomcat.start();//启动tomcat 注解版MVC利用Tomcat SPI机制


        tomcat.getServer().await(); //服务器等待

    }

}

QuickAppStarter

package cn.imlql.boot;

import cn.imlql.boot.config.SpringConfig;

import cn.imlql.boot.config.SpringMVCConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import javax.servlet.ServletRegistration;


/**
 * 最快速的整合注解版SpringMVC和Spring的
 */
public class QuickAppStarter extends AbstractAnnotationConfigDispatcherServletInitializer {
   @Override //根容器的配置(Spring的配置文件===Spring的配置类)
   protected Class<?>[] getRootConfigClasses() {
      return new Class<?>[]{SpringConfig.class};
   }

   @Override //web容器的配置(SpringMVC的配置文件===SpringMVC的配置类)
   protected Class<?>[] getServletConfigClasses() {
      return new Class<?>[]{SpringMVCConfig.class};
   }

   @Override //Servlet的映射,DispatcherServlet的映射路径
   protected String[] getServletMappings() {
      return new String[]{"/"};
   }

   @Override
   protected void customizeRegistration(ServletRegistration.Dynamic registration) {
//    super.customizeRegistration(registration);

//    registration.addMapping("");//
   }
}

SpringConfig

@ComponentScan(value = "cn.imlql.boot",excludeFilters = {
      @ComponentScan.Filter(type= FilterType.ANNOTATION,value = Controller.class)
})
@Configuration
public class SpringConfig {
   //Spring的父容器

}

SpringMVCConfig

@ComponentScan(value = "cn.imlql.boot",includeFilters = {
      @ComponentScan.Filter(type= FilterType.ANNOTATION,value = Controller.class)
},useDefaultFilters = false)
public class SpringMVCConfig {
   //SpringMVC的子容器,能扫描的Spring容器中的组件


}

HelloController

package cn.imlql.boot.controller;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello66")
    public String hello(){

        return "66666666~~~~~";
    }
}

测试效果

再来简单捋一下SPI如何启动的Web容器

  1. 我们看到上面很神奇的效果,我们自己写代码做到了类似SpringBoot的效果,不需要配置本地tomcat,直接就把Web应用启动起来了。
  2. 这里只是简单的捋一下,详细过程在前面讲过

META-INF/services

AbstractAnnotationConfigDispatcherServletInitializer继承树

我们的QuickAppStarter实现了

SpringServletContainerInitializer

利用Java的SPI加载META-INF/services下的实现类

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

   /**
    * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
    * implementations present on the application classpath.
    * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
    * Servlet 3.0+ containers will automatically scan the classpath for implementations
    * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
    * such types to the {@code webAppInitializerClasses} parameter of this method.
    * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
    * this method is effectively a no-op. An INFO-level log message will be issued notifying
    * the user that the {@code ServletContainerInitializer} has indeed been invoked but that
    * no {@code WebApplicationInitializer} implementations were found.
    * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
    * they will be instantiated (and <em>sorted</em> if the @{@link
    * org.springframework.core.annotation.Order @Order} annotation is present or
    * the {@link org.springframework.core.Ordered Ordered} interface has been
    * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
    * method will be invoked on each instance, delegating the {@code ServletContext} such
    * that each instance may register and configure servlets such as Spring's
    * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
    * or any other Servlet API componentry such as filters.
    * @param webAppInitializerClasses all implementations of
    * {@link WebApplicationInitializer} found on the application classpath
    * @param servletContext the servlet context to be initialized
    * @see WebApplicationInitializer#onStartup(ServletContext)
    * @see AnnotationAwareOrderComparator
    */
  	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = Collections.emptyList();

		if (webAppInitializerClasses != null) {
			initializers = new ArrayList<>(webAppInitializerClasses.size());
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says... 所有的非接口非抽象的WebApplicationInitializer实现类
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer) //集合负责保存满足上面条件的类
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}
		//下面会遍历所有满足要求的WebApplicationInitializer,调用他们的onStartup
		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext); //所有的 WebApplicationInitializer 的 onStartup
		}
	}


}

@HandlesTypes

  1. 其中@HandlesTypes注解表示可以处理的类,在onStartup 方法中,可以通过Set<Class<?>> webAppInitializerClasses 获取得到。
  2. @HandlesTypes属于sun公司对Servlet定义的规范,包括tomcat,jetty等服务器都对它有不同的实现
  3. tomcat的具体实现咱们这里不深究,可以肯定的是一定用到了Java的SPI,如下。
ServiceLoader<DataSaveService> load = ServiceLoader.load(WebApplicationInitializer.class);
  1. tomcat具体对于@HandlesTypes一定是和上面类似甚至是一样的代码来加载WebApplicationInitializer的实现

因为咱们的QuickAppStarter继承的AbstractAnnotationConfigDispatcherServletInitializer也属于WebApplicationInitializer,所以它就会被加载

Servlet相关规范

  1. tomcat会遵循sun公司的规范给每一个Servlet创建对象

  2. 所以DispatcherServlet肯定也会创建对象

  3. Servlet的规范

    1. Servlet创建对象
    2. Servlet调用Init初始化
    3. 每次请求调用service处理
    4. tomcat停止的时候调用destroy进行销毁
  4. Serlvet是被谁调用开始初始化的属于tomcat的源码,我们这里不研究

DispatcherServlet

  1. spring-web中有一个叫DispatcherServlet的类,很明显他是一个Servlet,所以tomcat启动的时候就会加载它,加载它的话当然是从父类一层一层加载的

  1. 也就是说是从Servlet最顶层开始一层一层往下面调用
  2. 最终我们发现FrameworkServlet里有一个核心方法

FrameworkServlet

    /** 追踪看web应用启动做了什么。
     * Overridden method of {@link HttpServletBean}, invoked after any bean properties
     * have been set. Creates this servlet's WebApplicationContext.
     */
    @Override
    protected final void initServletBean() throws ServletException {
       getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
       if (logger.isInfoEnabled()) {
          logger.info("Initializing Servlet '" + getServletName() + "'");
       }
       long startTime = System.currentTimeMillis();

       try {
          this.webApplicationContext = initWebApplicationContext(); //初始化WebIOC容器,那我们想一下大概率是在这里启动的IOC容器
          initFrameworkServlet(); //这又是留给子类的
       }
       catch (ServletException | RuntimeException ex) {
          logger.error("Context initialization failed", ex);
          throw ex;
       }

       if (logger.isDebugEnabled()) {
          String value = this.enableLoggingRequestDetails ?
                "shown which may lead to unsafe logging of potentially sensitive data" :
                "masked to prevent unsafe logging of potentially sensitive data";
          logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
                "': request parameters and headers will be " + value);
       }

       if (logger.isInfoEnabled()) {
          logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
       }
    }
  1. this.webApplicationContext = initWebApplicationContext();没错,看名字就知道是从这里开始启动Web容器的。

  2. 然后我们就自己搭建了一个MySpringBoot项目,我们这个项目和SpringBoot官方的区别就是官方帮我们封装了很多自动配置类,帮我们给容器中放了很多组件,使得我们感觉开发更方便了。

SpringBoot启动导入了很多自动配置类

为什么 @SpringBootApplication +SpringApplication.run(SpringbootSourceApplication.class, args);能把Spring+SpringMVC+Tomcat+其他场景都整合进来

@SpringBootApplication
public class SpringbootSourceApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootSourceApplication.class, args);
    }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.atuigu.boot</groupId>
    <artifactId>springboot-source</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-source</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
  1. 首先是在Maven依赖上的支持,spring-boot-starter-xxx的这种依赖内部又导入了很多的依赖,包括上面说的嵌入式tomcat,以及Spring,SpringMVC

@SpringBootApplication原理

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	//......
}

@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

   @AliasFor(annotation = Configuration.class)
   boolean proxyBeanMethods() default true;

}

这个注解的功能就相当于@Configuration

@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	//......
}

@AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

	//......
}

@AutoConfigurationPackage导入的AutoConfigurationPackages.Registrar类

AutoConfigurationPackages.Registrar#registerBeanDefinitions()

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
   }

   @Override
   public Set<Object> determineImports(AnnotationMetadata metadata) {
      return Collections.singleton(new PackageImports(metadata));
   }

}

这里就是得到要注册哪些包下的信息,F7进入此方法。从这里你也能知道SpringBoot默认导的包是SpringbootXXXApplication所在的那个包层级

AutoConfigurationPackages#register()

    public static void register(BeanDefinitionRegistry registry, String... packageNames) {
       if (registry.containsBeanDefinition(BEAN)) {
          BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
          beanDefinition.addBasePackages(packageNames);
       }
       else {
          registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
       }
    }

	private static final String BEAN = AutoConfigurationPackages.class.getName();

AutoConfigurationPackages.BasePackagesBeanDefinition

static final class BasePackagesBeanDefinition extends GenericBeanDefinition {

   private final Set<String> basePackages = new LinkedHashSet<>();

   BasePackagesBeanDefinition(String... basePackages) {
      setBeanClass(BasePackages.class);
      setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
      addBasePackages(basePackages);//就是要指定最终要扫哪些包
   }

   @Override
   public Supplier<?> getInstanceSupplier() {
      return () -> new BasePackages(StringUtils.toStringArray(this.basePackages));
   }

   private void addBasePackages(String[] additionalBasePackages) {
      this.basePackages.addAll(Arrays.asList(additionalBasePackages));
   }

}

BeanDefinitionMap里的数据

此时beanDefinitionMap已经有了AutoConfigurationPackages,当处理到这个Bean的时候,最终发现这是个包导入的组件,最终就会导入这个包里面的组件

@EnableAutoConfiguration注解导入的AutoConfigurationImportSelector类

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	
          //......
          
}
  1. AutoConfigurationImportSelector是用@Import注解导进来的
  2. AutoConfigurationImportSelector根据它的名字很明显它是一个ImportSelector的实现类,了解ImportSelector的都应该知道它是通过selectImports()方法来实现导入哪些组件的

AutoConfigurationImportSelector.AutoConfigurationGroup#process()

F7进入此方法

AutoConfigurationImportSelector#getAutoConfigurationEntry()

F7进入此方法

AutoConfigurationImportSelector#getCandidateConfigurations()

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
       List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
             getBeanClassLoader());
       Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
             + "are using a custom packaging, make sure that file is correct.");
       return configurations;
    }

	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

SpringFactoriesLoader#loadFactoryNames()

SpringFactoriesLoader#loadSpringFactories()加载类路径下META-INF/spring.factories的资源

	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
       Map<String, List<String>> result = cache.get(classLoader);
       if (result != null) {
          return result;
       }

       result = new HashMap<>();
       try {
          //加载类路径下META-INF/spring.factories的资源
          Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
          while (urls.hasMoreElements()) {
             URL url = urls.nextElement();
             UrlResource resource = new UrlResource(url);
             Properties properties = PropertiesLoaderUtils.loadProperties(resource);
             for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                String[] factoryImplementationNames =
                      StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                   result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                         .add(factoryImplementationName.trim());
                }
             }
          }

          // Replace all lists with unmodifiable lists containing unique elements
          result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
          cache.put(classLoader, result);
       }
       catch (IOException ex) {
          throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
       }
       return result;
    }

然后咱们就要找类路径下META-INF/spring.factories,并且名字是org.springframework.boot.autoconfigure.EnableAutoConfiguration的资源,这有点类似于SPI机制

spring.factories

  1. 不止这个包下有spring.factories文件,可能很多第三方的starter都有,这个包下的这些类只是Spring能想到的常用的组件。

返回到AutoConfigurationImportSelector#getAutoConfigurationEntry()

  1. 自此这130个组件会先被放到List里,但不一定全部导入

  1. 然后这里会有一个过滤configurations = getConfigurationClassFilter().filter(configurations);

  1. 最终这里只会有23个组件被放到容器中,为什么这里要过滤?看下面的@ConditionalOnClass注解,当容器中有KafkaTemplate这个类时才会导入KafkaAutoConfiguration,而KafkaTemplate这个类只有导入了kafka相关jar包才会有。意思就是你只有在maven中导入了相关jar包,才会给你自动配置

还有下面这个SpringMvc的,当你有DispatcherServlet这个类的时候,才会给你自动配置web相关的东西。而有DispatcherServlet类就代表你导入了web的相关依赖

容器刷新在onRefresh步骤会启动Tomcat

  1. 在刚开始的时候我们自己实现的简易SpringBoot是利用SPI机制启动的Web容器
  2. 其实我们还要一个方法就是自己创建一个DispatcherServlet注册到Tomcat里,然后Tomcat就会调用Servlet相关初始化,最终调用到FrameworkServlet类里调用的this.webApplicationContext = initWebApplicationContext();进而启动Web容器。在SpringBoot里使用的就是这种方式启动Web容器

DispatcherServletAutoConfiguration

package org.springframework.boot.autoconfigure.web.servlet;


@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
//这里就是DispatcherServlet在自动配置之前,先自动配置ServletWebServerFactoryAutoConfiguration
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

   /**
    * The bean name for a DispatcherServlet that will be mapped to the root URL "/".
    */
   public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

   /**
    * The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
    */
   public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

   @Configuration(proxyBeanMethods = false)
   @Conditional(DefaultDispatcherServletCondition.class)
   @ConditionalOnClass(ServletRegistration.class)
   @EnableConfigurationProperties(WebMvcProperties.class)
   protected static class DispatcherServletConfiguration {

      @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
      public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
         DispatcherServlet dispatcherServlet = new DispatcherServlet();
         dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
         dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
         dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
         dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
         dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
         return dispatcherServlet;
      }

      @Bean
      @ConditionalOnBean(MultipartResolver.class)
      @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
      public MultipartResolver multipartResolver(MultipartResolver resolver) {
         // Detect if the user has created a MultipartResolver but named it incorrectly
         return resolver;
      }

   }

   @Configuration(proxyBeanMethods = false)
   @Conditional(DispatcherServletRegistrationCondition.class)
   @ConditionalOnClass(ServletRegistration.class)
   @EnableConfigurationProperties(WebMvcProperties.class)
   @Import(DispatcherServletConfiguration.class)
   protected static class DispatcherServletRegistrationConfiguration {

      @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
      @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
      public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
            WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
         DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
               webMvcProperties.getServlet().getPath());
         registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
         registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
         multipartConfig.ifAvailable(registration::setMultipartConfig);
         return registration;
      }

   }

   @Order(Ordered.LOWEST_PRECEDENCE - 10)
   private static class DefaultDispatcherServletCondition extends SpringBootCondition {
		// ......
   }

   @Order(Ordered.LOWEST_PRECEDENCE - 10)
   private static class DispatcherServletRegistrationCondition extends SpringBootCondition {
		// ......
   }

}
  1. @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
  2. 这里就是最关键的,@AutoConfigureAfter注解看名字就能大概明白是什么意思,这里就是DispatcherServlet在自动配置之前,先自动配置ServletWebServerFactoryAutoConfiguration

ServletWebServerFactoryAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, //这里是最核心的
      ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, //这里都是嵌入式服务器
      ServletWebServerFactoryConfiguration.EmbeddedJetty.class, //这里都是嵌入式服务器
      ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) //这里都是嵌入式服务器
public class ServletWebServerFactoryAutoConfiguration {
	// ......
}

ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
          BeanDefinitionRegistry registry) {
       if (this.beanFactory == null) {
          return;
       }
       //给容器中注册一个服务器的后置处理器
       registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
             WebServerFactoryCustomizerBeanPostProcessor.class,
             WebServerFactoryCustomizerBeanPostProcessor::new);
       registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
             ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);
    }

ServletWebServerFactoryConfiguration

package org.springframework.boot.autoconfigure.web.servlet;

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

   @Configuration(proxyBeanMethods = false)
   @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
   @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
   static class EmbeddedTomcat {

      @Bean
      TomcatServletWebServerFactory tomcatServletWebServerFactory(
            ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
            ObjectProvider<TomcatContextCustomizer> contextCustomizers,
            ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
         TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
         factory.getTomcatConnectorCustomizers()
               .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
         factory.getTomcatContextCustomizers()
               .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
         factory.getTomcatProtocolHandlerCustomizers()
               .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
         return factory;
      }

   }

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

      @Bean
      JettyServletWebServerFactory JettyServletWebServerFactory(
            ObjectProvider<JettyServerCustomizer> serverCustomizers) {
         JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
         factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
         return factory;
      }

   }

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

      @Bean
      UndertowServletWebServerFactory undertowServletWebServerFactory(
            ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
            ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
         UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
         factory.getDeploymentInfoCustomizers()
               .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
         factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
         return factory;
      }

      @Bean
      UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
         return new UndertowServletWebServerFactoryCustomizer(serverProperties);
      }

   }

}

ServletWebServerFactory:服务器工厂,我们可以自己放Serlvet容器,我们自己放了就会用我们自己的

XXXProvider的意思就是这些方法的参数都是从容器中拿,如果你自定义了,就用自定义的

TomcatServletWebServerFactory自己new Tomcat()

    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());
       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);
    }

Debug调用栈

ServletWebServerApplicationContext

    @Override
    protected void onRefresh() {
       super.onRefresh();
       try {
          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) {
			StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
			ServletWebServerFactory factory = getWebServerFactory();
			createWebServer.tag("factory", factory.getClass().toString());
             //最终在这里调用了TomcatServletWebServerFactory#getWebServer()
			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();
	}

返回到getWebServer并且调用prepareContext()

注意看ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);这一步就是Tomcat启动加载DispatcherServlet的时机

Tomcat启动加载DispatcherServlet的时机

package org.springframework.boot.autoconfigure.web.servlet;

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

   /**
    * The bean name for a DispatcherServlet that will be mapped to the root URL "/".
    */
   public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

   /**
    * The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
    */
   public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

   @Configuration(proxyBeanMethods = false)
   @Conditional(DefaultDispatcherServletCondition.class)
   @ConditionalOnClass(ServletRegistration.class)
   @EnableConfigurationProperties(WebMvcProperties.class)
   protected static class DispatcherServletConfiguration {

      @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
      public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
         DispatcherServlet dispatcherServlet = new DispatcherServlet();
         dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
         dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
         dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
         dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
         dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
         return dispatcherServlet;
      }

      @Bean
      @ConditionalOnBean(MultipartResolver.class)
      @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
      public MultipartResolver multipartResolver(MultipartResolver resolver) {
         // Detect if the user has created a MultipartResolver but named it incorrectly
         return resolver;
      }

   }

   @Configuration(proxyBeanMethods = false)
   @Conditional(DispatcherServletRegistrationCondition.class)
   @ConditionalOnClass(ServletRegistration.class)
   @EnableConfigurationProperties(WebMvcProperties.class)
   @Import(DispatcherServletConfiguration.class)
   protected static class DispatcherServletRegistrationConfiguration {

      @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
      @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
      //注意看DispatcherServletRegistrationBean
      public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
            WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
         DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
               webMvcProperties.getServlet().getPath());
         registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
         registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
         multipartConfig.ifAvailable(registration::setMultipartConfig);
         return registration;
      }

   }

	// ......
}

DispatcherServletRegistrationBean继承树

我们发现DispatcherServletRegistrationBean它是一个ServletContextInitializer

F7进入上面说的这个configureContext(context, initializersToUse);

TomcatServletWebServerFactory#configureContext()

    protected void configureContext(Context context, ServletContextInitializer[] initializers) {
       TomcatStarter starter = new TomcatStarter(initializers);//注意在这里把这些ServletContextInitializer给了TomcatStarter
       // ......
       for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
          customizer.customize(context);
       }
    }

返回到TomcatServletWebServerFactory#getWebServer()

然后返回到getWebServer调用最后一步

TomcatStarter#onStartup()

最终初始化会调用到onStartup()

    TomcatStarter(ServletContextInitializer[] initializers) {
       this.initializers = initializers;
    }

    @Override
    public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
       try {
          for (ServletContextInitializer initializer : this.initializers) {
             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());
          }
       }
    }

RegistrationBean#onStartup()

F7进入,省略到一些不重要的方法

DynamicRegistrationBean#register()

    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);
    }

ServletRegistration.Dynamic#addRegistration()将dispatcherServlet放入Tomcat容器中

servletContext这个就是tomcat容器

  1. ServletRegistration.Dynamic#addRegistration()将dispatcherServlet放入tomcat容器中

  2. 然后Tomcat启动之后自然就调用Servelt初始化,进而调到了dispatcherServlet,然后就是之前讲过的初始化web容器。

    this.webApplicationContext = initWebApplicationContext();

得到下面的结论

@SpringBootApplication
public class SpringbootSourceApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootSourceApplication.class, args);
    }

    @Bean  //所有的xxxRegistrationBean都是允许我们注册原生的Servlet组件进去,
    //利用 ServletContextInitializer在Tomcat启动完成以后进行回调的机制
    ServletRegistrationBean<HelloServlet> registrationBean(){

        ServletRegistrationBean<HelloServlet> registrationBean = new ServletRegistrationBean<>(new HelloServlet());
        registrationBean.addUrlMappings("/he66");
        return registrationBean;
    }

}

SpringApplication的run方法

    public ConfigurableApplicationContext run(String... args) {
       StopWatch stopWatch = new StopWatch();
       stopWatch.start();
       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);
          //创建容器
          context = createApplicationContext();
          context.setApplicationStartup(this.applicationStartup);
          prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
          //刷新容器
          refreshContext(context);
          afterRefresh(context, applicationArguments);
          stopWatch.stop();
          if (this.logStartupInfo) {
             new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
          }
          listeners.started(context);
          callRunners(context, applicationArguments);
       }
       catch (Throwable ex) {
          handleRunFailure(context, ex, listeners);
          throw new IllegalStateException(ex);
       }

       try {
          listeners.running(context);
       }
       catch (Throwable ex) {
          handleRunFailure(context, ex, null);
          throw new IllegalStateException(ex);
       }
       return context;
    }

就是这样很简单