spring boot validator 源码分析

spring boot validator 源码分析

1: spring boot请求入口方法是org.springframework.web.servlet.DispatcherServlet#doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
            .....

          // Actually invoke the handler.
          mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         ......
       catch (Exception ex) {
          dispatchException = ex;
       }
       catch (Throwable err) {
          // As of 4.3, we're processing Errors thrown from handler methods as well,
          // making them available for @ExceptionHandler methods and other scenarios.
          dispatchException = new ServletException("Handler dispatch failed: " + err, err);
       }
       processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
       triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
       triggerAfterCompletion(processedRequest, response, mappedHandler,
             new ServletException("Handler processing failed: " + err, err));
    }
    finally {
       if (asyncManager.isConcurrentHandlingStarted()) {
          // Instead of postHandle and afterCompletion
          if (mappedHandler != null) {
             mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
          }
       }
       else {
          // Clean up any resources used by a multipart request.
          if (multipartRequestParsed) {
             cleanupMultipart(processedRequest);
          }
       }
    }
}

2:在doDispatch方法中

ha.handle(processedRequest, response, mappedHandler.getHandler())调用 org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handleInternal 调用

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal 调用

protected ModelAndView handleInternal(HttpServletRequest request,
       HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  
    .....

   // No synchronization on session demanded at all...
       mav = invokeHandlerMethod(request, response, handlerMethod);
        ....
    return mav;
}

方法中调用

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod 调用

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
       HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

  .....

    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    if (this.argumentResolvers != null) {
       invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    if (this.returnValueHandlers != null) {
       invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }
    invocableMethod.setDataBinderFactory(binderFactory);
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    invocableMethod.setMethodValidator(this.methodValidator);

    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

  ....

    invocableMethod.invokeAndHandle(webRequest, mavContainer);
   ...

    return getModelAndView(mavContainer, modelFactory, webRequest);
}
WX20240424-165639@2x.png

这里请记住org.springframework.validation.beanvalidation.LocalValidatorFactoryBean

org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle调用

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
       Object... providedArgs) throws Exception {

    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    .....
}

org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest 调用

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
       Object... providedArgs) throws Exception {

    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
   .....

    return returnValue;
}

org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues调用

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
       Object... providedArgs) throws Exception {

        .......
       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;
}

org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument调用

2.png

从调试图片来看,是调用org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument方法

@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
       NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

  .....

    if (binderFactory != null) {
       String name = Conventions.getVariableNameForParameter(parameter);
       ResolvableType type = ResolvableType.forMethodParameter(parameter);
       WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name, type);
       if (arg != null) {
          validateIfApplicable(binder, parameter);
          if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
             throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
          }
       }
     .......
    }

    return adaptArgumentIfNecessary(arg, parameter);
}
3.png

从上面截图来看,validators 指向是org.springframework.validation.beanvalidation.LocalValidatorFactoryBean

2:分析LocalValidatorFactoryBean初始化和使用

4.png

LocalValidatorFactoryBean实现InitializingBean接口,我们只需要查看afterPropertiesSet方法。

@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public void afterPropertiesSet() {
    Configuration<?> configuration;
  .....
    else {
       GenericBootstrap bootstrap = Validation.byDefaultProvider();
       if (this.validationProviderResolver != null) {
          bootstrap = bootstrap.providerResolver(this.validationProviderResolver);
       }
       configuration = bootstrap.configure();
    }

   
    MessageInterpolator targetInterpolator = this.messageInterpolator;
    if (targetInterpolator == null) {
       targetInterpolator = configuration.getDefaultMessageInterpolator();
    }
    configuration.messageInterpolator(new LocaleContextMessageInterpolator(targetInterpolator));

     .....

    ConstraintValidatorFactory targetConstraintValidatorFactory = this.constraintValidatorFactory;
    ...

    try {
       this.validatorFactory = configuration.buildValidatorFactory();
       setTargetValidator(this.validatorFactory.getValidator());
    }
    finally {
       closeMappingStreams(mappingStreams);
    }
}

核心代码是上面 GenericBootstrap LocaleContextMessageInterpolator validatorFactory 类,我们可以通过单元测试来验证

public abstract class BaseTest {

    private Validator validator;


    @BeforeEach
    public void init(){
        GenericBootstrap bootstrap = Validation.byDefaultProvider();

        Configuration<?>  configuration = bootstrap.configure();

        MessageInterpolator   targetInterpolator = configuration.getDefaultMessageInterpolator();

        configuration.messageInterpolator(new LocaleContextMessageInterpolator(targetInterpolator));

        ValidatorFactory validatorFactory = configuration.buildValidatorFactory();
        this.validator = validatorFactory.getValidator();
    }

    protected  <T>Set<ConstraintViolation<T>> validate(T object,Class<?>... groups){
        return validator.validate(object,groups);
    }
}
public class UserTest extends BaseTest {

    @Test
    public void testValidateUser(){
        User user = new User();

        Set<ConstraintViolation<User>> constraintViolations =validate(user);

        constraintViolations.forEach(cv-> System.out.println(cv.getMessage()));
    }
}

github地址 https://github.com/knowledgeAlan/trail-excise-demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容