参考 知识星球 中 芋道源码 星球的源码解析,一个活跃度非常高的 Java 技术社群,感兴趣的小伙伴可以加入 芋道源码 星球,一起学习😄
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读
Spring 版本:5.1.14.RELEASE
该系列其他文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》
MultipartResolver 组件,内容类型( Content-Type )为 multipart/* 的请求的解析器,主要解析文件上传的请求。例如,MultipartResolver 会将 HttpServletRequest 封装成 MultipartHttpServletRequest 对象,便于获取参数信息以及上传的文件
使用方式,可以参考《MyBatis 使用手册》中的 集成 Spring 模块下的 spring-mvc.xml 文件中配置 MultipartResolver 为 CommonsMultipartResolver 实现类,然后在方法入参中用 MultipartFile 类型接收
关于在 SpringBoot 中如何使用文件上传可参考 Spring 官方文档
回顾先来回顾一下在 DispatcherServlet 中处理请求的过程中哪里使用到 MultipartResolver 组件,可以回到《一个请求的旅行过程》中的 DispatcherServlet 的 doDispatch 方法中看看,如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// ... 省略相关代码// 检测请求是否为上传请求,如果是则通过 multipartResolver 将其封装成 MultipartHttpServletRequest 对象processedRequest = checkMultipart(request);// ... 省略相关代码}protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {// 如果该请求是一个涉及到 multipart (文件)的请求if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {if (request.getDispatcherType().equals(DispatcherType.REQUEST)) {logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");}}else if (hasMultipartException(request)) {logger.debug("Multipart resolution previously failed for current request - " +"skipping re-resolution for undisturbed error rendering");}else {try {// 将 HttpServletRequest 请求封装成 MultipartHttpServletRequest 对象,解析请求里面的参数以及文件return this.multipartResolver.resolveMultipart(request);}catch (MultipartException ex) {if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {logger.debug("Multipart resolution failed for error dispatch", ex);// Keep processing error dispatch with regular request handle below}else {throw ex;}}}}// If not returned before: return original request.return request;}处,如果该请求是一个涉及到 multipart (文件)的请求,则通过 multipartResolver 将 HttpServletRequest 请求封装成 MultipartHttpServletRequest 对象,解析请求里面的参数以及文件
MultipartResolver接口org.springframework.web.multipart.MultipartResolver 接口,内容类型( Content-Type )为 multipart/* 的请求的解析器接口,代码如下:
public interface MultipartResolver {/** * 是否为 multipart 请求 */boolean isMultipart(HttpServletRequest request);/** * 将 HttpServletRequest 请求封装成 MultipartHttpServletRequest 对象 */MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;/** * 清理处理 multipart 产生的资源,例如临时文件 */void cleanupMultipart(MultipartHttpServletRequest request);}MultipartResolver 接口体系的结构如下:
一共有两块:
上半部分,MultipartRequest 接口及其实现类下半部分,MultipartResolver 接口以及其实现类初始化过程在 DispatcherServlet 的 initMultipartResolver(ApplicationContext context) 方法,初始化 MultipartResolver 组件,方法如下:
private void initMultipartResolver(ApplicationContext context) {try {// 从 Spring 上下文中获取名称为 "multipartResolver" ,类型为 MultipartResolver 的 Beanthis.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);if (logger.isTraceEnabled()) {logger.trace("Detected " + this.multipartResolver);}else if (logger.isDebugEnabled()) {logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());}}catch (NoSuchBeanDefinitionException ex) {// Default is no multipart resolver.this.multipartResolver = null;if (logger.isTraceEnabled()) {logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");}}}在 Spring MVC 中,multipartResolver 默认为 null【注意】,需要自己配置,例如《MyBatis 使用手册》中的 集成 Spring 模块下的 spring-mvc.xml 文件中配置 MultipartResolver 为 CommonsMultipartResolver 实现类,也可以配置为 StandardServletMultipartResolver 实现类
在 Spring Boot 中,multipartResolver 默认为 StandardServletMultipartResolver 实现类
目前 Spring 只提供上面两种实现类,接下来依次进行分析
StandardServletMultipartResolverorg.springframework.web.multipart.support.StandardServletMultipartResolver,实现 MultipartResolver 接口,基于 Servlet 3.0 标准的上传文件 API 的 MultipartResolver 实现类,代码如下:
public class StandardServletMultipartResolver implements MultipartResolver {/*** 是否延迟解析*/private boolean resolveLazily = false;public void setResolveLazily(boolean resolveLazily) { this.resolveLazily = resolveLazily;}@Overridepublic boolean isMultipart(HttpServletRequest request) { // 请求的 Content-type 必须 multipart/ 开头 return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");}@Overridepublic MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException { return new StandardMultipartHttpServletRequest(request, this.resolveLazily);}@Overridepublic void cleanupMultipart(MultipartHttpServletRequest request) { if (!(request instanceof AbstractMultipartHttpServletRequest) || ((AbstractMultipartHttpServletRequest) request).isResolved()) { // To be on the safe side: explicitly delete the parts, // but only actual file parts (for Resin compatibility) try {// 删除临时的 Partfor (Part part : request.getParts()) {if (request.getFile(part.getName()) != null) { part.delete();}} } catch (Throwable ex) {LogFactory.getLog(getClass()).warn("Failed to perform cleanup of multipart items", ex); } }}}isMultipart(HttpServletRequest request)方法,请求的 Content-type 是否以 multipart/ 开头
resolveMultipart(HttpServletRequest request)方法,直接将 HttpServletRequest 转换成 StandardMultipartHttpServletRequest 对象
cleanupMultipart(MultipartHttpServletRequest request)方法,清理资源,删除临时的 javax.servlet.http.Part 们
StandardMultipartHttpServletRequestorg.springframework.web.multipart.support.StandardMultipartHttpServletRequest,继承 AbstractMultipartHttpServletRequest 抽象类,基于 Servlet 3.0 的 Multipart HttpServletRequest 实现类,包含了一个 javax.servlet.http.HttpServletRequest 对象和它的 javax.servlet.http.Part 对象们,其中 Part 对象会被封装成 StandardMultipartFile 对象
构造方法public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {/** * 普通参数名的集合 */@Nullableprivate Set multipartParameterNames;public StandardMultipartHttpServletRequest(HttpServletRequest request) throws MultipartException {this(request, false);}public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing) throws MultipartException {super(request);// 如果不需要延迟解析if (!lazyParsing) {// 解析请求parseRequest(request);}}}multipartParameterNames:普通参数名的集合,非上传文件的参数名如果不需要延迟解析,则调用 parseRequest(HttpServletRequest request) 方法,直接解析请求parseRequestparseRequest(HttpServletRequest request) 方法,解析请求,解析 HttpServletRequest 中的 Part 对象,如果是文件,则封装成 StandardMultipartFile 对象,否则就是普通参数,获取其名称,如下:
private void parseRequest(HttpServletRequest request) {try {// 从 HttpServletRequest 中获取 Part 们Collection parts = request.getParts();this.multipartParameterNames = new LinkedHashSet(parts.size());MultiValueMap files = new LinkedMultiValueMap(parts.size());// 遍历 parts 数组for (Part part : parts) {// 获得请求头中的 Content-Disposition 信息,MIME 协议的扩展String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);// 对 Content-Disposition 信息进行解析,生成 ContentDisposition 对象// 包含请求参数信息,以面向“对象”的形式进行访问ContentDisposition disposition = ContentDisposition.parse(headerValue);// 获得文件名String filename = disposition.getFilename();// 情况一,文件名非空,说明是文件参数,则创建 StandardMultipartFile 对象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());}}// 将上面生成的 StandardMultipartFile 文件对象们,设置到父类的 multipartFiles 属性中setMultipartFiles(files);}catch (Throwable ex) {handleParseFailure(ex);}}从 HttpServletRequest 中获取 Part 们
遍历 parts 数组
从 Part 对象中获得请求头中的 Content-Disposition 信息,MIME 协议的扩展对 Content-Disposition 信息进行解析,生成 ContentDisposition 对象,包含请求参数信息,以面向“对象”的形式进行访问从ContentDisposition 对象中获得文件名情况一,文件名非空,说明是文件参数,则创建 StandardMultipartFile 对象情况二,文件名为空,说明是普通参数,则保存参数名称将上面生成的 StandardMultipartFile 文件对象们,设置到父类的 multipartFiles 属性中
如果发生异常则抛出
其他方法/** 初始化请求 */@Overrideprotected void initializeMultipart() {parseRequest(getRequest());}/** 获取请求中的参数名称 */@Overridepublic Enumeration getParameterNames() {if (this.multipartParameterNames == null) {initializeMultipart();}if (this.multipartParameterNames.isEmpty()) {return super.getParameterNames();}// Servlet 3.0 getParameterNames() not guaranteed to include multipart form items// (e.g. on WebLogic 12) -> need to merge them here to be on the safe sideSet paramNames = new LinkedHashSet();Enumeration paramEnum = super.getParameterNames();while (paramEnum.hasMoreElements()) {paramNames.add(paramEnum.nextElement());}paramNames.addAll(this.multipartParameterNames);return Collections.enumeration(paramNames);}/** 获取请求中的参数,参数名和参数值的映射 */@Overridepublic Map getParameterMap() {if (this.multipartParameterNames == null) {initializeMultipart();}if (this.multipartParameterNames.isEmpty()) {return super.getParameterMap();}// Servlet 3.0 getParameterMap() not guaranteed to include multipart form items// (e.g. on WebLogic 12) -> need to merge them here to be on the safe sideMap paramMap = new LinkedHashMap(super.getParameterMap());for (String paramName : this.multipartParameterNames) {if (!paramMap.containsKey(paramName)) {paramMap.put(paramName, getParameterValues(paramName));}}return paramMap;}/** 获取请求的 Content-Type 内容类型 */@Overridepublic String getMultipartContentType(String paramOrFileName) {try {Part part = getPart(paramOrFileName);return (part != null ? part.getContentType() : null);}catch (Throwable ex) {throw new MultipartException("Could not access multipart servlet request", ex);}}/** 获取请求头信息 */@Overridepublic HttpHeaders getMultipartHeaders(String paramOrFileName) {try {Part part = getPart(paramOrFileName);if (part != null) {HttpHeaders headers = new HttpHeaders();for (String headerName : part.getHeaderNames()) {headers.put(headerName, new ArrayList(part.getHeaders(headerName)));}return headers;}else {return null;}}catch (Throwable ex) {throw new MultipartException("Could not access multipart servlet request", ex);}}StandardMultipartFileorg.springframework.web.multipart.support.StandardMultipartHttpServletRequest 的私有内部静态类,实现了 MultipartFile 接口和 Serializable 接口,内部封装了 javax.servlet.http.Part 对象和文件名称,代码如下:
private static class StandardMultipartFile implements MultipartFile, Serializable {private final Part part;private final String filename;public StandardMultipartFile(Part part, String filename) {this.part = part;this.filename = filename;}@Overridepublic String getName() {return this.part.getName();}@Overridepublic String getOriginalFilename() {return this.filename;}@Overridepublic String getContentType() {return this.part.getContentType();}@Overridepublic boolean isEmpty() {return (this.part.getSize() == 0);}@Overridepublic long getSize() {return this.part.getSize();}@Overridepublic byte[] getBytes() throws IOException {return FileCopyUtils.copyToByteArray(this.part.getInputStream());}@Overridepublic InputStream getInputStream() throws IOException {return this.part.getInputStream();}@Overridepublic void transferTo(File dest) throws IOException, IllegalStateException {this.part.write(dest.getPath());if (dest.isAbsolute() && !dest.exists()) {// Servlet 3.0 Part.write is not guaranteed to support absolute file paths:// may translate the given path to a relative location within a temp dir// (e.g. on Jetty whereas Tomcat and Undertow detect absolute paths).// At least we offloaded the file from memory storage; it'll get deleted// from the temp dir eventually in any case. And for our user's purposes,// we can manually copy it to the requested location as a fallback.FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest.toPath()));}}@Overridepublic void transferTo(Path dest) throws IOException, IllegalStateException {FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest));}}这个类封装了 Servlet 3.0 的 Part 对象,也就是我们常用到的 MultipartFile 对象,支持对文件的操作,内部其实都是调用 javax.servlet.http.Part 的方法
AbstractMultipartHttpServletRequestorg.springframework.web.multipart.support.AbstractMultipartHttpServletRequest 抽象类,继承了 HttpServletRequestWrapper 类,实现了 MultipartHttpServletRequest接口
该类是 StandardMultipartHttpServletRequest 和 DefaultMultipartHttpServletRequest 的父类,实现了一些公共的方法,代码如下:
public abstract class AbstractMultipartHttpServletRequest extends HttpServletRequestWrapper implements MultipartHttpServletRequest {/** * 请求中的文件信息 */@Nullableprivate MultiValueMap multipartFiles;protected AbstractMultipartHttpServletRequest(HttpServletRequest request) {super(request);}@Overridepublic HttpServletRequest getRequest() {return (HttpServletRequest) super.getRequest();}@Overridepublic HttpMethod getRequestMethod() {return HttpMethod.resolve(getRequest().getMethod());}/** 获取请求头信息 */@Overridepublic HttpHeaders getRequestHeaders() {HttpHeaders headers = new HttpHeaders();Enumeration headerNames = getHeaderNames();while (headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();headers.put(headerName, Collections.list(getHeaders(headerName)));}return headers;}/** 获取文件名称列表 */@Overridepublic Iterator getFileNames() {return getMultipartFiles().keySet().iterator();}/** 获取指定文件名的单个文件 */@Overridepublic MultipartFile getFile(String name) {return getMultipartFiles().getFirst(name);}/** 获取指定文件名的多个文件 */@Overridepublic List getFiles(String name) {List multipartFiles = getMultipartFiles().get(name);if (multipartFiles != null) {return multipartFiles;}else {return Collections.emptyList();}}@Overridepublic Map getFileMap() {return getMultipartFiles().toSingleValueMap();}@Overridepublic MultiValueMap getMultiFileMap() {return getMultipartFiles();}public boolean isResolved() {return (this.multipartFiles != null);}protected final void setMultipartFiles(MultiValueMap multipartFiles) {this.multipartFiles = new LinkedMultiValueMap(Collections.unmodifiableMap(multipartFiles));}protected MultiValueMap getMultipartFiles() {if (this.multipartFiles == null) {initializeMultipart();}return this.multipartFiles;}/** 交由子类实现 */protected void initializeMultipart() {throw new IllegalStateException("Multipart request not initialized");}}上面的方法都比较简单,用于获取请求中的文件对象
MultiValueMap multipartFiles属性,保存由子类解析出请求中的 Part 对象所封装成的 MultipartFile 对象
CommonsMultipartResolverorg.springframework.web.multipart.commons.CommonsMultipartResolver,实现 MultipartResolver、ServletContextAware 接口,继承 CommonsFileUploadSupport 抽象类,基于 Apache Commons FileUpload 的 MultipartResolver 实现类
如果需要使用这个 MultipartResolver 实现类,需要引入 commons-fileupload、commons-io 和 commons-codec 组件,例如:
commons-fileuploadcommons-fileupload1.4commons-iocommons-io2.8.0commons-codeccommons-codec1.15注意,如果 Spring Boot 项目中需要使用 CommonsMultipartResolver,需要在 application.yml 中添加如下配置,排除其默认的配置,如下:
spring: autoconfigure:exclude: org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration构造方法public class CommonsMultipartResolver extends CommonsFileUploadSupport implements MultipartResolver, ServletContextAware {/** * 是否延迟解析 */private boolean resolveLazily = false;public CommonsMultipartResolver() {super();}public CommonsMultipartResolver(ServletContext servletContext) {this();setServletContext(servletContext);}}isMultipart@Overridepublic boolean isMultipart(HttpServletRequest request) {// 必须是 POST 请求,且 Content-Type 为 multipart/ 开头return ServletFileUpload.isMultipartContent(request);}判断是否为 multipart 请求,必须是 POST 请求,且 Content-Type 为 multipart/ 开头
resolveMultipart@Overridepublic MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {Assert.notNull(request, "Request must not be null");if (this.resolveLazily) {return new DefaultMultipartHttpServletRequest(request) {@Overrideprotected void initializeMultipart() {// 解析请求,获取文件、参数信息MultipartParsingResult parsingResult = parseRequest(request);setMultipartFiles(parsingResult.getMultipartFiles());setMultipartParameters(parsingResult.getMultipartParameters());setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());}};}else {// 解析请求,获取文件、参数信息MultipartParsingResult parsingResult = parseRequest(request);return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());}}将 HttpServletRequest 转换成 DefaultMultipartHttpServletRequest 对象
如果开启了延迟解析,则重写该对象的 initializeMultipart() 方法,用于解析请求
否则直接调用 parseRequest(HttpServletRequest request) 方法解析请求,返回 MultipartParsingResult 对象,包含 MultipartFile 对象和普通参数信息
parseRequestparseRequest(HttpServletRequest request)方法,用于解析请求,返回 MultipartParsingResult 对象,包含 MultipartFile 对象、普通参数信息以及参数的 Content-Type 信息,方法如下:
protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {// 获取请求中的编码String encoding = determineEncoding(request);// 获取 ServletFileUpload 对象FileUpload fileUpload = prepareFileUpload(encoding);try {// 获取请求中的流数据List fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);// 将这些流数据转换成 MultipartParsingResult,包含 CommonsMultipartFile、参数信息、Content-typereturn parseFileItems(fileItems, encoding);}catch (FileUploadBase.SizeLimitExceededException ex) {throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);}catch (FileUploadBase.FileSizeLimitExceededException ex) {throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex);}catch (FileUploadException ex) {throw new MultipartException("Failed to parse multipart servlet request", ex);}}获取请求中的编码
根据编码获取到 ServletFileUpload 对象( commons-fileupload 中的类),在 newFileUpload(FileItemFactory fileItemFactory) 方法中返回的就是 ServletFileUpload 对象,可以看到父类 CommonsFileUploadSupport 的构造方法,如下:
// org.springframework.web.multipart.commons.CommonsFileUploadSupport.javapublic CommonsFileUploadSupport() {this.fileItemFactory = newFileItemFactory();// 由子类实现this.fileUpload = newFileUpload(getFileItemFactory());}具体细节就不讲述了
通过 ServletFileUpload 对象解析请求,返回流数据 List fileItems
调用父类 CommonsFileUploadSupport 的 parseFileItems(List fileItems, String encoding) 方法,将这些流数据转换成 MultipartParsingResult 对象
// org.springframework.web.multipart.commons.CommonsFileUploadSupport.javaprotected MultipartParsingResult parseFileItems(List fileItems, String encoding) {MultiValueMap multipartFiles = new LinkedMultiValueMap();Map multipartParameters = new HashMap();Map multipartParameterContentTypes = new HashMap();// Extract multipart files and multipart parameters.for (FileItem fileItem : fileItems) {if (fileItem.isFormField()) {String value;String partEncoding = determineEncoding(fileItem.getContentType(), encoding);try {value = fileItem.getString(partEncoding);}catch (UnsupportedEncodingException ex) {if (logger.isWarnEnabled()) {logger.warn("Could not decode multipart item '" + fileItem.getFieldName() +"' with encoding '" + partEncoding + "': using platform default");}value = fileItem.getString();}String[] curParam = multipartParameters.get(fileItem.getFieldName());if (curParam == null) {// simple form fieldmultipartParameters.put(fileItem.getFieldName(), new String[] {value});}else {// array of simple form fieldsString[] newParam = StringUtils.addStringToArray(curParam, value);multipartParameters.put(fileItem.getFieldName(), newParam);}multipartParameterContentTypes.put(fileItem.getFieldName(), fileItem.getContentType());}else {// multipart file fieldCommonsMultipartFile file = createMultipartFile(fileItem);multipartFiles.add(file.getName(), file);LogFormatUtils.traceDebug(logger, traceOn ->"Part '" + file.getName() + "', size " + file.getSize() +" bytes, filename='" + file.getOriginalFilename() + "'" +(traceOn ? ", storage=" + file.getStorageDescription() : ""));}}return new MultipartParsingResult(multipartFiles, multipartParameters, multipartParameterContentTypes);}大致就是遍历 fileItems 集合,如果是一个简单的表单字段,那么就是一个普通的参数,将参数名和值保存起来
否则就是文件,将其封装成 CommonsMultipartFile 保存起来
cleanupMultipartcleanupMultipart(MultipartHttpServletRequest request)方法,清理文件产生的临时资源,如下:
// CommonsMultipartResolver.java@Overridepublic void cleanupMultipart(MultipartHttpServletRequest request) {if (!(request instanceof AbstractMultipartHttpServletRequest) ||((AbstractMultipartHttpServletRequest) request).isResolved()) {try {cleanupFileItems(request.getMultiFileMap());}catch (Throwable ex) {logger.warn("Failed to perform multipart cleanup for servlet request", ex);}}}// CommonsFileUploadSupport.javaprotected void cleanupFileItems(MultiValueMap multipartFiles) {for (List files : multipartFiles.values()) {for (MultipartFile file : files) {if (file instanceof CommonsMultipartFile) {CommonsMultipartFile cmf = (CommonsMultipartFile) file;cmf.getFileItem().delete();LogFormatUtils.traceDebug(logger, traceOn -> "Cleaning up part '..."));}}}}DefaultMultipartHttpServletRequestorg.springframework.web.multipart.support.DefaultMultipartHttpServletRequest,继承 AbstractMultipartHttpServletRequest 抽象类,MultipartHttpServletRequest 的默认实现类,代码如下:
public class DefaultMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {private static final String CONTENT_TYPE = "Content-Type";@Nullableprivate Map multipartParameters;@Nullableprivate Map multipartParameterContentTypes;public DefaultMultipartHttpServletRequest(HttpServletRequest request, MultiValueMap mpFiles,Map mpParams, Map mpParamContentTypes) {super(request);setMultipartFiles(mpFiles);setMultipartParameters(mpParams);setMultipartParameterContentTypes(mpParamContentTypes);}public DefaultMultipartHttpServletRequest(HttpServletRequest request) {super(request);}@Override@Nullablepublic String getParameter(String name) {String[] values = getMultipartParameters().get(name);if (values != null) {return (values.length > 0 ? values[0] : null);}return super.getParameter(name);}@Overridepublic String[] getParameterValues(String name) {String[] parameterValues = super.getParameterValues(name);String[] mpValues = getMultipartParameters().get(name);if (mpValues == null) {return parameterValues;}if (parameterValues == null || getQueryString() == null) {return mpValues;}else {String[] result = new String[mpValues.length + parameterValues.length];System.arraycopy(mpValues, 0, result, 0, mpValues.length);System.arraycopy(parameterValues, 0, result, mpValues.length, parameterValues.length);return result;}}@Overridepublic Enumeration getParameterNames() {Map multipartParameters = getMultipartParameters();if (multipartParameters.isEmpty()) {return super.getParameterNames();}Set paramNames = new LinkedHashSet();paramNames.addAll(Collections.list(super.getParameterNames()));paramNames.addAll(multipartParameters.keySet());return Collections.enumeration(paramNames);}@Overridepublic Map getParameterMap() {Map result = new LinkedHashMap();Enumeration names = getParameterNames();while (names.hasMoreElements()) {String name = names.nextElement();result.put(name, getParameterValues(name));}return result;}@Overridepublic String getMultipartContentType(String paramOrFileName) {MultipartFile file = getFile(paramOrFileName);if (file != null) {return file.getContentType();}else {return getMultipartParameterContentTypes().get(paramOrFileName);}}@Overridepublic HttpHeaders getMultipartHeaders(String paramOrFileName) {String contentType = getMultipartContentType(paramOrFileName);if (contentType != null) {HttpHeaders headers = new HttpHeaders();headers.add(CONTENT_TYPE, contentType);return headers;}else {return null;}}protected final void setMultipartParameters(Map multipartParameters) {this.multipartParameters = multipartParameters;}protected Map getMultipartParameters() {if (this.multipartParameters == null) {initializeMultipart();}return this.multipartParameters;}protected final void setMultipartParameterContentTypes(Map multipartParameterContentTypes) {this.multipartParameterContentTypes = multipartParameterContentTypes;}protected Map getMultipartParameterContentTypes() {if (this.multipartParameterContentTypes == null) {initializeMultipart();}return this.multipartParameterContentTypes;}}代码并不复杂,稍微阅读一下就理解了😈
总结