主要内容

主要从四个方面来解释springmvc是如何注入的。

  1. 文件的注入
  2. body的注入
  3. param的注入
  4. path variable的注入

其次,总结在日常开发中的这里可以有哪些规范来提升自己的研发效率。

源码分析

  1. org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

    1. /**
    2. * 获取当前方法参数的值
    3. * Get the method argument values for the current request, checking the provided
    4. * argument values and falling back to the configured argument resolvers.
    5. * <p>The resulting array will be passed into {@link #doInvoke}.
    6. * @since 5.1.2
    7. */
    8. protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
    9. Object... providedArgs) throws Exception {
    10. MethodParameter[] parameters = getMethodParameters();
    11. if (ObjectUtils.isEmpty(parameters)) {
    12. return EMPTY_ARGS;
    13. }
    14. //遍历解决每个参数(优先级、重复等问题)
    15. Object[] args = new Object[parameters.length];
    16. for (int i = 0; i < parameters.length; i++) {
    17. MethodParameter parameter = parameters[i];
    18. parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
    19. args[i] = findProvidedArgument(parameter, providedArgs);
    20. if (args[i] != null) {
    21. continue;
    22. }
    23. if (!this.resolvers.supportsParameter(parameter)) {
    24. throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
    25. }
    26. try {
    27. args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
    28. }
    29. catch (Exception ex) {
    30. // Leave stack trace for later, exception may actually be resolved and handled...
    31. if (logger.isDebugEnabled()) {
    32. String exMsg = ex.getMessage();
    33. if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
    34. logger.debug(formatArgumentError(parameter, exMsg));
    35. }
    36. }
    37. throw ex;
    38. }
    39. }
    40. return args;
    41. }

    核心模型:
    MethodParameter:方法参数的抽象
    HandlerMethodArgumentResolver:核心用来解决参数的转换和注入的问题,同理这里用了HandlerMethodArgumentResolverComposite来解决HandlerMethodArgumentResolver的管理问题,同时提供缓存匹配结果。这里resolver有很多个,可以具体举几个例子来处理。

1. RequestParamMethodArgumentResolver

  1. @Override
  2. @Nullable
  3. protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
  4. HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
  5. if (servletRequest != null) {
  6. Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
  7. if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
  8. return mpArg;
  9. }
  10. }
  11. //解决MultipartFile的注入
  12. Object arg = null;
  13. MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
  14. if (multipartRequest != null) {
  15. List<MultipartFile> files = multipartRequest.getFiles(name);
  16. if (!files.isEmpty()) {
  17. arg = (files.size() == 1 ? files.get(0) : files);
  18. }
  19. }
  20. if (arg == null) {
  21. String[] paramValues = request.getParameterValues(name);
  22. if (paramValues != null) {
  23. arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
  24. }
  25. }
  26. return arg;
  27. }

�在解决mulitpartFile的时候,以下这段代码很重要

  1. //参数名称会跟文件名匹配,才能数据一样;那么这个add的默认文件名是怎么设置的呢?
  2. if (MultipartFile.class == parameter.getNestedParameterType()) {
  3. if (multipartRequest == null && isMultipart) {
  4. multipartRequest = new StandardMultipartHttpServletRequest(request);
  5. }
  6. return (multipartRequest != null ? multipartRequest.getFile(name) : null);
  7. }

参数名称会跟文件名匹配,才能数据一样;那么这个add的默认文件名是怎么设置的呢? 在mock场景下,默认文件名就是自己new MockMultipartFile(fileName)

异常场景

  1. 文件上传的时候,报空指针异常。

编程规范