自动配置
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.servlet.multipart", name = "enabled", matchIfMissing = true)
@ConditionalOnWebApplication(type = Type.SERVLET)
@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;
}
}
MultipartConfigElement
看看谁用到了这个类
居然是StandardServletMultipartResolver的注释说到了它.
Standard implementation of the MultipartResolver interface, based on the Servlet 3.0 Part API. To be added as “multipartResolver” bean to a Spring DispatcherServlet context, without any extra configuration at the bean level (see below).
Note: In order to use Servlet 3.0 based multipart parsing, you need to
- mark the affected servlet with a “multipart-config” section in web.xml,
- or with a javax.servlet.MultipartConfigElement in programmatic servlet registration,
- or (in case of a custom servlet class) possibly with a javax.servlet.annotation.MultipartConfig annotation on your servlet class.
Configuration settings such as maximum sizes or storage locations need to be applied at that servlet registration level; Servlet 3.0 does not allow for them to be set at the MultipartResolver level
相当于向servlet注册一个上传的配置,做法就是multipartProperties->MultipartConfigElement,属性映射
翻译过来就是
- 可以通过xml或者注解->注册servlet
或者通过配置属性,让springmvc的dispatchServlet具备这个上传配置
StandardServletMultipartResolver
代码流程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//逻辑this.multipartResolver.resolveMultipart(request)->new StandardMultipartHttpServletRequest(request, this.resolveLazily)
//具体看下面代码,不是懒加载的话就直接解析文件流
processedRequest = checkMultipart(request);
//true
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.真正执行的逻辑,包括参数的确定和返回值计算,关键在于参数确定,见下面
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
解析文件流
```java public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
throws MultipartException {
@Nullable private MultiValueMap
multipartFiles; //构造器 根据懒加载配置,看看是否进行解析 public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
throws MultipartException {
super(request);
if (!lazyParsing) {
parseRequest(request);
}
} //解析逻辑 private void parseRequest(HttpServletRequest request) {
try {
//servlet3.0原生api,直接从request中获取文件流,封装为Part
Collection<Part> parts = request.getParts();
this.multipartParameterNames = new LinkedHashSet<>(parts.size());
//封装到单key多value的Map中去
MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
for (Part part : parts) {
String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
ContentDisposition disposition = ContentDisposition.parse(headerValue);
String filename = disposition.getFilename();
if (filename != null) {
if (filename.startsWith("=?") && filename.endsWith("?=")) {
filename = MimeDelegate.decode(filename);
}
files.add(part.getName(), new StandardMultipartFile(part, filename));
}
else {
this.multipartParameterNames.add(part.getName());
}
}
//最终封装的放在这个request中的multipartFiles属性
setMultipartFiles(files);
}
catch (Throwable ex) {
handleParseFailure(ex);
}
} /**
- Obtain the MultipartFile Map for retrieval,
- lazily initializing it if necessary.
- @see #initializeMultipart()
*/
protected MultiValueMap
getMultipartFiles() { //根据属性,如果没有就加载 //懒加载true:则初始化的时候没有准备这个值,那么就是空的,就要初始化 //懒加载false:已经初始化好了这个属性,直接就可以返回这个属性 if (this.multipartFiles == null) {
} return this.multipartFiles; }//真正逻辑在parseRequest(getRequest()),跟构造器那个同样的方法
initializeMultipart();
}
<a name="C0E0h"></a>
## 确定参数
RequestMappingHandlerAdapter<br />#方法调用栈<br />handle<br />handleInternal<br />invokeHandlerMethod<br />new ServletInvocableHandlerMethod()-->设置参数-->invocableMethod.invokeAndHandle**(**webRequest, mavContainer**)**;<br />ServletInvocableHandlerMethod<br />#方法调用栈<br />invokeAndHandle<br />invokeForRequest<br />getMethodArgumentValues<br />策略模式的应用
```java
public class InvocableHandlerMethod extends HandlerMethod
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//核心代码1:RequestPartMethodArgumentResolver#supportsParameter->getArgumentResolver(1遍历 2缓存 3break)
//逻辑:parameter.hasParameterAnnotation(RequestPart.class)->true
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//核心代码2:RequestPartMethodArgumentResolver#resolveArgument->getArgumentResolver(1遍历 2缓存 3break)resolver.resolveArgument
//resolver.resolveArgument逻辑要看代码
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
上传文件请求参数解析器RequestPartMethodArgumentResolver
public class RequestPartMethodArgumentResolver extends AbstractMessageConverterMethodArgumentResolver {
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
RequestPart requestPart = parameter.getParameterAnnotation(RequestPart.class);
boolean isRequired = ((requestPart == null || requestPart.required()) && !parameter.isOptional());
String name = getPartName(parameter, requestPart);
parameter = parameter.nestedIfOptional();
Object arg = null;
//逻辑代码
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
arg = mpArg;
}
return adaptArgumentIfNecessary(arg, parameter);
}
干活的人MultipartResolutionDelegate,文件流已经封装在request里面的,何时封装的呢?就在上面的解析文件流中
public static Object resolveMultipartArgument(String name, MethodParameter parameter, HttpServletRequest request)
throws Exception {
MultipartHttpServletRequest multipartRequest =
WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
boolean isMultipart = (multipartRequest != null || isMultipartContent(request));
//单文件
if (MultipartFile.class == parameter.getNestedParameterType()) {
if (!isMultipart) {
return null;
}
if (multipartRequest == null) {
multipartRequest = new StandardMultipartHttpServletRequest(request);
}
return multipartRequest.getFile(name);
}
//文件集合Collection
else if (isMultipartFileCollection(parameter)) {
if (!isMultipart) {
return null;
}
if (multipartRequest == null) {
multipartRequest = new StandardMultipartHttpServletRequest(request);
}
List<MultipartFile> files = multipartRequest.getFiles(name);
return (!files.isEmpty() ? files : null);
}
//文件数组
else if (isMultipartFileArray(parameter)) {
if (!isMultipart) {
return null;
}
if (multipartRequest == null) {
multipartRequest = new StandardMultipartHttpServletRequest(request);
}
List<MultipartFile> files = multipartRequest.getFiles(name);
return (!files.isEmpty() ? files.toArray(new MultipartFile[0]) : null);
}
.......
else {
return UNRESOLVABLE;
}
}
总结
springmvc在servlet3.0上做的封装不多
multipartRequest.getFile
multipartRequest.getFiles(name)
解析的文件流存放位置:
用的时候根据MultipartFile接口的api去使用即可,
根据参数名,操作不同的流,或者是同名参数的多个流操作
设计还是不错的