SpringBoot中Tomcat和SpringMVC整合源码分析

tomcat-embed-core-9.0.16.jar 立即下载

概述

SpringBoot中集成官方的第三方组件是通过在POM文件中添加组件的starter的Maven依赖来完成的。添加相关的Maven依赖之后,会引入具体的jar包,在SpringBoot启动的时候会根据默认自动装配的配置类的注入条件判断是否注入该自动配置类到Spring容器中。自动配置类中会创建具体的第三方组件需要的类 。Tomcat和SpringMVC都是通过这样的方式进行集成的。SpringBoot出现之前SpringMVC项目是直接部署在Tomcat服务器中的,Tomcat是一个符合Servlet标准的Web服务器,Tomcat单独作为一个可安装软件。这种方式下Tomcat是一个完整独立的web服务器,SpringMVC项目不能脱离Web服务器直接运行,需要先部署在Tomcat服务器的目录中才能运行。SpringBoot出现之后改变了这种方式,我们可以直接运行SpringBoot项目,因为SpringBoot中可以内嵌tomcat服务器,而tomcat也是java开发的。在启动SpringBoot项目的时候直接创建tomcat服务并且把SpringMVC的项目部署在tomcat服务中。

一、自动装配原理

​ 在SpringBoot自动装配的程中,会加载/META-INF/factories文件中的以EnableAutoConfiguration为Key的配置类的字符串数组,在该类中会根据具体引入的服务器类进行创建具体的服务器对象。这些字符串数组是否真正加载进Spring容器,需要经过两方面的判断,

​ 1.根据META-INF/spring-autoconfigure-metadata.properties文件中配置的规则判断是否需要过滤。

​ 2.在org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass中会根据自动装配类的条件注解来判断是否进行自动加载。如果条件注解全部校验成功才会加载该配置类。

两种情况的底层校验逻辑都是一样的,都是通过调用OnWebApplicationCondition、OnClassCondition、OnBeanCondition的match方法进行判断。但是在processConfigurationClass方式的检查类型会更多,比如ConditionalOnMissingBean 等条件的检查。

条件注解

注解备注

OnWebApplicationCondition

判断是否符合服务器类型

OnClassCondition

判断项目中是否存在配置的类

OnBeanCondition

判断Spring容器中是否注入配置的类

二、内嵌式Tomcat注入

2.1自动注入配置类分析

1.ServletWebServerFactoryAutoConfiguration 配置类分析

​ 1.META-INF/factories文件中以EnableAutoConfiguration为Key的配置类的字符串数组,其中 ServletWebServerFactoryAutoConfiguration 为创建Web服务器的自动配置类,根据META-INF/spring-autoconfigure-metadata.properties文件中配置的规则判断是否需要过滤,该文件关于ServletWebServerFactoryAutoConfiguration的配置描述如下,

org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.ConditionalOnClass=javax.servlet.ServletRequest
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.ConditionalOnWebApplication=SERVLET
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.AutoConfigureOrder=-2147483648

此配置说明需要在当前的项目中有javax.servlet.ServletRequest类并且WebApplication是SERVLET容器,才不会过滤。

  1. 根据自动装配类的条件注解来判断是否进行自动加载,ServletWebServerFactoryAutoConfiguration 配置类的条件注解如下,
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 判断是否有 ServletRequest 类
@ConditionalOnClass(ServletRequest.class)
// 判断 WebApplication 是 SERVLET 容器
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)

此配置说明需要在当前的项目中有javax.servlet.ServletRequest类并且WebApplication是SERVLET容器,才会进行ServletWebServerFactoryAutoConfiguration 类的加载。

2.2注入逻辑具体分析

  1. tomcat的starter依赖配置代码如下,
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.2.5.RELEASE</version>
    <scope>compile</scope>
</dependency>
  1. 添加Maven依赖配置后,会引入tomcat-embed-corejar包,其中有ServletRequest接口。代码截图如下,

SpringBoot的在创建服务器类型的时候是Servlet的,在创建SpringApplication的run方法中会调用createApplicationContext方法创建具体的AnnotationConfigServletWebServerApplicationContext类,该类继承了org.springframework.web.context.support.GenericWebApplicationContext类。OnWebApplicationCondition条件注解中就是通过判断是否有GenericWebApplicationContext类来检查是否是SERVLET类型的。

  1. 所以添加完tomcat的starter依赖配置后,ServletWebServerFactoryAutoConfiguration所有的条件注解都会匹配成功 ,即会自动加载 ServletWebServerFactoryAutoConfiguration 自动装配类。该类会通过@Import注解导入3个内嵌服务器的实现类,这3个实现类的服务器都是Servlet标准的。代码如下,
