目录
一、集成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);
}
}
其大致配置类结构图如下:
可以看到需要的仅三个对象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 {
...
}
}
其大致配置类结构图如下:
可以看到,基本上和传统的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>
其大致配置类结构图如下:
我们根据上面两个配置来具体确认一下SpringMVC哪些是必须的:
- Tomcat容器及相关的自定义配置项;
- 将DispatcherServlet当成一个Serlvet注册进Tomcat;
- SpringMVC核心类DispatcherSerlvet所必需的的HandlerMapping、Resolver等;
- 扫描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相关配置类结构图:
大致结构类图如上,其中不包含各种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又有多“轻量”。