这几天在使用springboot时,controller层接受前端传入的数据为null,而在发送请求时也是有数据的,这就让我很疑惑,于是在查看了一下源码,发现了问题所在。
1. 我们都知道,在springboot启动完成之后,会加载很多的bean进入容器,怎么加载的请在网上自行搜索,其中就有这么一个配置类DispatcherServletAutoConfiguration.java,看源码:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.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;
public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {
this.webMvcProperties = webMvcProperties;
}
@Bean //就是这个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;
}
}
}
我们会发现DispatcherServletAutoConfiguration.java类中DispatcherServletConfiguration内部类中会向容器中导入一个MultipartResolver类型的bean,要想导入这个bean是有前提的,容器中需要有MultipartResolver.class的类,而且需要当容器中没有这个名字的bean,name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME,而这个name是这样的:
package org.springframework.web.servlet;
public class DispatcherServlet extends org.springframework.web.servlet.FrameworkServlet {
public static final java.lang.String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
这样我们就知道了,当我们容器中有MultipartResolver.class这个类,却没有multipartResolver名字的bean,这时springboot认为我们的命名不规范,就会容器中注册一个multipartResolver名字的,类型为MultipartResolver的bean。
2. 我们查看另一个配置类MultipartAutoConfiguration.java,
@Configuration
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class,
MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.http.multipart", name = "enabled",
matchIfMissing = true)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {
private final MultipartProperties multipartProperties;
public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
this.multipartProperties = multipartProperties;
}
@Bean
@ConditionalOnMissingBean({ MultipartConfigElement.class,
CommonsMultipartResolver.class })
public MultipartConfigElement multipartConfigElement() {
return this.multipartProperties.createMultipartConfig();
}
@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
@ConditionalOnMissingBean(MultipartResolver.class)
public StandardServletMultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
return multipartResolver;
}
}
它主要往容器中添加两个bean,在添加multipartConfigElement时会判断容器中是否缺少CommonsMultipartResolver和MultipartConfigElement类型的bean,如果缺少,那么就multipartConfigElement加入容器中。
第二个bean是当缺少MultipartResolver类型的bean时,加入到容器。
当我们知道这些就可以解决为啥controller传入的为空,我自己写的MultipartResolver是这样定义的:
//显示声明CommonsMultipartResolver为mutipartResolver
@Bean(name = "multipartResolver")
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setDefaultEncoding("UTF-8");
resolver.setMaxUploadSize(50 * 1024 * 1024);//上传文件大小 50M 50*1024*1024
return resolver;
}
这样定义我们来分析一下,首先容器会将在我们自己定义的bean到容器中,然后判断前面的情况,在DispatcherServletAutoConfiguration类中的bean不会被加入到容器,为什么?因为我们自己定义的bean已经叫multipartResolver了,所以不会加入到容器。
再看MultipartAutoConfiguration,multipartConfigElement会被加入到容器,因为没有CommonsMultipartResolver类型的bean,我自己写的向上转型了,StandardServletMultipartResolver 类型bean不会被加入到容器,这时容器有两个bean,而且我自己定义的bean是MultipartResolver ,这时就会出错了。
出错原因是我们定义的MultipartResolver要明确的指出是哪个类的,不应该是MultipartResolver这个接口类型,改成
//显示声明CommonsMultipartResolver为mutipartResolver
@Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setDefaultEncoding("UTF-8");
resolver.setMaxUploadSize(50 * 1024 * 1024);//上传文件大小 50M 50*1024*1024
return resolver;
}
就可以了,这时在思考一下,会发现springboot只会将我们的multipartResolver加入到容器,multipartConfigElement则不会加入到容器。