// 导入具体服务器类
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
         ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
         ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
         ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })

三个Embeddedxxx表示相应的服务器实现类,每个服务器类都有自己的条件注解实现不同的加载逻辑,如果这里注入了多个web服务器,在服务器启动的时候会报错。EmbeddedTomcat类的代码如下,

@Configuration(proxyBeanMethods = false)
#当前工程下需要有Servlet、Tomcat UpgradeProtocol类
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
#当前的Spring容器类不能有ServletWebServerFactory类型的Bean,搜索的是当前容器。
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
    @Bean
    TomcatServletWebServerFactory tomcatServletWebServerFactory(
        ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
        ObjectProvider<TomcatContextCustomizer> contextCustomizers,
        ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
        // 创建 TomcatServletWebServerFactory ,是产生TomcatServletWebServer的工厂类
        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;
    }
}

从以上代码看出,EmbeddedTomcat配置类能加载注入的条件是当前工程下需要有Servlet、Tomcat、 UpgradeProtocol3个类并且当前Spring容器没有注入ServletWebServerFactory类型的Bean。而这个三个类都在tomcat-embed-core.jar包中,并且Spring容器中之前没有注入ServletWebServerFactory类型的Bean,所会自动加载EmbeddedTomcat类。EmbeddedTomcat用来注入TomcatServletWebServerFactory到Spring容器,TomcatServletWebServerFactory的实现了ServletWebServerFactory接口,TomcatServletWebServerFactory可以创建具体的TomcatWebServer类。在SpringBoot启动的时候会从Spring容器中获取ServletWebServerFactory类型的Bean。至此Tomcat的自动装置解析完了。

三、SpringMVC注入

​ SpringMVC中最重要的组件是DispatchServlet,SpringMVC要在Servlet类型的Web服务器运行,就要把Servlet添加到Web容器中,用Servlet来处理请求。回想一下以前的SpringMVC,我们需要在web.xml配置文件中添加Servlet的配置,会默认把SpringMVC的DispatchServlet配置到Servlet配置节点中,web.xm配置节点代码如下,

<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
            </init-param>
            <!--Web服务器一旦启动,Servlet就会实例化创建对象,然后初始化(预备创建对象)-->
            <load-on-startup>1</load-on-startup>
</servlet>

在SpringBoot中我们使用的是内嵌式的Tomcat,那tomcat和SpringMVC的DispatchServlet是怎么关联起来的?一起往下看,META-INF/factories文件中的以EnableAutoConfiguration为Key的配置类的字符串数组,其中 DispatcherServletAutoConfiguration 和 WebMvcAutoConfiguration 是SpringMVC中最为重要的两个自动配置类,DispatcherServletAutoConfiguration 用来注册 DispatcherServlet到 Spring容器,WebMvcAutoConfiguration 用来注入MVC的相关组件到Spring容器。

3.1自动注入配置类分析

1.DispatcherServletAutoConfiguration 配置类分析

​ 1.1 根据META-INF/spring-autoconfigure-metadata.properties文件中配置的规则判断是否需要过滤,该文件关于DispatcherServletAutoConfiguration 的配置描述如下,

#WebApplication是SERVLET容器
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.ConditionalOnWebApplication=SERVLET
#工程中需要引入 DispatcherServlet 类
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.ConditionalOnClass=org.springframework.web.servlet.DispatcherServlet
#容器注入在 ServletWebServerFactoryAutoConfiguration(服务器自动配置类) 类之后,
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.AutoConfigureOrder=-2147483648

DispatcherServletAutoConfiguration 的逻辑为:需要在当前的项目中有DispatcherServlet类并且WebApplication是SERVLET容器,才不会过滤掉。

​ 1.2 根据自动装配类的条件注解来判断是否进行自动加载,DispatcherServletAutoConfiguration 配置类的条件注解如下,

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

