主要内容
主要从四个方面来解释springmvc是如何注入的。
- 文件的注入
- body的注入
- param的注入
- path variable的注入
其次,总结在日常开发中的这里可以有哪些规范来提升自己的研发效率。
源码分析
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
/**
* 获取当前方法参数的值
* Get the method argument values for the current request, checking the provided
* argument values and falling back to the configured argument resolvers.
* <p>The resulting array will be passed into {@link #doInvoke}.
* @since 5.1.2
*/
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;
}
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
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;
}
核心模型:
MethodParameter:方法参数的抽象
HandlerMethodArgumentResolver:核心用来解决参数的转换和注入的问题,同理这里用了HandlerMethodArgumentResolverComposite来解决HandlerMethodArgumentResolver的管理问题,同时提供缓存匹配结果。这里resolver有很多个,可以具体举几个例子来处理。
1. RequestParamMethodArgumentResolver
@Override
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if (servletRequest != null) {
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
}
//解决MultipartFile的注入
Object arg = null;
MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
if (arg == null) {
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
�在解决mulitpartFile的时候,以下这段代码很重要
//参数名称会跟文件名匹配,才能数据一样;那么这个add的默认文件名是怎么设置的呢?
if (MultipartFile.class == parameter.getNestedParameterType()) {
if (multipartRequest == null && isMultipart) {
multipartRequest = new StandardMultipartHttpServletRequest(request);
}
return (multipartRequest != null ? multipartRequest.getFile(name) : null);
}
参数名称会跟文件名匹配,才能数据一样;那么这个add的默认文件名是怎么设置的呢? 在mock场景下,默认文件名就是自己new MockMultipartFile(fileName)
�
异常场景
- 文件上传的时候,报空指针异常。
编程规范
