目录

一、集成Mybatis

1.传统Spring集成Mybatis

2.Springboot集成Mybatis

二、集成SpringMVC

1.Tomcat传统集成方式

2.Springboot自动集成

2.1 WebMvcAutoConfiguration类

2.2 DispatcherServletAutoConfiguration类

2.3 ServletWebServerFactoryAutoConfiguration类

2.4 ServletWebServerFactoryConfiguration类

2.5 ServletWebServerApplicationContext类

3.后话


前面两个文章已经详细介绍过Springboot如何自动配置集成某个框架,本篇笔记便着重分析一下Springboot是如何集成Mybatis和SpringMVC的。前两篇传送门:

  • (一)Springboot原理源码解析之Springboot框架组成
  • (二)Springboot原理源码解析之启动原理

一、集成Mybatis

1.传统Spring集成Mybatis

首先我们需要知道Spring和Mybatis集成需要哪些配置,Spring集成Mybatis配置类如下:

@Configuration
@MapperScan(basePackages = SystemCst.BASE_PACKAGE, 
                sqlSessionTemplateRef = SystemCst.SQL_SESSION_TEMPLATE)
public class DbConfig {

    @Bean(SystemCst.DATASOURCE)
    public DataSource getDataSource() {
        OracleDataSource dataSource = null;
        try {
            dataSource = new OracleDataSource();
            dataSource.setDriverType(SystemCst.DRIVER);
            dataSource.setURL(SystemCst.URL);
            dataSource.setUser(SystemCst.USERNAME);
            dataSource.setPassword(SystemCst.PASSWORD);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return dataSource;
    }

    @Bean(SystemCst.SQL_SESSION_FACTORY)
    public SqlSessionFactory getSqlSessionFactoryBean(
            @Qualifier(SystemCst.DATASOURCE) DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = 
                new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        PathMatchingResourcePatternResolver resolver = 
                new PathMatchingResourcePatternResolver();
        try {
            sqlSessionFactoryBean.setMapperLocations(
                resolver.getResources(SystemCst.MAPPER_LOCATION));
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            return sqlSessionFactoryBean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Bean(SystemCst.SQL_SESSION_TEMPLATE)
    public SqlSessionTemplate getSqlSessionTemplate(
            @Qualifier(SystemCst.SQL_SESSION_FACTORY) 
                SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

其大致配置类结构图如下:

springboot整合spring seata_xml

可以看到需要的仅三个对象Bean,DataSource、SqlSessionFactory和SqlSessionTemplate,再加上一个@MapperScan注解说明扫描的包,扫描到具体的Mapper接口即可,因为这种集成方式是通过@MapperScan中@Import引入的MapperScannerRegistrar类完成的,这个类实现了接口ImportBeanDefinitionRegistrar,因此在获得配置类的信息后将会调用这个接口方法,实现集成。

2.Springboot集成Mybatis

Springboot既然说了是开箱即用和约定高于配置,就不会让开发者感受到以前这种手动配置的感觉,只需要在yml中配置一下mybatis.mapper-locations,然后在Mapper接口上加上@Mapper即可完成Mybatis和Springboot的集成。

即以下三个配置即可,中间的入口类是Springboot必须的,因此实际上只需要额外添加两个:

入口类:
@SpringBootApplication(scanBasePackages = "com.iboxpay")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

Mapper接口:
@Mapper
public interface TestMapper {
    void method();
}

yml文件:
mybatis:
  mapper-locations: classpath:mapper/*

按照Springboot给出的既有约定,即可轻松完成集成。那么为什么Springboot能做到这种地步呢?首先看到MybatisAutoConfiguration类的具体内容,源码关键如下:

@Configuration
@ConditionalOnClass({SqlSessionFactory.class,SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
    private final MybatisProperties properties;
    private final Interceptor[] interceptors;
    private final ResourceLoader resourceLoader;
    private final DatabaseIdProvider databaseIdProvider;
    private final List<ConfigurationCustomizer> configurationCustomizers;
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) 
            throws Exception {
        ...
    }
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(
            SqlSessionFactory sqlSessionFactory) {
        ...
    }
    public static class AutoConfiguredMapperScannerRegistrar
            implements BeanFactoryAware, ImportBeanDefinitionRegistrar, 
            ResourceLoaderAware {
        @Override
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata, 
                BeanDefinitionRegistry registry) {
            ClassPathMapperScanner scanner = 
                    new ClassPathMapperScanner(registry);
            ...
            scanner.setAnnotationClass(Mapper.class);
            ...
            scanner.doScan(StringUtils.toStringArray(packages));
        }
    }
    @Configuration
    @Import({ AutoConfiguredMapperScannerRegistrar.class })
    @ConditionalOnMissingBean(MapperFactoryBean.class)
    public static class MapperScannerRegistrarNotFoundConfiguration {
        ...
    }
}

其大致配置类结构图如下:

springboot整合spring seata_sql_02

可以看到,基本上和传统的Mybatis集成Spring一样的操作,但下面多出来的两个内部类便是Springboot使用@Mapper注解Mapper接口,而无需使用@MapperScan的原因。其内在逻辑为:当Spring工厂中没有MapperFactoryBean类型的Bean,则使用MapperScannerRegistrarNotFound-Configuration类,这个类里面没有其它的操作,但其@Import引入了另一个内部类AutoConfigured-MapperScannerRegistrar,这个也实现了ImportBeanDefinitionRegistrar接口,因此在读取完全部的配置类后registerBeanDefinitions方法将会被调用,在这里面ClassPathMapperScanner扫描其和MapperScannerRegistrar不同的是其确定了一定要被@Mapper注解才会被扫描到,并且扫描的包就是SpringBootApplication注解或者@ComponentScan注解中的scanBasePackages值。

注:@MapperScan将会扫描到包下的所有接口,并且使用MapperFactoryBean代理,而使用@Mapper将只会扫描包下有这个注解的接口,没有的将不会被扫描。

二、集成SpringMVC

既然SpringMVC是Spring官方写的Web转发器,那么其具体的方式和Mybatis这种纯第三方集成肯定是有点不一样的,光就Mybatis集成包里面spring.factories文件的EnableAutoConfiguration配置就可以看出来亲生的果然不一样。

1.Tomcat传统集成方式

首先是重要的web.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                 http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

其次是对应的spring-mvc.xml文件配置:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context.xsd 
       http://www.springframework.org/schema/mvc 
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.iboxpay"/>

    <!-- 获取html拦截 -->
    <mvc:default-servlet-handler/>

    <!-- 让controller层能访问到html -->
    <mvc:annotation-driven/>

    <!-- 默认解析jsp的 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="suffix" value=".html"/>
    </bean>

</beans>

其大致配置类结构图如下:

springboot整合spring seata_java_03

我们根据上面两个配置来具体确认一下SpringMVC哪些是必须的:

  1. Tomcat容器及相关的自定义配置项;
  2. 将DispatcherServlet当成一个Serlvet注册进Tomcat;
  3. SpringMVC核心类DispatcherSerlvet所必需的的HandlerMapping、Resolver等;
  4. 扫描Bean的包。

我们在接下来就称之为四个关键点吧。

2.Springboot自动集成

前面说了SpringMVC是Spring的亲儿子,自然是无需做其它的配置的,除非是要自定义一些配置,那么我们看Springboot只需要配置哪些东西:

@SpringBootApplication(scanBasePackages = "com.iboxpay")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

OK,结束了。WTF??果然是亲儿子,只需要确定扫描Bean的包就完成了整个集成过程(当然,必要的包是必须的,但是starter包已经给你全部安排好了),完成了上述的第四个关键点。在分析后面的三个关键点前先让我们看一下Springboot集成SpringMVC相关配置类结构图:

springboot整合spring seata_xml_04

springboot整合spring seata_sql_05

springboot整合spring seata_xml_06

大致结构类图如上,其中不包含各种XXXXProperties,关于Properties在第二篇和第三篇已经分析过具体在哪里被读取解析和绑定。

2.1 WebMvcAutoConfiguration类

接下来我们看看Springboot是如何把其它三个条件完成的。首先看到WebMvcAutoConfiguration这个自动配置类,部分关键源码如下:

@Configuration
@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 })
public class WebMvcAutoConfiguration {
    public static final String DEFAULT_PREFIX = "";
    public static final String DEFAULT_SUFFIX = "";
    private static final String[] SERVLET_LOCATIONS = { "/" };
    @Configuration
    @Import(EnableWebMvcConfiguration.class)
    @EnableConfigurationProperties({ WebMvcProperties.class, 
            ResourceProperties.class })
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter 
            implements WebMvcConfigurer, ResourceLoaderAware {
        private final ResourceProperties resourceProperties;
        private final WebMvcProperties mvcProperties;
        private final ListableBeanFactory beanFactory;
        private final ObjectProvider<HttpMessageConverters> 
                messageConvertersProvider;
        final ResourceHandlerRegistrationCustomizer 
                resourceHandlerRegistrationCustomizer;
        private ResourceLoader resourceLoader;
        ...
        @Bean
        @ConditionalOnMissingBean
        public InternalResourceViewResolver defaultViewResolver() {
            ...
        }
        @Bean
        @ConditionalOnBean(View.class)
        @ConditionalOnMissingBean
        public BeanNameViewResolver beanNameViewResolver() {
            ...
        }
        @Bean
        @ConditionalOnBean(ViewResolver.class)
        @ConditionalOnMissingBean(name = "viewResolver", 
                value = ContentNegotiatingViewResolver.class)
        public ContentNegotiatingViewResolver viewResolver(
                BeanFactory beanFactory) {
            ...
        }
        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
        public LocaleResolver localeResolver() {
            ...
        }
        @Configuration
        @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", 
                matchIfMissing = true)
        public static class FaviconConfiguration 
                implements ResourceLoaderAware {
            ...
            @Bean
            public SimpleUrlHandlerMapping faviconHandlerMapping() {
                ...
            }
            @Bean
            public ResourceHttpRequestHandler faviconRequestHandler() {
                ...
            }
        ]
    }
    @Configuration
    public static class EnableWebMvcConfiguration 
            extends DelegatingWebMvcConfiguration {
        ...
        @Bean
        @Override
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
            ...
        }
        @Bean
        @Primary
        @Override
        public RequestMappingHandlerMapping requestMappingHandlerMapping(){
            ...
        }
    }
}

这个自动配置类的信息量有点大,但总体看下来而言就知道,无非是确定和其它必须配置的执行顺序和关系,看过<mvc:default-servlet-handler/>和<mvc:annotation-driven/>标签的具体实现类就能知道,这里面其它的配置基本上也是添加这两个标签里面类,所以关键点并不是这个类,这个类只是在前面的Tomcat、DispatcherServlet等配置好了再为其添加额外的处理类而已,当然,这个类也是非常重要的,诸如static-location这个配置也是在这里面完成的。这里完成了上述第三个关键点。

2.2 DispatcherServletAutoConfiguration类

接下来我们看到注解@AutoConfigureAfter里面的DispatcherServletAutoConfiguration类,看名字就能知道这个类大致是干嘛的了,其部分关键源码如下:

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
    public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = 
            "dispatcherServlet";
    public static final String 
            DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = 
                    "dispatcherServletRegistration";
    @Configuration
    @Conditional(DefaultDispatcherServletCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties({ HttpProperties.class, 
            WebMvcProperties.class })
    protected static class DispatcherServletConfiguration {
        private final HttpProperties httpProperties;
        private final WebMvcProperties webMvcProperties;
        @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServlet dispatcherServlet() {
            ...
        }
        @Bean
        @ConditionalOnBean(MultipartResolver.class)
        @ConditionalOnMissingBean(name = 
                DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
        public MultipartResolver multipartResolver(
                MultipartResolver resolver) {
           return resolver;
        }
    }
    @Configuration
    @Conditional(DispatcherServletRegistrationCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties(WebMvcProperties.class)
    @Import(DispatcherServletConfiguration.class)
    protected static class DispatcherServletRegistrationConfiguration {
        private final WebMvcProperties webMvcProperties;
        private final MultipartConfigElement multipartConfig;
        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        @ConditionalOnBean(value = DispatcherServlet.class, 
                name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServletRegistrationBean 
                dispatcherServletRegistration(
                        DispatcherServlet dispatcherServlet) {
            DispatcherServletRegistrationBean registration = 
                new DispatcherServletRegistrationBean(dispatcherServlet,
                this.webMvcProperties.getServlet().getPath());
            registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
            registration.setLoadOnStartup(
                    this.webMvcProperties.getServlet().getLoadOnStartup());
            if (this.multipartConfig != null) {
               registration.setMultipartConfig(this.multipartConfig);
            }
            return registration;
        }
    }
    @Order(Ordered.LOWEST_PRECEDENCE - 10)
    private static class DefaultDispatcherServletCondition 
            extends SpringBootCondition {
        ...
    }
}

看到这里终于看到DispatcherServlet这个SpringMVC核心类了,可以看到这个自动配置类做的大致事情就是声明一个DispatcherSerlvet的Bean对象,在DispatcherServletRegistrationConfiguration这个内部类中使用DispatcherServletRegistrationBean对象完成DispatcherServlet注册进Serlvet上下文的操作,因为其最顶层实现了ServletContextInitializer接口,这个接口的onStartup方法将会在Tomcat容器启动后被调用,DispatcherServletRegistrationBean便是利用这点,在onStartup方法中完成了编码式的注册Servlet操作。

而最下面的DefaultDispatcherServletCondition类功能便是判断所必需的的类是不是已经在Spring工厂中。

至此,第二个关键点便完成。

2.3 ServletWebServerFactoryAutoConfiguration类

我们可以看到DispatcherServletAutoConfiguration类依赖于这个类完成后才能执行,根据这个名称也可以十有八九的才出来这个类是干嘛用的。其部分关键源码如下:

@Configuration
@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 {
    @Bean
    public ServletWebServerFactoryCustomizer 
            servletWebServerFactoryCustomizer(
                    ServerProperties serverProperties) {
       return new ServletWebServerFactoryCustomizer(serverProperties);
    }
    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer 
            tomcatServletWebServerFactoryCustomizer(
                    ServerProperties serverProperties) {
       return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }
    public static class BeanPostProcessorsRegistrar 
            implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
        @Override
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata,
                BeanDefinitionRegistry registry) {
           if (this.beanFactory == null) {
              return;
           }
           registerSyntheticBeanIfMissing(registry, 
                   "webServerFactoryCustomizerBeanPostProcessor",
                   WebServerFactoryCustomizerBeanPostProcessor.class);
           registerSyntheticBeanIfMissing(registry, 
                   "errorPageRegistrarBeanPostProcessor",
                   ErrorPageRegistrarBeanPostProcessor.class);
        }
    }
}
public class ServletWebServerFactoryCustomizer
        implements WebServerFactoryCustomizer
                <ConfigurableServletWebServerFactory>, Ordered {
    ...
    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
       PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
       map.from(this.serverProperties::getPort).to(factory::setPort);
       map.from(this.serverProperties::getAddress).to(factory::setAddress);
       map.from(this.serverProperties.getServlet()
               ::getContextPath).to(factory::setContextPath);
       map.from(this.serverProperties.getServlet()
               ::getApplicationDisplayName).to(factory::setDisplayName);
       map.from(this.serverProperties.getServlet()
               ::getSession).to(factory::setSession);
       map.from(this.serverProperties::getSsl).to(factory::setSsl);
       map.from(this.serverProperties.getServlet()
               ::getJsp).to(factory::setJsp);
       map.from(this.serverProperties
               ::getCompression).to(factory::setCompression);
       map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
       map.from(this.serverProperties
               ::getServerHeader).to(factory::setServerHeader);
       map.from(this.serverProperties.getServlet()
               ::getContextParameters).to(factory::setInitParameters);
    }
}

我们看到这个类中的两个类后缀Customizer便可以看出,这两个Bean是使用ServerProperties的属性对Servlet容器进行自定义操作的,而点进去看这两个中的ServletWebServerFactoryCustomizer类便可以直接看到其就是对port、address和contextPath等属性进行自定义赋值的。

而BeanPostProcessorsRegistrar中将被调用的WebServerFactoryCustomizerBeanPost-Processor类将是调用Springboot完成自定义Tomcat属性的关键,这个类将会调用各种Customizer完成自定义操作(BeanPostProcessor看过Spring接口源码便能知道这个接口的大致作用)。

至此,我们可以说完成了第一个关键点的一半,知道了port等这些属性是在哪里被设置进Tomcat容器的。

2.4 ServletWebServerFactoryConfiguration类

跟着ServletWebServerFactoryAutoConfiguration类中@Import属性来到了这个类,其关键源码如下:

@Configuration
class ServletWebServerFactoryConfiguration {
    @Configuration
    @ConditionalOnClass({ Servlet.class, Tomcat.class, 
            UpgradeProtocol.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, 
            search = SearchStrategy.CURRENT)
    public static class EmbeddedTomcat {
        @Bean
        public TomcatServletWebServerFactory 
                tomcatServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }
    }
}

这个类实际上是有Jetty这些容器的初始化声明,但由于我们使用的是Tomcat,因此其它的便不贴出来了。至此,我们所需的四个关键点都已经被注册进了Spring工厂中,只需要Spring工厂在刷新时使用这些Bean即可。

2.5 ServletWebServerApplicationContext类

已经有了上述的四个关键点了,接下来看下Tomcat容器在哪里被使用的,部分源码如下:

public class ServletWebServerApplicationContext 
        extends GenericWebApplicationContext
        implements ConfigurableWebServerApplicationContext {
    @Override
    public final void refresh() 
            throws BeansException, IllegalStateException {
       try {
          super.refresh();
       }
       catch (RuntimeException ex) {
          stopAndReleaseWebServer();
          throw ex;
       }
    }
    @Override
    protected void onRefresh() {
       super.onRefresh();
       try {
          createWebServer();
       }
       catch (Throwable ex) {
          throw new ApplicationContextException(
                  "Unable to start web server", ex);
       }
    }
    @Override
    protected void finishRefresh() {
       super.finishRefresh();
       WebServer webServer = startWebServer();
       if (webServer != null) {
          publishEvent(
                  new ServletWebServerInitializedEvent(webServer, this));
       }
    }
    @Override
    protected void onClose() {
       super.onClose();
       stopAndReleaseWebServer();
    }
    private void createWebServer() {
       WebServer webServer = this.webServer;
       ServletContext servletContext = getServletContext();
       if (webServer == null && servletContext == null) {
          ServletWebServerFactory factory = getWebServerFactory();
          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();
    }
}

可以看到这里有个getWebServerFactory方法,这个方法里面便是从Spring工厂中获取ServletWebServerFactory类的Bean对象,在这个获取过程中将会调用BeanPostProcessor,对将要创建的Bean进行特殊的处理。这个BeanPostProcessor便是BeanPostProcessorsRegistrar里面被注册进工厂的WebServerFactoryCustomizerBeanPostProcessor类,即对WebServer进行自定义化的Bean后置处理类,在这里面会分别调用Spring工厂中WebServerFactoryCustomizer接口实现的各个Bean对象,即XXXServletWebServerXXXAutoConfiguration类中被声明实例化的Bean对象,进而完成对Tomcat的创建及初始化。

3.后话

一起来看看Springboot对亲儿子SpringMVC嵌入进框架所做的努力吧,spring.factories文件涉及的AutoConfiguration类如下:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet
        .DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet
        .ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error
        .ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet
        .HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet
        .MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet
        .WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded
        .EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive
        .ReactiveWebServerFactoryAutoConfiguration,\

SpringMVC和Tomcat集成进Springboot,官方为其设置了八个自动配置类,再看看Mybatis的自动配置类,只有一个MybatisAutoConfiguration,对比可知差距。这也说明了SpringMVC涉及之复杂以及Spring官方将其解耦做了多少努力,而Mybatis又有多“轻量”。