此逻辑需要在当前的项目中有DispatcherServlet类并且WebApplication是SERVLET容器,并且注入在 DispatcherServletAutoConfiguration 类之后,才会进行DispatcherServletAutoConfiguration 类的加载。在DispatcherServletAutoConfiguration 中有两个非常重要的内部类,DispatcherServletConfiguration 和DispatcherServletRegistrationConfiguration ,这两个内部类也会根据条件注解加载来决定是否加载进Spring容器中 。DispatcherServletConfiguration 主要用来注入 DispatcherServlet ;DispatcherServletRegistrationConfiguration 主要用来注入 DispatcherServletRegistrationBean,DispatcherServletRegistrationBean 用来包装 DispatcherServlet 类,它实现了ServletContextInitializer接口,用来注册Servlet对象到tomcat中的ServletContext对象。

2.DispatcherServletConfiguration 配置类分析

​ DispatcherServletConfiguration 中有个DispatcherServlet类型的@Bean对象,会把DispatcherServlet注入到Spring容器中,DispatcherServletConfiguration 代码如下,

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

以上条件注解代码可知,将BeanName为 dispatcherServlet ,类型为DispatcherServlet 的Bean注入到容器中的条件为:先查找是否已经容器中是否存在名称 dispatcherServlet的Bean或者类型DispatcherServlet 的Bean,如果都不存并且当前工程中有ServletRegistration类则将DispatcherServletConfiguration注入到Spring容器中,并且还会注入DispatcherServlet类。

  1. DispatcherServletRegistrationConfiguration 配置类分析

​ DispatcherServletRegistrationConfiguration 主要用来注入 DispatcherServletRegistrationBean,DispatcherServletRegistrationBean 用来包装 DispatcherServlet 类,它实现了ServletContextInitializer接口,SpringBoot用DispatcherServletRegistrationBean 来注册Servlet对象到Tomcat服务器中的ServletContext对象,在后面Tomcat启动的时候会讲具体实现。DispatcherServletRegistrationConfiguration代码如下,

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

以上条件注解代码可知,将BeanName为 dispatcherServletRegistration,类型为DispatcherServletRegistrationBean的Bean注入到容器中的条件为:先查找是否已经容器中是否存在名称 dispatcherServletRegistration的Bean或者类型DispatcherServletRegistrationBean的Bean,如果都不存并且当前工程中有ServletRegistration类则将DispatcherServletRegistrationConfiguration注入到Spring容器中,并且还会注入DispatcherServletRegistrationBean类。

  1. WebMvcAutoConfiguration

​ 4. 1 根据META-INF/spring-autoconfigure-metadata.properties文件中配置的规则判断是否需要过滤,该文件关于 WebMvcAutoConfiguration 的配置描述如下,

#工程中需要引入Servlet、WebMvcConfigurer、 DispatcherServlet类 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.ConditionalOnClass=javax.servlet.Servlet,org.springframework.web.servlet.config.annotation.WebMvcConfigurer,org.springframework.web.servlet.DispatcherServlet
# 容器注入在 DispatcherServletAutoConfiguration、TaskExecutionAutoConfiguration、ValidationAutoConfiguration之
# 后
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.AutoConfigureOrder=-2147483638

WebMvcAutoConfiguration的逻辑为:需要在当前的项目中有Servlet、WebMvcConfigurer、DispatcherServlet3个类,才不会过滤掉。

​ 4.2 根据自动装配类的条件注解来判断是否进行自动加载,WebMvcAutoConfiguration配置类的条件注解如下,

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })

此逻辑需要当前的应用程序为SERVLET类型,当前的项目中有Servlet、DispatcherServlet、WebMvcConfigurer3个类、WebMvcConfigurationSupport没有被注入到Spring容器、在 DispatcherServletAutoConfiguration 、TaskExecutionAutoConfiguration、ValidationAutoConfiguration 类注入到Spring容器之后,才会进行WebMvcAutoConfiguration类的注入。在该中有个内部类WebMvcAutoConfigurationAdapter ,主要用来注入 SpringMVC的组件,比如ViewResolver、LocaleResolver等。

3.2 注入逻辑具体分析

  1. springmvc的starter依赖配置代码如下
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.4.RELEASE</version>
    <scope>compile</scope>
