SpringBoot 启动原理

启动类

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

@SpringBootApplication

@SpringBootApplication标注在某个类,说明这个类是SpringBoot的主配置类。

查看@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 {
    ......
}

@EnableAutoConfiguration由下面两个注解组成

@AutoConfigurationPackage

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

}

用来自动配置包,通过@Import注解,指定使用AutoConfigurationPackages.Registrar.class往容器中注册组件。

// AutoConfigurationPackages
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    ......
    // AnnotationMetadata metadata为主配置类的注解信息
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 该方法主要是往容器中注入了一个组件, 组件保存了主配置类所在的包名, 这个包名为自动扫描的包名
        register(registry, new PackageImport(metadata).getPackageName());
    }
    ......
}

@AutoConfigurationImportSelector

@AutoConfigurationImportSelector注解也是往容器中注册组件。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    /**
     * 返回值就是导入到容器中的全类名, 这些类就会被注册到容器中
     */        
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        // 这一步会获取到所有的自动配置类
        // 它会从类路径中拿到所有名为META-INF/spring.factories的配置文件,获取EnableAutoConfiguration指定的值。
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                annotationMetadata);
        // 返回获取到的自动配置类
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

例如,查看spring-boot-autoconfigure包下的META-INF/spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
......

此时SpringApplicationAdminJmxAutoConfigurationAopAutoConfiguration等组件就会被注册到容器中。

这些XxxAutoConfiguration是用来自动往容器中注入组件的。

自动配置类

从上面的分析我们可以知道,SpringBoot会收集META-INF/spring.factories中的自动配置类,下面来分析一下自动配置类的作用。

自动配置常用注解

@Configuration :指定这个类是一个配置类

@ConditionalOnClass :当类路径classpath下有指定的类的情况下进行自动配置

@ConditionalOnMissingClass:当类路径下没有指定的类的条件下

@ConditionalOnBean:当容器(Spring Context)中有指定的Bean的条件下

@ConditionalOnMissingBean:当容器(Spring Context)中没有指定Bean的情况下进行自动配置

@ConfigurationProperties :结合相关XxxProperties来绑定相关配置, 主要用来把properties配置文件转化为对应的XxxProperties来使用的, 并不会把该类放入到IOC容器中

@EnableConfigurationProperties(XxxProperties.class) :注解的作用是使@ConfigurationProperties注解生效并且将XxxProperties注入到IOC容器中。

如果在每个Properties上都使用@Component来标注,那么也不需要使用@EnableConfigurationProperties({XxxProperties.class})来标注了,同样也可以在Spring上下文容器中也能获取到XxxProperties对应的Bean。

@ConditionalOnProperty(prefix = “spring.person”, value = “enabled”, matchIfMissing = true)
当配置文件中spring.person.enabled=true时进行自动配置,如果没有设置此值就默认使用matchIfMissing对应的值。如果不设置matchIfMissing = true, 默认为false。

HttpEncodingAutoConfiguration为例,当满足一些条件时,它会自动往ioc容器中注入组件。

// 表示这是一个配置类
@Configuration(proxyBeanMethods = false)
// 将HttpProperties中的属性与配置文件进行绑定, 并注入ioc容器中
@EnableConfigurationProperties(HttpProperties.class)
// web应用类型是SERVLET
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 当前类路径不存在CharacterEncodingFilter.class
@ConditionalOnClass(CharacterEncodingFilter.class)
// 当配置文件中spring.http.encoding.enabled=true时进行自动配置,如果没有设置此值就默认使用matchIfMissing对应的值。如果不设置matchIfMissing = true, 默认为false。
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
    private final HttpProperties.Encoding properties;
    public HttpEncodingAutoConfiguration(HttpProperties properties) {
        this.properties = properties.getEncoding();
    }
    @Bean
    // 容器中不存在CharacterEncodingFilter类型的bean时,这个bean才会生效
    @ConditionalOnMissingBean 
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
        return filter;
    }
}

run()

  • 准备环境

    • 执行ApplicationContextInitializer.initialize()方法
    • 监听器回调SpringApplicationRunListener.contextPrepared()方法
    • 加载主配置类定义信息
    • 监听器回调SpringApplicationRunListener.contextLoaded()方法
  • 刷新IOC容器

    • 扫描加载所有容器中的组件
    • 包括META-INF/spring.factories下的EnableAutoConfiguration组件
  • 回调容器中所有的ApplicationRunner、CommandLineRunner的run方法

  • 回调所有SpringApplicationRunListener.running(context)方法

