Spring Boot
是一个流行的开源框架,它可以帮助开发者快速构建高质量的 Web
应用程序。在 Spring Boot
中,有很多功能和特性可以帮助开发者更好地开发 Web
应用程序,其中之一就是 DispatcherServlet
和 Multipart
。
1.引言
在本教程中,我们将深入探讨 DispatcherServlet 和 Multipart 的配置,以便开发者可以更好地理解和使用它们。我们将讨论如何配置这些组件,以及如何使用它们来处理请求和响应。
本教程适合那些已经熟悉 Spring Boot 框架的开发者,并且想要深入了解 DispatcherServlet 和 Multipart 的配置和使用方法。我们将使用一些示例来说明如何配置这些组件,并且会提供一些实用的技巧和建议,以帮助开发者更好地使用它们。
2.DispatcherServlet解析
如果大家搭建过SpringMVC应用,那么一定会写个几个xml配置文件,如
application.xml, spring-mvc.xml 等。一般来说,我们搭建项目的初始步骤如下:
- 初始化Spring MVC 的DispatcherServlet;
- 添加转码过滤器(HttpMessageConverter),保证客户端请求都能正确的编码
- 搭建视图解析器(view resolver),告诉Spring去哪里查找视图,以及视图方言(如Freemarker, Thymeleaf等)。
- 配置静态资源(css,js,image等)
- 配置mulpart解析器,保证文件上传正常。
- 配置一些错误处理,AOP切面日志等。
当我们开始使用SpringBoot来搭建我们的Web项目的时候,你会发现,这些事情SpringBoot默认都帮你处理了。
SpringBoot原则是约定优于配置,并且默认情况下,会在你的项目中使用这些约定。
接下来,让我们来了解下,幕后发生了什么?
首先让我们新建一个SpringBoot项目,并在src/main/resources/中的application.properties增加一行:debug=true
。
OK,现在我们启动下项目看看控制台的输出。
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
CodecsAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.http.codec.CodecConfigurer'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
···(中间就省略了)
Negative matches:
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
我们可以看到Spring Boot 的自动配置报告。它分为两部分:一部分是匹配上的(positive matches),列出了应用中所有的自动配置。另一部分是没有匹配上的(negative matches),这部分是应用在启动的时候,需求没有满足的Spring Boot自动配置。
3.DispatcherServletAutoConfiguration源码解析
接下来重点看下DispatcherServletAutoConfiguration这个类的源码(想想还是贴一下源码吧…)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
@EnableConfigurationProperties(ServerProperties.class)
public class DispatcherServletAutoConfiguration {
/*
* The bean name for a DispatcherServlet that will be mapped to the root URL "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/*
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {
private final WebMvcProperties webMvcProperties;
private final ServerProperties serverProperties;
public DispatcherServletConfiguration(WebMvcProperties webMvcProperties,
ServerProperties serverProperties) {
this.webMvcProperties = webMvcProperties;
this.serverProperties = serverProperties;
}
@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());
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;
}
@Bean
public DispatcherServletPathProvider mainDispatcherServletPathProvider() {
return () -> DispatcherServletConfiguration.this.serverProperties.getServlet()
.getPath();
}
}
@Configuration
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
private final ServerProperties serverProperties;
private final WebMvcProperties webMvcProperties;
private final MultipartConfigElement multipartConfig;
public DispatcherServletRegistrationConfiguration(
ServerProperties serverProperties, WebMvcProperties webMvcProperties,
ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
this.serverProperties = serverProperties;
this.webMvcProperties = webMvcProperties;
this.multipartConfig = multipartConfigProvider.getIfAvailable();
}
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) {
ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(
dispatcherServlet,
this.serverProperties.getServlet().getServletMapping());
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 {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("Default DispatcherServlet");
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
List<String> dispatchServletBeans = Arrays.asList(beanFactory
.getBeanNamesForType(DispatcherServlet.class, false, false));
if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome.noMatch(message.found("dispatcher servlet bean")
.items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome
.noMatch(message.found("non dispatcher servlet bean")
.items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
if (dispatchServletBeans.isEmpty()) {
return ConditionOutcome
.match(message.didNotFind("dispatcher servlet beans").atAll());
}
return ConditionOutcome.match(message
.found("dispatcher servlet bean", "dispatcher servlet beans")
.items(Style.QUOTE, dispatchServletBeans)
.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
}
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DispatcherServletRegistrationCondition
extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);
if (!outcome.isMatch()) {
return outcome;
}
return checkServletRegistration(beanFactory);
}
private ConditionOutcome checkDefaultDispatcherName(
ConfigurableListableBeanFactory beanFactory) {
List<String> servlets = Arrays.asList(beanFactory
.getBeanNamesForType(DispatcherServlet.class, false, false));
boolean containsDispatcherBean = beanFactory
.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
if (containsDispatcherBean
&& !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome
.noMatch(startMessage().found("non dispatcher servlet")
.items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
return ConditionOutcome.match();
}
private ConditionOutcome checkServletRegistration(
ConfigurableListableBeanFactory beanFactory) {
ConditionMessage.Builder message = startMessage();
List<String> registrations = Arrays.asList(beanFactory
.getBeanNamesForType(ServletRegistrationBean.class, false, false));
boolean containsDispatcherRegistrationBean = beanFactory
.containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
if (registrations.isEmpty()) {
if (containsDispatcherRegistrationBean) {
return ConditionOutcome
.noMatch(message.found("non servlet registration bean").items(
DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
return ConditionOutcome
.match(message.didNotFind("servlet registration bean").atAll());
}
if (registrations
.contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) {
return ConditionOutcome.noMatch(message.found("servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
if (containsDispatcherRegistrationBean) {
return ConditionOutcome
.noMatch(message.found("non servlet registration bean").items(
DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
return ConditionOutcome.match(message.found("servlet registration beans")
.items(Style.QUOTE, registrations).append("and none is named "
+ DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
private ConditionMessage.Builder startMessage() {
return ConditionMessage.forCondition("DispatcherServlet Registration");
}
}
}
按步骤来观察下调用过程大致如下:
- 1.先看类注解@Configuration,很明显这是一个典型的SpringBoot配置类。
- 2.@AutoConfigurerOrder来声明优先级。
- 3.@AutoConfigureAfter 或@AutoConfigureBefore, 从而进一步细化配置处理的顺序。
- 4.@ConditionalOnClass (DispatcherServlet.class)这个特殊的配置,能够确保我们的类路径下包含 DispatcherServlet。
- 5.@Conditional(DefaultDispatcherServletCondition.class)条件满足的情况下,ServletRegistrationBean 函 数才会启用,这有些复杂,但是能够检查在你的配置中,是否已经注册了分发器 Servlet
- 6.@ConditionalOnMissingBean(name=DispatcherServlet.MULTIPARTRESOLVER BEAN_NAME)条件的情况下,MultipartResolver 函数才会处于激活状态,例如,当我们自己还没有注册的时候。
5.结语
上一篇教程:精通spring boot:手写一个spring-boot-starter包
通过本教程,开发者将能够更好地理解和使用 DispatcherServlet 和 Multipart,从而更好地开发 Spring Boot 应用程序。
[…] 上一篇教程:精通Spring Boot: DispatcherServlet和Multipart配置 […]