</dependency>
  1. 添加Maven依赖配置后,会引入spring-webmvc.jar包,其中有DispatcherServlet类、WebMvcConfigurer接口。而springmvc的自动配置类在ServletWebServerFactoryAutoConfiguration配置类加载之后进行加载,所以肯定会有Servlet的存在。
  2. 所以添加完springmvc的Maven依赖配置后,会自动加载 DispatcherServletAutoConfiguration类和WebMvcAutoConfiguration类以及他们配置类中其他的配置类。至此SpringMVC的自动装置解析完了。

四、Tomcat与SpringMVC集成

  1. 在SpringApplication的run方法中,会进行ApplicationContext的创建,然后会执行 refreshContext 方法,其中的会执行onRefresh方法,这个方法是由子类ServletWebServerApplicationContext实现,ServletWebServerApplicationContext中onRefresh方法代码如下,
protected void onRefresh() {
    super.onRefresh();
    try {
        // 创建 web 服务器
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}
  1. createWebServer里面的逻辑主要是创建web服务器,主要代码如下,
private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    // 如果为 null,则创建
    if (webServer == null && servletContext == null) {
        // 获取WebServerFactory
        ServletWebServerFactory factory = getWebServerFactory();
       // 获取 webServer ,getSelfInitializer() 返回的是 函数式接口
	   // 是一个 实现了@FunctionalInterface 接口的列表
        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. 创建服务器分为两步,第一步获取WebServerFactory 服务器工厂,第二步根据工厂获取具体的web服务器。getWebServerFactory()主要逻辑为:先获 BeanFactory ,然后从 BeanFactory取 ServletWebServerFactory 类型的 BeanNames数组,如果数组长度为 0,则抛异常,如果数组长度大于 1,则抛异常,最后根据第一个beanName和ServletWebServerFactory类型获取容器中的Bean进行返回。
  2. getWebServerFactory方法源码如下,
protected ServletWebServerFactory getWebServerFactory() {
    // 先获 BeanFactory ,再从 BeanFactory取 ServletWebServerFactory 类型的 BeanNames
    String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    // 如果长度为 0,则抛异常
    if (beanNames.length == 0) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
                                              + "ServletWebServerFactory bean.");
    }
    // 如果长度大于 1,则抛异常
    if (beanNames.length > 1) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
                                              + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
    // 返回根据第一个beanName和ServletWebServerFactory类型获取容器中的Bean
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

因为之前是注入了Tomcat的TomcatServletWebServerFactory,这里返回的是前面注入到容器里面的 TomcatServletWebServerFactory,然后调用该类的getWebServer方法,传入getSelfInitializer()方法参数,这个时候不会进行getSelfInitializer()里面具体业务逻辑调用,而是生成实现了ServletContextInitializer接口的匿名类后传入方法中。ServletContextInitializer接口代码如下,

@FunctionalInterface
public interface ServletContextInitializer {
	/**
	 * Configure the given {@link ServletContext} with any servlets, filters, listeners
	 * context-params and attributes necessary for initialization.
	 * @param servletContext the {@code ServletContext} to initialize
	 * @throws ServletException if any call against the given {@code ServletContext}
	 * throws a {@code ServletException}
	 */
	void onStartup(ServletContext servletContext) throws ServletException;
}
  1. getSelfInitializer()方法返回的是函数表达式,不会立马执行方法内容。当tomcat服务器启动的时候会调用该匿名类的onStartup方法。getSelfInitializer方法中的主要逻辑为:找出实现了ServletContextInitializer 接口的类并注入容器 ,然后循环调用实现了ServletContextInitializer 接口的匿名类的onStartup()方法。在3.1 章节中注入了实现了ServletContextInitializer 接口的DispatcherServletRegistrationBean类,这里会调用DispatcherServletRegistrationBean的onStartup()方法,而DispatcherServletRegistrationBean中包含了3.1中注入的DispatcherServlet 类。DispatcherServletRegistrationBean的onStartup方法的主要逻辑为:将当前类中的DispatcherServlet 添加到Tomcat 的 servletContext 中。getSelfInitializer关键代码如下,
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
    prepareWebApplicationContext(servletContext);
    registerApplicationScope(servletContext);
    // 注入 servletContext 类型的 bean
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    // 注入实现了ServletContextInitializer 接口的类 ,并进行循环调用onStartup
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}