创建SpringApplication对象

查看启动类的run方法,将会看到新建一个SpringApplication, 并将配置类传递进去

// SpringApplication.java
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return (new SpringApplication(primarySources)).run(args);
}

查看创建SpringApplication对象过程

// SpringApplication.java
public SpringApplication(Class... primarySources) {
    this((ResourceLoader)null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = new HashSet();
    this.isCustomEnvironment = false;
    this.lazyInitialization = false;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 保存配置类
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    // 获取当前应用类型: NONE, SERVLET, REACTIVE;
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 从类路径下寻找META-INF/spring.factories中的所有ApplicationContextInitializer并保存起来
  this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
     // 从类路径下寻找META-INF/spring.factories中的所有ApplicationListener并保存起来
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    // 从多个配置类中找到有main方法的主配置类
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

执行SpringApplication.run()

// SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    this.configureHeadlessProperty();
    // 获取SpringApplicationRunListener
    // 从类路径下寻找META-INF/spring.factories中的所有SpringApplicationRunListener并保存起来
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    // 回调所有的SpringApplicationRunListener.starting()方法
    listeners.starting();

    Collection exceptionReporters;
    try {
        // 封装命令行参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 根绝应用类型创建不同的ConfigurableEnvironment
        // 创建ConfigurableEnvironment后会回调
        // SpringApplicationRunListener.environmentPrepared(ConfigurableEnvironment environment), 表示环境准备完成
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        // 打印控制台Banner
        Banner printedBanner = this.printBanner(environment);
        // 创建ApplicationContext, 根据NONE,SERVLET,REACTIVE类型创建不同的ApplicationContext
        // 如果使用默认的Tomcat内嵌服务器, web类型为SERVLET
        // SERVLET  => AnnotationConfigServletWebServerApplicationContext
        // REACTIVE => AnnotationConfigReactiveWebServerApplicationContext
        // NONE     => AnnotationConfigApplicationContext
        context = this.createApplicationContext();
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
        // 1. 准备上下文环境
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 2. 刷新IOC容器,
        // 加载所有容器中的组件, 
        // 包括META-INF/spring.factories下的EnableAutoConfiguration组件
        // 如果是web应用, 还会启动web容器,后面将会分析web容器是如何启动的
        this.refreshContext(context);
        // 这是一个空方法
        this.afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }
        // 回调所有SpringApplicationRunListener.started(context)方法
        listeners.started(context);
        // 从ioc容器中获取ApplicationRunner、CommandLineRunner, 并回调相关方法
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, exceptionReporters, listeners);
        throw new IllegalStateException(var10);
    }

    try {
        // 回调所有SpringApplicationRunListener.running(context)方法
        listeners.running(context);
        return context;
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}

prepareContext

// SpringApplication.java
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 为ioc容器设置环境信息
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    // 回调所有 ApplicationContextInitializer.initialize(context)方法
    applyInitializers(context);
    // 回调所有 SpringApplicationRunListener.contextPrepared(context)方法
    listeners.contextPrepared(context);
    // 打印一些日志信息  
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // 往ioc容器中注册一些组件
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // 获取配置类信息
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0])); // 将配置类信息注册到容器中
    // 回调所有SpringApplicationRunListener.contextLoaded(context)方法
    listeners.contextLoaded(context);
}

refreshContext

refreshContext方法最终会调用AbstractApplicationContext.refresh()方法,分析Spring源码的时候已经分析过该方法了,这个方法会加载所有组件到容器中并实例化,需要注意的是这个方法里面还调用了onRefresh()方法,这个方法是留给子类实现的。根据前面的分析我们可以知道,若web应用类型为SERVLET,此时的应用上下文为AnnotationConfigServletWebServerApplicationContextAnnotationConfigServletWebServerApplicationContext继承了ServletWebServerApplicationContext

ServletWebServerApplicationContext实现了AbstractApplicationContext.refresh()方法,web容器就是在这里启动的。

启动web容器

容器自动配置

