HandlerMethodArgumentResolver使用和原理

HandlerMethodArgumentResolver 用於將方法參數解析爲參數值的策略接口,我們常說的自定義參數解析器,源碼如下 

/**
 * Strategy interface for resolving method parameters into argument values in
 * the context of a given request.
 *
 * @author Arjen Poutsma
 * @since 3.1
 * @see HandlerMethodReturnValueHandler
 */
public interface HandlerMethodArgumentResolver {

   /**
    * Whether the given {@linkplain MethodParameter method parameter} is
    * supported by this resolver.
    * @param parameter the method parameter to check
    * @return {@code true} if this resolver supports the supplied parameter;
    * {@code false} otherwise 
    */
   boolean supportsParameter(MethodParameter parameter);

   /**
    * Resolves a method parameter into an argument value from a given request.
    * A {@link ModelAndViewContainer} provides access to the model for the
    * request. A {@link WebDataBinderFactory} provides a way to create
    * a {@link WebDataBinder} instance when needed for data binding and
    * type conversion purposes.
    * @param parameter the method parameter to resolve. This parameter must
    * have previously been passed to {@link #supportsParameter} which must
    * have returned {@code true}.
    * @param mavContainer the ModelAndViewContainer for the current request
    * @param webRequest the current request
    * @param binderFactory a factory for creating {@link WebDataBinder} instances
    * @return the resolved argument value, or {@code null} if not resolvable
    * @throws Exception in case of errors with the preparation of argument values
    */
   @Nullable
   Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
         NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

下面是我的一個小例子,主要是通過自定義參數解析器去將分頁參數,提取出來封裝成實體對象

1.首先自定義一個註解

/**
 * @Author: 虞雲波(18088704)
 * @Date: 2020/11/7 10:18
 * @Description:
 */
@Target(ElementType.PARAMETER)
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomArgumentPage {
}

2.寫一個自定義參數解析器實現類

import com.suning.logistics.jwms.on.base.vo.PageBean;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;

/**
 * @Author: 虞雲波(18088704)
 * @Date: 2020/11/7 10:14
 * @Description:
 */
public class CustomArgumentResolvers implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(CustomArgumentPage.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        String page = request.getParameter("page");
        String pageSize = request.getParameter("pageSize");
        PageBean pageBean = new PageBean();
        if (StringUtils.isNotEmpty(page)) {
            pageBean.setCurrentPage(Integer.valueOf(page).intValue());
        }
        if (StringUtils.isNotEmpty(pageSize)) {
            pageBean.setPageSize(Integer.valueOf(pageSize).intValue());
        }
        pageBean.setStartPage((pageBean.getCurrentPage()-1)*pageBean.getPageSize());
        return pageBean;
    }
}

3.將自定義解析器注入到IOC 中

@Bean
public CustomArgumentResolvers initCustomArgumentResolvers(){
    return new CustomArgumentResolvers();
}

4.將自定義的參數解析器,注入到MvcConfigure中

@Configuration
public class WebMvcCustomConfigurer implements WebMvcConfigurer {


    @Autowired
    private CustomArgumentResolvers customArgumentResolvers;

    /**
     * Add resolvers to support custom controller method argument types.
     * <p>This does not override the built-in support for resolving handler
     * method arguments. To customize the built-in support for argument
     * resolution, configure {@link RequestMappingHandlerAdapter} directly.
     *
     * @param resolvers initially an empty list
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(customArgumentResolvers);
    }
}

5.編寫測試Controller 

@RequestMapping("/queryPageConfigList")
@ResponseBody
public Object getWarehouseCodeReadyonly(@CustomArgumentPage PageBean pageBean){
    return warehouseConfigService.queryPage(pageBean,null);
}

6.效果如下

下面源碼分析一波,講述一下整個流程,

1.在Springboot  AutoConfiguration 會自動注入  WebMvcAutoConfiguration

2.在WebMvcAutoConfiguration 中 靜態內部類 EnableWebMvcConfiguration它繼承了DelegatingWebMvcConfiguration這個類,這個很重要,中的  requestMappingHandlerAdapter方法,初始化HandlerAdapter 

super.requestMappingHandlerAdapter()

3.在父類WebMvcConfigurationSupport 中,如下圖 565行代碼,會給HandlerAdapter 設置自定義的參數解析器

4.進入getArgumentResolvers方法,核心方法  addArgumentResolvers

5.是個空方法???,源碼跟蹤是進入了其子類 DelegatingWebMvcConfiguration 這個類中

6.繼續代碼跟蹤下一步,會到WebMvcConfigurerComposite 中addArgumentResolvers 方法,會循環WebMvcConfigurer List 調用裏面的 addArgumentResolvers 至於這個this.delegates 怎麼初始化的,你們可以自己研究一下

7.最終調到我們自定義的WebMvcCustomConfigurer 中,將我們自定義的參數解析器,添加到整個參數解析List中

8.將數參數解析List 設置到HandlerAdapter 中

9.調用的時候,整個調用鏈如下圖,剛興趣的同學可以自己去分析一下

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章