上面的getServletContextInitializerBeans()方法主要逻辑为:创建一个ServletContextInitializerBeans类,它实现了AbstractCollection接口,可以作为集合直接遍历。ServletContextInitializerBeans构造方法主要逻辑为:在容器中获取所有实现了ServletContextInitializer 接口的类添加到this.initializers中,排序后赋值给sortedList属性。getServletContextInitializerBeans()和ServletContextInitializerBeans的构造方法代码如下,

//getServletContextInitializerBeans具体代码
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
    // 构造ServletContextInitializerBeans对象,该对象实现了 AbstractCollection<ServletContextInitializer> 接口
    // 是 ServletContextInitializer的 一个集合类。
    return new ServletContextInitializerBeans(getBeanFactory());
}

// ServletContextInitializerBeans 构造方法
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
                                      Class<? extends ServletContextInitializer>... initializerTypes) {
    // 初始化 initializers
    this.initializers = new LinkedMultiValueMap<>();
    this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
        : Collections.singletonList(ServletContextInitializer.class);
    // 在beanFactory中查找实现了 ServletContextInitializer 接口的类
    // 这里包括了  ServletRegistrationBean 、FilterRegistrationBean
    // DelegatingFilterProxyRegistrationBean、ServletListenerRegistrationBean 和其他。
    // 添加到 initializers 集合中
    addServletContextInitializerBeans(beanFactory);
    // 添加适配的 Bean
    addAdaptableBeans(beanFactory);
    // initializers 排序
    List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
        .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
        .collect(Collectors.toList());
    // 给该集合可遍历对象赋值
    this.sortedList = Collections.unmodifiableList(sortedInitializers);
    // 返回 initializers
    logMappings(this.initializers);
}

addServletContextInitializerBeans()方法中会找到所有实现了 ServletContextInitializer 接口的类,即会找到3.1章节中解析的DispatcherServletRegistrationBean类,都添加到 this.initializers 集合中。

​ 5.1DispatcherServletRegistrationBean的onStartup方法主要逻辑为:把当前的DispacherServlet对象添加到从参数传进来的ServletContext对象中,onStartup具体代码如下,

public final void onStartup(ServletContext servletContext) throws ServletException {
    String description = getDescription();
    if (!isEnabled()) {
        logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
        return;
    }
    // 注册 Servlet 到 tomcat的 servletContext中
    register(description, servletContext);
}

​ 5.2 register()方法是具体注册当前Servlet到tomcat的 servletContext中,代码如下,

@Override
protected final void register(String description, ServletContext servletContext) {
    // 添加当前的 servlet 到 tomcat 的 servletContext 中
    D registration = addRegistration(description, servletContext);
    if (registration == null) {
        logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
        return;
    }
    configure(registration);
}

​ 5.3 addRegistration方法是先获取servlet名称再注册当前Servlet到参数传进来的的servletContext参数中,代码如下

@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
    // 获取 当前对象的 servlet 名称
    String name = getServletName();
    //添加当前的 servlet 到 tomcat 的 servletContext 中
    return servletContext.addServlet(name, this.servlet);
}

​ 5.4总结一下,这个getSelfInitializer()很关键,主要逻辑为:把springmvc中的DispatcherServlet 添加到tomcat服务器中的servletContext对象中 。

  1. getWebServer方法用来获取WebServer,大概逻辑是:先创建Tomcat类,再初始化相关属性,最后创建TomcatWebServer对象。具体的代码如下,
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    // 创建具体的Tomcat类
    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);
    // 设置 host 的自动部署属性为 false
    tomcat.getHost().setAutoDeploy(false);
    // 配置 engine
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    // 初始化 TomcatEmbeddedContext ,并把 TomcatEmbeddedContext 添加至 host 中
	// 用 initializersToUse 初始化 TomcatStarter ,并设置 TomcatEmbeddedContext 的Starter属性为  TomcatStarter 
    // initializersToUse 为函数式接口 ,即 实现了ServletContextInitializer接口 的匿名类
    prepareContext(tomcat.getHost(), initializers);
    // 创建具体的 TomcatwebServer
    return getTomcatWebServer(tomcat);
}
  1. prepareContext()方法是创建WebServer前的准备 ,主要逻辑为:创建 TomcatEmbeddedContext ,并把 TomcatEmbeddedContext 添加至 host 中,然后用 initializersToUse (initializersToUse 为实现了ServletContextInitializer接口 的集合,其中包括了getWebServer方法中的参数 initializers )初始化 TomcatStarter ,并设置 TomcatEmbeddedContext 的Starter属性为TomcatStarter。即TomcatEmbeddedContext的Starter属性为TomcatStarter。在tomcat启动的时候会调用到TomcatStarter的onStartup方法。
  2. getTomcatWebServer()中会创建TomcatwebServer类,并且传入 tomcat 和 端口是否大于等于0 两个参数,代码如下
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    return new TomcatWebServer(tomcat, getPort() >= 0);
}

