SpringWebMvc 自动装配大纲:
在引入 spring-boot-starter-web
的依赖后,SpringBoot 会自动进行 Web 环境的装载,按照 SpringFramework 一贯的规律,SpringWebMvc 的自动装配类应该叫:WebMvcAutoConfiguration
WebMvcAutoConfiguration
@Configuration
//当前环境必须是WebMvc(Servlet)环境
@ConditionalOnWebApplication(type = Type.SERVLET)
//当前运行环境的classpath中必须有Servlet类,DispatcherServlet类,WebMvcConfigurer类
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
//如果没有自定义WebMvc的配置类,则使用本自动配置
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration
在配置中标注了,WebMvcAutoConfiguration
必须在 DispatcherServletAutoConfiguration
、TaskExecutionAutoConfiguration
、ValidationAutoConfiguration
执行完后再执行。
DispatcherServletAutoConfiguration
/**
* DispatcherServlet 的自动配置。它起作用应该依赖于一个已经存在嵌入式Web服务器的独立应用程序,也适用于使用 SpringBootServletInitializer 的可部署应用程序。
*/
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration
其中 SpringBootServletInitializer 是SpringBoot用于打war包时留给Web容器初始化应用的钩子。
DispatcherServletAutoConfiguration 的源码中又标注了 @AutoConfigureAfter ,说明它又要在 ServletWebServerFactoryAutoConfiguration 之后再执行。
ServletWebServerFactoryAutoConfiguration
@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
这个类又导入了几个组件:EmbeddedTomcat
、EmbeddedJetty
、EmbeddedUndertow
、BeanPostProcessorsRegistrar
EmbeddedTomcat
@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();
}
}
由条件装配的注解 @ConditionalOnClass
可以看到,当前 classpath 下必须有 Tomcat 这个类,该配置类才会生效。对比 Jetty:
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.webapp.WebAppContext;
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty
默认导入的 spring-boot-starter-web 中导入的是 Tomcat 的依赖,故 Jetty 不会生效。
ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar
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);
}
private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
它实现了 ImportBeanDefinitionRegistrar 接口,在registerBeanDefinitions 中可以编程式向IOC容器中注入组件。它注册的两个组件是:WebServerFactoryCustomizerBeanPostProcessor、ErrorPageRegistrarBeanPostProcessor。
WebServerFactoryCustomizerBeanPostProcessor
Bean的后置处理器,它将 Bean 工厂中的所有 WebServerFactoryCustomizer 类型的 Bean 应用于 WebServerFactory 类型的 Bean。
它的作用是执行组件定制器
ErrorPageRegistrarBeanPostProcessor
Bean的后置处理器,它将Bean工厂中的所有 ErrorPageRegistrars 应用于 ErrorPageRegistry 类型的Bean。
它的作用是将所有设置的错误页跳转规则注册到错误处理器中。
ErrorPageRegistry 接口的源码:
public interface ErrorPageRegistry {
/**
* Adds error pages that will be used when handling exceptions.
* 添加错误页面
*/
void addErrorPages(ErrorPage... errorPages);
}
SpringBoot中的Customizer(定制器)
一般情况下,修改 SpringBoot 的配置,都是通过 application.yml
显示的声明配置。除此之外,还可以使用 Customizer 定制器机制
在 WebMvc 模块中,使用 Customizer 修改配置,可以实现 WebServletFactoryCustomizer
接口。该接口可以传入泛型,泛型的类型是 ServletWebServerFactory
例:
@Order(0)
@Component
public class WebMvcCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered {
@Override
public void customize(TomcatServletWebServerFactory factory) {
factory.setPort(9090);
factory.setContextPath("/demo");
}
@Override
public int getOrder() {
return 0;
}
}
Customizer 可以设置配置顺序(上面的 @Order 注解,或 Ordered 接口),通过配置执行顺序,可以自定义的覆盖某些自动配置,达到个性化配置的目的。
提这个 Customizer 机制,是为了看下面的配置类:
ServletWebServerFactoryAutoConfiguration中注册的其他组件
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);
}
}
这里创建了两个定制器,并把 ServerProperties 传入,让定制器根据配置信息做自动化配置。
ServerProperties的来源
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
// ......
@ConfigurationProperties 的作用:可用于某个类上,设置属性profix用于指定在工程的全局配置文件(application.properties 或 application.yml)中的配置的根信息。
简言之, @ConfigurationProperties 可以实现指定属性开头的属性值注入。
那么 ServerProperties 的属性值来源,就是全局配置文件中的server开头的所有配置。
以上执行完毕后,ServletWebServerFactoryAutoConfiguration 的全部配置也就完成了,下面执行 DispatcherServletAutoConfiguration 。
DispatcherServletAutoConfiguration
// 最高配置优先级
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
// Servlet环境下才生效
@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";
// 注册DispatcherServlet的配置类
@Configuration
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
// 启用配置文件与Properties的映射
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
private final HttpProperties httpProperties;
private final WebMvcProperties webMvcProperties;
public DispatcherServletConfiguration(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
this.httpProperties = httpProperties;
this.webMvcProperties = webMvcProperties;
}
// 构造DispatcherServlet
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet
.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setEnableLoggingRequestDetails(this.httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
// 注册文件上传组件
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}
// 注册DispatcherServletRegistration的配置类
@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;
public DispatcherServletRegistrationConfiguration(WebMvcProperties webMvcProperties,
ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
this.webMvcProperties = webMvcProperties;
this.multipartConfig = multipartConfigProvider.getIfAvailable();
}
// 辅助注册DispatcherServlet的RegistrationBean
@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;
}
}
//......
里面嵌套了两个内部类,分别注册 DispatcherServlet 和 DispatcherServletRegistrationBean 。
SpringBoot注册Servlet等组件
由于 SpringBoot 项目中没有 web.xml(Servlet3.0规范中就没有了),故有另外的方式注册Servlet三大组件。SpringBoot 提供两种方式。
- 组件扫描@ServletComponentScan
在启动类上标注 @ServletComponentScan 注解,指定 value/basePackage,即可扫描指定包及子包下所有的 Servlet 组件。
之后注册 Servlet、Filter、Listener 组件,就可以像 Servlet3.0 规范后的方式,直接在 Servlet 上标注 @WebServlet 等注解即可。
- 借助RegistrationBean
自定义的Servlet可以创建 ServletRegistrationBean
<T extends Servlet>
。使用时,只需要在配置类中注册一个 ServletRegistrationBean,创建它的对象时,使用有参构造方法,传入 Servlet 和 urlMapping 即可。
例:
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("demo servlet");
}
}
public class DemoServletRegistryBean extends ServletRegistrationBean<DemoServlet> {
public DemoServletRegistryBean(DemoServlet servlet, String... urlMappings) {
super(servlet, urlMappings);
}
}
@Configuration
public class ServletConfiguration {
@Bean
public DemoServletRegistryBean demoServletRegistryBean() {
return new DemoServletRegistryBean(new DemoServlet(), "/demo/servlet");
}
}
注册DispatcherServlet
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
private final HttpProperties httpProperties;
private final WebMvcProperties webMvcProperties;
public DispatcherServletConfiguration(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
this.httpProperties = httpProperties;
this.webMvcProperties = webMvcProperties;
}
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet
.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setEnableLoggingRequestDetails(this.httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
DispatcherServletConfiguration 类上标注了 @EnableConfigurationProperties,代表启用指定类的 ConfigurationProperties 功能。
下面创建 DispatcherServlet,并将默认的一些配置设置到 DispatcherServlet 中。这些属性就来自于已经被启用的 HttpProperties、WebMvcProperties 中。
WebMvcConfiguration
在SpringBoot2.x中,自定义的WebMvc配置需要实现 WebMvcConfigurer 接口,并重写接口中需要配置的方法即可。
WebMvcAutoConfigurationAdapter 也实现了该接口,并进行默认配置。
@Configuration
// 导入配置类
@Import(EnableWebMvcConfiguration.class)
// 启用WebMvcProperties、ResourceProperties
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware
配置HttpMessageConverter
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.messageConvertersProvider
.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
}
ViewResolver的组件注册
// 最常用的视图解析器
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
// 使用前后缀拼接的方式
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
@Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
}
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
// ContentNegotiatingViewResolver uses all the other view resolvers to locate
// a view so it should have a high precedence
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
// 国际化组件
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
注册了 ViewResolver、LocaleResolver。
- ContentNegotiatingViewResolver:最高级的 ViewResolver,负责将视图解析的工作代理给不同的 ViewResolver 来处理不同的View
- BeanNameViewResolver:如果 Controller 中返回的视图名称恰好有一个Bean的名称与之相同,则会交予Bean处理
- InternalResourceViewResolver:最常用的 ViewResolver,通过设置前后缀来匹配视图
5.1.3 静态资源映射
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
// 映射webjars
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
// 映射静态资源路径
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
注册静态资源路径。可以看到,它将** /webjars** 路径下的资源都映射到 classpath:/META-INF/resources/webjars 中。
除了注册 webjars 的资源路径,倒数第二行,还取到 resourceProperties 中的 staticLocations,也加入进去。
而 ResourceProperties 中的 staticLocations:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
这也解释了为什么静态资源文件放在 resources 中和放在 static 中都能被正常加载的原因。
主页的设置
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext) {
// 调用getWelcomePage,跳转到下面的方法中
return new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext),
applicationContext, getWelcomePage(), this.mvcProperties.getStaticPathPattern());
}
static String[] getResourceLocations(String[] staticLocations) {
String[] locations = new String[staticLocations.length + SERVLET_LOCATIONS.length];
System.arraycopy(staticLocations, 0, locations, 0, staticLocations.length);
System.arraycopy(SERVLET_LOCATIONS, 0, locations, staticLocations.length, SERVLET_LOCATIONS.length);
return locations;
}
private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
// this::getIndexHtml调用下面的方法
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
由此可以看出,欢迎页面/主页的设置,是取的静态资源路径中的** index.html** 文件。
应用图标的设置
@Configuration
@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
public static class FaviconConfiguration implements ResourceLoaderAware {
// ......
// 配置图标映射器
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", faviconRequestHandler()));
return mapping;
}
// .......
}
可以明显的看到默认的图标名称是 favicon.ico,且放在静态路径下的任意位置都可以被扫描到。
EnableWebMvcConfiguration
注册的核心组件
SpringWebMvc 中最核心的两个组件:处理器适配器、处理器映射器:
// 处理器适配器
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
adapter.setIgnoreDefaultModelOnRedirect(
this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
// 处理器映射器
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
// Must be @Primary for MvcUriComponentsBuilder to work
return super.requestMappingHandlerMapping();
}
注册了 Hibernate-Validator 参数校验器:
// 校验器
@Bean
@Override
public Validator mvcValidator() {
if (!ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
return super.mvcValidator();
}
return ValidatorAdapter.get(getApplicationContext(), getValidator());
}
注册了全局异常处理器:
@Override
// 全局异常处理器
protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
if (this.mvcRegistrations != null && this.mvcRegistrations.getExceptionHandlerExceptionResolver() != null) {
return this.mvcRegistrations.getExceptionHandlerExceptionResolver();
}
return super.createExceptionHandlerExceptionResolver();
}
@Override
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
super.configureHandlerExceptionResolvers(exceptionResolvers);
if (exceptionResolvers.isEmpty()) {
addDefaultHandlerExceptionResolvers(exceptionResolvers);
}
if (this.mvcProperties.isLogResolvedException()) {
for (HandlerExceptionResolver resolver : exceptionResolvers) {
if (resolver instanceof AbstractHandlerExceptionResolver) {
((AbstractHandlerExceptionResolver) resolver).setWarnLogCategory(resolver.getClass().getName());
}
}
}
}
总结
-
自动配置类是有执行顺序的, WebMvcAutoConfiguration 的执行顺序在 ServletWebServerFactoryAutoConfiguration 、DispatcherServletAutoConfiguration 之后。
-
SpringBoot会根据当前classpath下的类来决定装配哪些组件,启动哪种类型的Web容器。
-
WebMvc的配置包括消息转换器、视图解析器、处理器映射器、处理器适配器、静态资源映射配置、主页设置、应用图标设置等。
-
配置 SpringBoot 应用除了可以使用 properties、yml 之外,还可以使用 Customizer 来编程式配置。