SpringBoot默认使用Tomcat容器作为内嵌容器。查看web容器的自动配置类ServletWebServerFactoryAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
// 仅在当前应用是 Servlet Web 应用时才生效
@ConditionalOnWebApplication(type = Type.SERVLET)
// 将配置文件中前缀为server的配置参数绑定到ServerProperties, 并将其导入到容器中
@EnableConfigurationProperties(ServerProperties.class)
// 往容器中注入组件
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }
    // Tomcat 定制器,
    // WebServerFactoryCustomizerBeanPostProcessor会获取TomcatServletWebServerFactoryCustomizer对 TomcatServletWebServerFactor 进行定制
    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }

    ......
}

ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class

往容器中注册了两个BeanPostProcessor,分别是WebServerFactoryCustomizerBeanPostProcessorErrorPageRegistrarBeanPostProcessor,这两个组件用来对WebServerFactory进行定制化。

1.ServletWebServerFactoryConfiguration.EmbeddedTomcat.class
往容器中注入TomcatServletWebServerFactory
2.ServletWebServerFactoryConfiguration.EmbeddedJetty.class
往容器中注入JettyServletWebServerFactory
3.ServletWebServerFactoryConfiguration.EmbeddedUndertow.class
往容器中注入UndertowServletWebServerFactory

TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory继承自抽象基类AbstractServletWebServerFactory,实现了接口WebServerFactoryErrorPageRegistry

这些类将分别会被WebServerFactoryCustomizerBeanPostProcessorErrorPageRegistrarBeanPostProcessor使用。由于代码比较简单,只看WebServerFactoryCustomizerBeanPostProcessor

// WebServerFactoryCustomizerBeanPostProcessor.java
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    private ListableBeanFactory beanFactory;
    private List<WebServerFactoryCustomizer<?>> customizers;
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
                "WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory");
        this.beanFactory = (ListableBeanFactory) beanFactory;
    }
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 判断bean的类型是否为WebServerFactory
        // 主要对 TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory进行处理定制化处理
        if (bean instanceof WebServerFactory) {
            postProcessBeforeInitialization((WebServerFactory) bean);
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    @SuppressWarnings("unchecked")
    private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
        // 对WebServerFactory进行定制化操作
        LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
                .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
                .invoke((customizer) -> customizer.customize(webServerFactory));
    }
    // 获取类型为WebServerFactoryCustomizer的组件
    private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
        if (this.customizers == null) {
            // Look up does not include the parent context
            this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
            this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
            this.customizers = Collections.unmodifiableList(this.customizers);
        }
        return this.customizers;
    }
    // 从容器中获取所有类型为WebServerFactoryCustomizer的组件
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
        return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
    }

}

创建容器

前面已经分析过,web容器的创建是在ServletWebServerApplicationContext.refresh()方法里面。

// ServletWebServerApplicationContext.java
// AnnotationConfigServletWebServerApplicationContext继承了ServletWebServerApplicationContext,ServletWebServerApplicationContext实现了AbstractApplicationContext.refresh()方法
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
        implements ConfigurableWebServerApplicationContext {    
    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }
    // 创建web服务
    private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            // 1. 获取 ServletWebServerFactory
            // 如果使用的是Tomcat, 那么返回的是 TomcatServletWebServerFactory
            ServletWebServerFactory factory = getWebServerFactory();
            // 2. 创建web服务, 启动Tomcat
            this.webServer = factory.getWebServer(getSelfInitializer());
        }
        else if (servletContext != null) {
            try {
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context", ex);
            }
        }
        initPropertySources();
    }
 }

1-获取 ServletWebServerFactory

// ServletWebServerApplicationContext.java
// 获取 ServletWebServerFactory
protected ServletWebServerFactory getWebServerFactory() {
    // 从容器中获取类型为ServletWebServerFactory的组件名
    // 常见的ServletWebServerFactory的实现类有TomcatServletWebServerFactory、UndertowServletWebServerFactory、JettyServletWebServerFactory
    String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    if (beanNames.length == 0) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
                                              + "ServletWebServerFactory bean.");
    }
    // 不能同时存在多个ServletWebServerFactory
    if (beanNames.length > 1) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
                                              + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
    // 从容器中获取ServletWebServerFactory实例
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