TomcatWebServer的构造函数中,除了给tomcat和 autoStart赋值,还会调用初始化方法initialize,TomcatWebServer的构造方法代码如下,

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    // 自动开启
    this.autoStart = autoStart;
    // 初始化方法
    initialize();
}
  1. initialize()方法中会进行TomcatWebServer的初始,关键代码如下,
// Start the server to trigger initialization listeners
// tomcat 服务器启动
this.tomcat.start();

整个tomcat的启动比较复杂,有兴趣的可以去研究下tomcat源码,这里不做多讲直接给出结论。其中会调用上面创建的TomcatStarter类的onStartup方法,并传入tomcat的servletContext对象。TomcatStarter的onStartup具体代码如下,

public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
    try {
        // 遍历已经注册的 initializers, 
        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());
        }
    }
}

onStartup方法中会遍历 initializers 集合并且调用其onStartup()方法,而initializers包括了我们在调用getWebServer时传入的getSelfInitializer方法,该方法体的主要业务逻辑上面已经讲了(请看本章的第5点),就是给传进去的servletContext添加当前Spring容器中注入的SpringMVC的DispatchServlet类。

  1. 最后tomcat完成初始化,会监听一个具体的端口。至此,tomcat启动的过程已经把SpringMVC的DispatchServlet类添加到了tomcat的servletContext对象中。当请求进来Web服务器的时候会转到DispatchServlet中。DispatchServlet的整个初始化过程这里不细讲了
组合预测模型 | LSTM-XGBoost长短期记忆神经网络结合极限梯度提升树时间序列预测(Matlab程序) 组合预测模型 | LSTM-XGBoost长短期记忆神经网络结合极限梯度提升树时间序列预测(Matlab完整程序) 阅读详情

相关推荐

对极几何 —两相机相对位置关系与相机姿态估计方法(基础矩阵,本征矩阵,单应矩阵梳理)附c++源码

对极几何 —两相机相对位置关系与相机姿态估计方法(基础矩阵,本质矩阵,单应矩阵梳理)附c++源码对极几何是什么,它有什么用(整体流程理解)对极约束(描述两相机关系)基础矩阵特性与求解方法本质矩阵特性与求解方法相机姿态恢复方法怎么又冒出了个单应矩阵H? 对极几何是什么,它有什么用(整体流程理解) 如图,当空间上有一个点分别在相机1和相机2上进行成像,这两个点存在什么变换关系呢?再扩展一下,真实世界下的场景分别用两个相机进行拍摄,获得两张照片,两张照片有什么变换关系呢?那么对极几何就是用来描述这种变换关系的。

qq_37764141的博客 5104

11-30 SpringBoot

tomcat的依赖 对于tomcat其实还是一个jar包 spring是一个IOC容器 tomcat的核心对象交给Spring容器 调用核心对象方法 启动Tomcat。对于tomcat的依赖排除以及tomcat的重新非排除设置:加入exclutions配置进行排除即可,以及使用jetty依赖进行tomcat的重新加入。二.在命令行运行springboot项目:mvc命令 mvc spring-boot:run。springboot的打包插件: spring-boot-maven-plugin。

weixin_53415999的博客 2822

百度飞桨+文心大模型实战:生成式AI工程师认证备考全攻略(含真题解析)

本文详细解析百度飞桨与文心大模型结合的生成式AI工程师认证备考策略,涵盖认证体系、学习路径、核心技能突破及真题分析。帮助开发者掌握大模型原理、飞桨平台操作和文心大模型应用,高效通过工信部与百度联合认证考试。

loss4bartender的博客 682

Spring Boot辅助功能之内嵌服务器【Tom Cat】

内嵌Tomcat服务器是SpringBoot辅助功能之一

一切总会归于平淡 5153

