平常使用:SpringBoot自定義參數解析器

SpringBoot自定義參數解析器

咱們都知道SpringMvc的Controller的方法上能夠接收各類各樣的參數,好比HttpServletRequestHttpServletResponse,各類註解@RequestParam@RequestHeader@RequestBody@PathVariable@ModelAttribute,這些參數是從哪裏獲取的?java

這些參數都是由不一樣的參數解析器爲咱們解析出來的,能夠解析類也能夠解析帶註解的類
  • 咱們能夠利用解析器解析自定義的參數(類、註解),在咱們須要的傳入的controller方法上傳入它(不須要每次都要在方法內部經過requestresponse等參數作一系列操做來獲取該類對象)

添加解析器

  • 咱們想要自定義解析參數的時候咱們就須要經過更改SpringBoot的配置來添加本身實現的解析類

自定義配置類實現WebMvcConfigurer接口,重寫其中的addArgumentResolvers方法來添加本身的解析類(經過自動注入的方法注入)web

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private SecKillUserArgumentResolvers secKillUserArgumentResolvers;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(secKillUserArgumentResolvers);
    }
}

實現自定義解析器

  • 咱們自定義解析器須要實現HandlerMethodArgumentResolver處理器方法解析器接口,並實現其中的supportsParameterresolveArgument方法

HandlerMethodArgumentResolver的接口定義以下:redis

(1)supportsParameter 用於判斷是否支持對某種參數的解析(支持則返回true)cookie

(2)resolveArgument 將請求中的參數值解析爲某種對象(具體的操做獲取解析對象)app

下面這個自定義解析器用於獲取User對象(經過token獲取保存在redis中的user),無需每次使用resquest和response在controller方法內部獲取,能夠直接獲取到做爲參數傳入ide

/**
 * 自定義參數解析器
 * 解析每次都要獲取的user自動傳入,無需每次獲取
 */
@Component
public class SecKillUserArgumentResolvers implements HandlerMethodArgumentResolver {

    @Autowired
    private UserService userService;

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        Class<?> c = methodParameter.getParameterType();
        return c == User.class;
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
        assert request != null;
        String paramToken = request.getParameter(UserServiceImpl.COOKIE_NAME_TOKEN);//COOKIE_NAME_TOKEN="token"
        String cookieToken = getCookieValue(request);
        if(StringUtils.isEmpty(cookieToken)&&StringUtils.isEmpty(paramToken)){//經過兩種方式獲取,若是都獲取失敗則返回null
            return null;
        }else {
            String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
            return UserService.getUserByToken(response,token);
        }
    }
//遍歷cookie獲取名稱相同的那個cookie的值
    private String getCookieValue(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        for(Cookie cookie:cookies){
            if(cookie.getName().equals(SecKillUserServiceImpl.COOKIE_NAME_TOKEN)){
                return cookie.getValue();
            }
        }
        return null;
    }
}
到此,一個自定義參數解析器就實現好了,咱們能夠經過傳入參數的形式直接經過解析器幫咱們獲取到

解析對象的使用

  • 咱們在controller方法中傳入該參數,能夠直接爲咱們獲取到User對象
@RequestMapping("/to_list")
    public String toList(Model model,User user){
    }

Springboot中其餘參數解析器

Model

  • 咱們知道咱們能夠傳入Model對象參數就能夠直接使用它,咱們看看它的參數解析器

ModelAttributeMethodProcessorui

public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(ModelAttribute.class) || this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType());
    }

    @Nullable
    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
        Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
        String name = ModelFactory.getNameForParameter(parameter);
        ModelAttribute ann = (ModelAttribute)parameter.getParameterAnnotation(ModelAttribute.class);
        if (ann != null) {
            mavContainer.setBinding(name, ann.binding());
        }
        Object attribute = null;
        BindingResult bindingResult = null;
        if (mavContainer.containsAttribute(name)) {
            attribute = mavContainer.getModel().get(name);
        } else {
            try {
                attribute = this.createAttribute(name, parameter, binderFactory, webRequest);
            } catch (BindException var10) {
                if (this.isBindExceptionRequired(parameter)) {
                    throw var10;
                }
                if (parameter.getParameterType() == Optional.class) {
                    attribute = Optional.empty();
                }
                bindingResult = var10.getBindingResult();
            }
        }
        if (bindingResult == null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
            if (binder.getTarget() != null) {
                if (!mavContainer.isBindingDisabled(name)) {
                    this.bindRequestParameters(binder, webRequest);
                }
                this.validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
                    throw new BindException(binder.getBindingResult());
                }
            }
            if (!parameter.getParameterType().isInstance(attribute)) {
                attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
            }
            bindingResult = binder.getBindingResult();
        }
        Map<String, Object> bindingResultModel = bindingResult.getModel();
        mavContainer.removeAttributes(bindingResultModel);
        mavContainer.addAllAttributes(bindingResultModel);
        return attribute;
    }

RequestParam

RequestParamMethodArgumentResolver類,基於註解的方式this

若是傳入參數parameter上有該註解,則能夠解析code

public boolean supportsParameter(MethodParameter parameter) {
        if (parameter.hasParameterAnnotation(RequestParam.class)) {
            if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
                return true;
            } else {
                RequestParam requestParam = (RequestParam)parameter.getParameterAnnotation(RequestParam.class);
                return requestParam != null && StringUtils.hasText(requestParam.name());
            }
        } else if (parameter.hasParameterAnnotation(RequestPart.class)) {
            return false;
        } else {
            parameter = parameter.nestedIfOptional();
            if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
                return true;
            } else {
                return this.useDefaultResolution ? BeanUtils.isSimpleProperty(parameter.getNestedParameterType()) : false;
            }
        }
    }

PathVariable

PathVariableMethodArgumentResolver類,也是基於註解,與RequestParam相似對象

public boolean supportsParameter(MethodParameter parameter) {
        if (!parameter.hasParameterAnnotation(PathVariable.class)) {
            return false;
        } else if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
            return true;
        } else {
            PathVariable pathVariable = (PathVariable)parameter.getParameterAnnotation(PathVariable.class);
            return pathVariable != null && StringUtils.hasText(pathVariable.value());
        }
    }

基於註解自定義參數解析器

  • 編寫註解類,須要解析的類須要有此註解
@Target(value = ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamModel {
}
  • 實現解析器
public class MyArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(ParamModel.class);//帶有註解就解析
    }
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
        //實現解析對象代碼
    }
  • 配置到WebMvcConfigurer
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private MyArgumentResolvers myArgumentResolvers;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(myArgumentResolvers);
    }
}
相關文章
相關標籤/搜索