2-创建web服务

// ServletWebServerApplicationContext.java
this.webServer = factory.getWebServer(getSelfInitializer());

getSelfInitializer()

这里需要注意的是,getSelfInitializer()这个方法返回了一个ServletContextInitializer,这里使用了回调的方式。

// ServletWebServerApplicationContext.java
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    return this::selfInitialize;
}

ServletContainerInitializer 接口只有一个方法

// ServletContainerInitializer.java
public interface ServletContainerInitializer {
    void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}

接着往下看

// 当ServletContextInitializer 类被 TomcatStarter 的#onStartup()方法调用时才会触发这里
private void selfInitialize(ServletContext servletContext) throws ServletException {
    // 1. 判断 ServletContext 里面是否存在 WebApplicationContext.class.getName() + ".ROOT"
    // 2. 如果不存在则往 ServletContext 里面注册一个根容器, 如果web类型为SERVLET,那就是注册了一个AnnotationConfigServletWebServerApplicationContext
    prepareWebApplicationContext(servletContext);
    // 设置作用域
    registerApplicationScope(servletContext);
    // 往环境中注册bean
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    // 从容器中获取类型为ServletContextInitializer组件并执行他们的onStartup方法
    // 一般使用这个给容器动态地添加Servlet、Filter、Listener
    // 注意 ServletContextInitializer 和 servlet3.0 中的 ServletContainerInitializer长得特别像,因为SpringBoot使用内嵌web容器启动的时候并没有遵守 servlet 的规范,所以我们不能使用ServletContainerInitializer的来实现动态添加组件,但是可以通过实现ServletContextInitializer接口来动态添加Servlet、Filter、Listener
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}

这里主要做了两件事情:

  1. ServletContext中注入当前的ApplicationContext,这个就相当于是一个ROOT容器。学习SpringMVC的时候我们知道,SpringMVC共有两个ioc容器,一个是ServletContextListener 中的ROOT容器,一个是在DispatcherServlet中的WEB容器,WEB容器的parent为ROOT容器。
  2. 从ioc容器中获取所有类型为ServletContextInitializer类型的组件,并执行他们的onStartup(ServletContext servletContext)方法。在SpringBoot中,如果想要实现动态添加ServletFilterListener组件,需要实现ServletContextInitializer接口来动态添加,例如:
@Component
public class MyServletContextInitializer implements ServletContextInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        ServletRegistration.Dynamic dynamic = servletContext.addServlet("MyServlet",new MyServlet());
        dynamic.addMapping("/myServlet");
    }
}

跳过这里继续往后看就会发现,这里返回的ServletContextInitializer 会在 TomcatStarter 的 #onStartup() 方法里面调用它的ServletContextInitializer#onStartup(ServletContext servletContext),那时才会触发这里的selfInitialize(ServletContext servletContext) 方法。

getWebServer()

创建Tomcat

// TomcatServletWebServerFactory.java
// 这里就是Tomcat的启动
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    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);
    }
    // 这里传入了 initializers(getSelfInitializer())
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}
// 这里只要为了看清楚 initializers 参数被 TomcatStarter 调用,因此省略了很多代码
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
    // 省略很多代码........
    ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
    // 省略......
    configureContext(context, initializersToUse);
    // 省略......
}
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
    // 创建 TomcatStarter 实例并传入了 initializers
    TomcatStarter starter = new TomcatStarter(initializers);
}

查看TomcatStarter.onStartup()方法

// TomcatStarter.java
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
    try {
        for (ServletContextInitializer initializer : this.initializers) {
            // 这里会触发 前面提到的selfInitialize(ServletContext servletContext) 方法
            initializer.onStartup(servletContext);
        }
    }
    catch (Exception ex) {
        // ......
    }
}

创建DispatcherServlet

DispatcherServlet是在自动配置类DispatcherServletAutoConfiguration中被创建的。

@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({ HttpProperties.class, WebMvcProperties.class })
    protected static class DispatcherServletConfiguration {

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

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

    }
}

从上面的代码可以看出DispatcherServlet实例被封装成DispatcherServletRegistrationBean注入到ioc容器的,而DispatcherServletRegistrationBean实现了ServletContextInitializer接口,因此最终这个DispatcherServlet会被注入到ServletContext中。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!