时序基础模型Timer-S1:十亿参数如何革新时间序列预测

时间序列预测是数据分析与机器学习中的核心任务,旨在基于历史数据预测未来趋势。其原理在于捕捉数据中的趋势、周期性和季节性等时序依赖关系。这项技术的价值在于赋能业务决策,广泛应用于金融风控、能源管理、物联网监控和供应链优化等关键场景。传统方法依赖针对特定任务的专用模型,面临泛化能力弱和开发成本高的挑战。本文聚焦的时序基础模型,通过在海量跨领域数据上进行预训练,学习时序通用模式,并结合十亿级参数带来的强大容量,实现了性能的显著突破。这种范式转变,使得模型能够像“全科医生”一样,通过少量数据微调即可快速适应新任务,

weixin_29164843的博客 269

AF自动对焦 CDAF PDAF

前言 主流的AF: CDAF, PDAF, laser assist AF(这个只是辅助,在微距或者拍摄纹理不明显的场景下好用)。 1.cdaf原理 CDAF的大致原理就是检测图像锐度或者等价于锐度的参数,不断推动马达寻找最清晰点实现合焦或者对焦。如下图: 缺点:对焦准确,速度慢,由于camera帧率,cpu处理速度,马达反映时间问题不能实时对焦运动物体。 2.pdaf原理 计算相位差,将相位差转换为相位差和af code的关系....

camerall 1万+

SpringBoot启动过程分析

SpringBoot启动过程分析 SpringBoot的出现给我们带了许多的便利性,其中一点就是可以内置tomcat,从而实现从jar包直接运行,那么SpringBoot是怎么实现的呢? 嵌入式tomcat 在一个简单的SpringBoot项目中,我们只需要在项目中添加spring-boot-starter-web依赖,然后通过SpringApplication.run方法就可以启动一个web服务。查看spring-boot-starter-web的依赖关系,发现其依赖了tomcat-embed-cor

ja.rome 4678

DeepSmote:一种深度SMOTE方法

DeepSmote 一种全新的将深度学习与Smote相结合的过采样方法! 目前在许多领域已经被使用,值得大家关注学习!

走在深度学习前沿的小宋 1447

SpringBoot关于内嵌tomcat的相关问题

Springboot

weixin_45827905的博客 4361

【100%通过率】华为OD机试真题 C++ 实现【打印机队列】【2022.11 Q4 新题】

有5台打印机打印文件,每台打印机有自己的待打印队列。因为打印的文件内容有轻重缓急之分,所以队列中的文件有1~10不同的代先级,其中数字越大优先级越高。打印机会从自己的待打印队列中选择优先级最高的文件来打印。如果存在两个优先级一样的文件,则选择最早进入队列的那个文件。现在请你来模拟这5台打印机的打印过程。

MISAYAONE的博客 9946

科研绘图系列:R语言绘制世界地图数据分布(world map)

科研绘图系列:R语言绘制世界地图数据分布(world map)

专注生信领域 527

三星架构师:你的 Spring Boot 源码还停留在启动阶段?

Spring Boot 作为Spring 社区的招牌框架之一 ,现在的一个热度可以说家喻户晓也不为过了, 但是还有好多程序员同行们并没有使用过或是了解过Spring Boot这个框架, ...

架构文摘 191

操作系统实验一到实验九合集(哈工大李治军)

操作系统实验一到实验九合集(哈工大李治军) 有详细代码及注释,程序已在本地测试,均能跑通

leoabcd12的博客 5万+

springboot整合springmvc源码分析(1)--前言

springboot是怎么做到内嵌tomcat,而不需要去部署项目到tomcat的,在分析springboot整合springmvc前,我们先来说说一点关于tomcat的东西,我们这里不需要下载tomcat服务,只需要new 一个tomcat 即可,下面我们来一个例子看看 org.apache.tomcat.embed:tomcat-embed-core:9.0.31 public cla...

菜 头笔记 1129

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

springboot源码分析

快速开发底层原理 SpringBoot核心理念 能够实现帮助开发者快速的整合第三方框架(Spring、Mybatis、hibernate) 原理:Maven依赖封装整合和自定义starter. 完全去除XML配置,采用注解形式 原理:SpringBoot其实根据 Spring 体系原生的注解实现包装 不需要外部容器,内嵌入服务器(Tomcat) . 原理:Java语言创建tomcat服务器,让后将本地class文件交给tomcat加载。 案例:注解方式启动MVC将SpringMVC交给内部tomcat运行

weixin_44044929的博客 6046

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

简单直接让你也读懂springmvc源码分析(1)-- DispatcherServlet

DispatcherServlet干什么用的? 我们从源码注释中可以得知: 用于HTTP请求处理程序/控制器的中央调度器,例如用于web UI控制器或基于HTTP的远程服务导出器。发送到注册处理程序进行处理web请求,提供方便的映射和异常处理工具。 DispatcherServlet说实在的他就是一个Servlet,将其注册到tomcat的Servlet容器里,然后接受到请求进入到ser...

菜 头笔记 666

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

springboot启动时自行方法_springboot整合springmvc源码分析(1)前言

springboot是怎么做到内嵌tomcat,而不需要去部署项目到tomcat的,在分析springboot整合springmvc前,我们先来说说一点关于tomcat的东西,我们这里不需要下载tomcat服务,只需要new 一个tomcat 即可,下面我们来一个例子看看 org.apache.tomcat.embed tomcat-embed-core 8.5.28...

weixin_39637256的博客 154

理解SpringBoot源码分析

这里写目录标题1.Spring boot 简介2.@SpringBootApplication注解分析3.剖析代码 1.Spring boot 简介 Spring Boot是由Pivotal团队提供的快速开发框架,基于SpringMVC通过注解+内置Http服务器如:tomcat-embed-core,简化了XML配置,快速将一些常用的第三方依赖整合(通过Maven继承依赖关系),最终实现以Java应用程序的方式进行执行。 Spring Boot 包含的特性如下: 创建可以独立运行的 Spring 应用

MVC_lh的博客 254

SpringBoot学习笔记

文章目录第一部分:SpringBoot应用回顾1.1 概述1.2 主要特性1.3 抛出疑问1.4 热部署1.5 全局配置文件1.6 属性注入1.6.1 属性注入常用注解1.6.2 如何覆盖第三方配置1.7 日志框架第二部分:SpringBoot源码剖析2.1 【重点】自动配置2.2 Run方法执行流程2.2.1 SpringApplication() 构造方法2.2.2 run(args)2.3 内嵌Tomcat2.3.1 内嵌Tomcat自动配置原理2.3.3 自动配置SpringMVC第三部分:Spri

徐小陌的博客 471

《学会 SpringMVC 系列 · 基础篇》

博主所在公司早期后端采用 SSM(Spring + SpringMVC + MyBatis),2020年进行了微服务拆分,顺带技术栈升级,后端调整为 SpringCloud 和 SpringBoot,深刻体会到了SpringBoot为我们带来的遍历。经常会听到新人之间在议论,“还是新框架好,旧框架已过时了,不要浪费时间学习旧框架”,那么 SpringBoot 是否完全取代了 SSM 技术栈呢?那么,有了 SpringBoot,是否还需要学习 SpringMVC?

山人行 1388

互联网架构-SpringBoot源码深度解析-032:SpringBoot大体流程源码分析

032:SpringBoot大体流程源码分析1 SpringBoot快速开发底层实现原理2 SpringBoot去除XML,采用注解方式原理3 SpringBoot内置Tomcat服务器原理4 SpringBootStarter基本思想介绍5 SpringBoot自定义Starter插件016 SpringBoot自定义Starter插件02 1 SpringBoot快速开发底层实现原理 课程内容: 1.SpringBoot底层设计原理与思想 2.如何自定义SpringBootStarter 3.模拟手写S

枫火的博客 344

Spring Boot与Spring MVC集成启动过程源码分析

开源项目推荐 Pepper Metrics是我与同事开发的一个开源工具(https://github.com/zrbcool/pepper-metrics),其通过收集jedis/mybatis/httpservlet/dubbo/motan的运行性能统计,并暴露成prometheus等主流时序数据库兼容数据,通过grafana展示趋势。其插件化的架构也非常方便使用者扩展并集成其他开源组件。 请大...

低调的程序猿 256
上一篇: Java Web开发核心技术全面解析
下一篇: 如何评价《领域驱动设计精粹》?为什么说它是拯救烂代码的“最后解药”?
程序员职业指南
博客等级 码龄4年 2136粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值