• Spring mvc解析


    方案时间 ,写代码时间 ,解决技术难点时间 , 自测时间,解决bug时间 , 联调时间 ,数据库优化,代码走查
    1个接口:2个小时


    把那个字段再复原回来,不然兼容性不强
    还有一个刷数据的接口

    public static void main(String[] args) throws ParseException {
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'Z", Locale.US);
    String format = simpleDateFormat.format(new Date());
    System.out.println(format);
    // Date parse = simpleDateFormat.parse("Wed Dec 12 2018 00:00:00 GMT 0800 (中国标准时间)");
    // System.out.println(parse);

    String time = "1543852800000";
    Date date = new Date(Long.decode(time));
    System.out.println(date);
    }


    // binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"), true));
    // binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'Z"), true));
    queryString中匹配各种时间格式


    Wed Dec 12 2018 00:00:00 GMT 0800 (中国标准时间)
    EEE MMM dd yyyy HH:mm:ss 'GMT'Z


    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)

    不是来商量的,而是来让我接受你的意见的

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")


    public static void main(String[] args) throws ParseException {

    // Date parse = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").parse("2018-12-06T16:00:00.000Z");
    // System.out.println(parse);

    // SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS Z");
    SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

    Date parse1 = inputFormat.parse("2018-12-06T16:00:00.000Z");
    System.out.println(parse1);
    }
    https://stackoverflow.com/questions/49752149/how-do-i-convert-2018-04-10t040000-000z-string-to-datetime


    // Actually invoke the handler.
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    org.springframework.web.servlet.HandlerAdapter#handle

    /**
    * This implementation expects the handler to be an {@link HandlerMethod}.
    */
    @Override
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {

    return handleInternal(request, response, (HandlerMethod) handler);
    }
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle【org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter】


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

    ModelAndView mav;
    checkRequest(request);

    // Execute invokeHandlerMethod in synchronized block if required.
    if (this.synchronizeOnSession) {
    HttpSession session = request.getSession(false);
    if (session != null) {
    Object mutex = WebUtils.getSessionMutex(session);
    synchronized (mutex) {
    mav = invokeHandlerMethod(request, response, handlerMethod);
    }
    }
    else {
    // No HttpSession available -> no mutex necessary
    mav = invokeHandlerMethod(request, response, handlerMethod);
    }
    }
    else {
    // No synchronization on session demanded at all...
    mav = invokeHandlerMethod(request, response, handlerMethod); 【调用的是这一行】
    }

    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
    applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
    }
    else {
    prepareResponse(response);
    }
    }

    return mav;
    }
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal【org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter】


    /**
    * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
    * if view resolution is required.
    * @since 4.2
    * @see #createInvocableHandlerMethod(HandlerMethod)
    */
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    invocableMethod.setDataBinderFactory(binderFactory);
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

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

    AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
    asyncWebRequest.setTimeout(this.asyncRequestTimeout);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.setTaskExecutor(this.taskExecutor);
    asyncManager.setAsyncWebRequest(asyncWebRequest);
    asyncManager.registerCallableInterceptors(this.callableInterceptors);
    asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

    if (asyncManager.hasConcurrentResult()) {
    Object result = asyncManager.getConcurrentResult();
    mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
    asyncManager.clearConcurrentResult();
    if (logger.isDebugEnabled()) {
    logger.debug("Found concurrent result value [" + result + "]");
    }
    invocableMethod = invocableMethod.wrapConcurrentResult(result);
    }

    invocableMethod.invokeAndHandle(webRequest, mavContainer);【这一行】
    if (asyncManager.isConcurrentHandlingStarted()) {
    return null;
    }

    return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
    webRequest.requestCompleted();
    }
    }
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod【org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter】


    /**
    * Invoke the method and handle the return value through one of the
    * configured {@link HandlerMethodReturnValueHandler}s.
    * @param webRequest the current request
    * @param mavContainer the ModelAndViewContainer for this request
    * @param providedArgs "given" arguments matched by type (not resolved)
    */
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {

    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); 【这一行】
    setResponseStatus(webRequest);

    if (returnValue == null) {
    if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
    mavContainer.setRequestHandled(true);
    return;
    }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) {
    mavContainer.setRequestHandled(true);
    return;
    }

    mavContainer.setRequestHandled(false);
    try {
    this.returnValueHandlers.handleReturnValue(
    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {
    if (logger.isTraceEnabled()) {
    logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
    }
    throw ex;
    }
    }
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle【org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod】


    /**
    * Invoke the method after resolving its argument values in the context of the given request.
    * <p>Argument values are commonly resolved through {@link HandlerMethodArgumentResolver}s.
    * The {@code providedArgs} parameter however may supply argument values to be used directly,
    * i.e. without argument resolution. Examples of provided argument values include a
    * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
    * Provided argument values are checked before argument resolvers.
    * @param request the current request
    * @param mavContainer the ModelAndViewContainer for this request
    * @param providedArgs "given" arguments matched by type, not resolved
    * @return the raw value returned by the invoked method
    * @exception Exception raised if no suitable argument resolver can be found,
    * or if the method raised an exception
    */
    public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {

    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
    logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
    "' with arguments " + Arrays.toString(args));
    }
    Object returnValue = doInvoke(args);
    if (logger.isTraceEnabled()) {
    logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
    "] returned [" + returnValue + "]");
    }
    return returnValue;
    }
    org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest【org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod】


    /**
    * Get the method argument values for the current request.
    */
    private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {

    MethodParameter[] parameters = getMethodParameters();
    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
    MethodParameter parameter = parameters[i];
    parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
    args[i] = resolveProvidedArgument(parameter, providedArgs);
    if (args[i] != null) {
    continue;
    }
    if (this.argumentResolvers.supportsParameter(parameter)) {
    try {
    args[i] = this.argumentResolvers.resolveArgument(
    parameter, mavContainer, request, this.dataBinderFactory);【这一行】
    continue;
    }
    catch (Exception ex) {
    if (logger.isDebugEnabled()) {
    logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
    }
    throw ex;
    }
    }
    if (args[i] == null) {
    throw new IllegalStateException("Could not resolve method parameter at index " +
    parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
    ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
    }
    }
    return args;
    }
    org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues【org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod】

    /**
    * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
    * @throws IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
    */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
    throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
    }
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
    org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument【org.springframework.web.method.support.HandlerMethodArgumentResolverComposite】

    /**
    * Resolve the argument from the model or if not found instantiate it with
    * its default if it is available. The model attribute is then populated
    * with request values via data binding and optionally validated
    * if {@code @java.validation.Valid} is present on the argument.
    * @throws BindException if data binding and validation result in an error
    * and the next method parameter is not of type {@link Errors}.
    * @throws Exception if WebDataBinder initialization fails.
    */
    @Override
    public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

    String name = ModelFactory.getNameForParameter(parameter);
    Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) :
    createAttribute(name, parameter, binderFactory, webRequest));

    if (!mavContainer.isBindingDisabled(name)) {
    ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
    if (ann != null && !ann.binding()) {
    mavContainer.setBindingDisabled(name);
    }
    }

    WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
    if (binder.getTarget() != null) {
    if (!mavContainer.isBindingDisabled(name)) {
    bindRequestParameters(binder, webRequest); 【这一行】
    }
    validateIfApplicable(binder, parameter);
    if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
    throw new BindException(binder.getBindingResult());
    }
    }

    // Add resolved attribute and BindingResult at the end of the model
    Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
    mavContainer.removeAttributes(bindingResultModel);
    mavContainer.addAllAttributes(bindingResultModel);

    return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
    }
    org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument【org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor】

    /**
    * This implementation downcasts {@link WebDataBinder} to
    * {@link ServletRequestDataBinder} before binding.
    * @see ServletRequestDataBinderFactory
    */
    @Override
    protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
    ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
    ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
    servletBinder.bind(servletRequest);
    }
    org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#bindRequestParameters【org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor】


    /**
    * Bind the parameters of the given request to this binder's target,
    * also binding multipart files in case of a multipart request.
    * <p>This call can create field errors, representing basic binding
    * errors like a required field (code "required"), or type mismatch
    * between value and bean property (code "typeMismatch").
    * <p>Multipart files are bound via their parameter name, just like normal
    * HTTP parameters: i.e. "uploadedFile" to an "uploadedFile" bean property,
    * invoking a "setUploadedFile" setter method.
    * <p>The type of the target property for a multipart file can be MultipartFile,
    * byte[], or String. The latter two receive the contents of the uploaded file;
    * all metadata like original file name, content type, etc are lost in those cases.
    * @param request request with parameters to bind (can be multipart)
    * @see org.springframework.web.multipart.MultipartHttpServletRequest
    * @see org.springframework.web.multipart.MultipartFile
    * @see #bind(org.springframework.beans.PropertyValues)
    */
    public void bind(ServletRequest request) {
    MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
    MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
    if (multipartRequest != null) {
    bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
    }
    addBindValues(mpvs, request);
    doBind(mpvs);
    }
    org.springframework.web.bind.ServletRequestDataBinder#bind【org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder】

    /**
    * This implementation performs a field default and marker check
    * before delegating to the superclass binding process.
    * @see #checkFieldDefaults
    * @see #checkFieldMarkers
    */
    @Override
    protected void doBind(MutablePropertyValues mpvs) {
    checkFieldDefaults(mpvs);
    checkFieldMarkers(mpvs);
    super.doBind(mpvs);
    }
    org.springframework.web.bind.WebDataBinder#doBind【org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder】


    /**
    * Actual implementation of the binding process, working with the
    * passed-in MutablePropertyValues instance.
    * @param mpvs the property values to bind,
    * as MutablePropertyValues instance
    * @see #checkAllowedFields
    * @see #checkRequiredFields
    * @see #applyPropertyValues
    */
    protected void doBind(MutablePropertyValues mpvs) {
    checkAllowedFields(mpvs);
    checkRequiredFields(mpvs);
    applyPropertyValues(mpvs);
    }
    org.springframework.validation.DataBinder#doBind【org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder】

    /**
    * Apply given property values to the target object.
    * <p>Default implementation applies all of the supplied property
    * values as bean property values. By default, unknown fields will
    * be ignored.
    * @param mpvs the property values to be bound (can be modified)
    * @see #getTarget
    * @see #getPropertyAccessor
    * @see #isIgnoreUnknownFields
    * @see #getBindingErrorProcessor
    * @see BindingErrorProcessor#processPropertyAccessException
    */
    protected void applyPropertyValues(MutablePropertyValues mpvs) {
    try {
    // Bind request parameters onto target object.
    getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields()); 【这一行】
    }
    catch (PropertyBatchUpdateException ex) {
    // Use bind error processor to create FieldErrors.
    for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
    getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
    }
    }
    }
    org.springframework.validation.DataBinder#applyPropertyValues【org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder】


    /**
    * Perform a batch update with full control over behavior.
    * <p>Note that performing a batch update differs from performing a single update,
    * in that an implementation of this class will continue to update properties
    * if a <b>recoverable</b> error (such as a type mismatch, but <b>not</b> an
    * invalid field name or the like) is encountered, throwing a
    * {@link PropertyBatchUpdateException} containing all the individual errors.
    * This exception can be examined later to see all binding errors.
    * Properties that were successfully updated remain changed.
    * @param pvs PropertyValues to set on the target object
    * @param ignoreUnknown should we ignore unknown properties (not found in the bean)
    * @param ignoreInvalid should we ignore invalid properties (found but not accessible)
    * @throws InvalidPropertyException if there is no such property or
    * if the property isn't writable
    * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions
    * occurred for specific properties during the batch update. This exception bundles
    * all individual PropertyAccessExceptions. All other properties will have been
    * successfully updated.
    */
    @Override
    public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
    throws BeansException {

    List<PropertyAccessException> propertyAccessExceptions = null;
    List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
    ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
    for (PropertyValue pv : propertyValues) {
    try {
    // This method may throw any BeansException, which won't be caught
    // here, if there is a critical failure such as no matching field.
    // We can attempt to deal only with less serious exceptions.
    setPropertyValue(pv); 【这一行】
    }
    catch (NotWritablePropertyException ex) {
    if (!ignoreUnknown) {
    throw ex;
    }
    // Otherwise, just ignore it and continue...
    }
    catch (NullValueInNestedPathException ex) {
    if (!ignoreInvalid) {
    throw ex;
    }
    // Otherwise, just ignore it and continue...
    }
    catch (PropertyAccessException ex) {
    if (propertyAccessExceptions == null) {
    propertyAccessExceptions = new LinkedList<PropertyAccessException>();
    }
    propertyAccessExceptions.add(ex);
    }
    }

    // If we encountered individual exceptions, throw the composite exception.
    if (propertyAccessExceptions != null) {
    PropertyAccessException[] paeArray =
    propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
    throw new PropertyBatchUpdateException(paeArray);
    }
    }
    org.springframework.beans.AbstractPropertyAccessor#setPropertyValues(org.springframework.beans.PropertyValues, boolean, boolean)【org.springframework.beans.BeanWrapperImpl】

    /**
    * Set the specified value as current property value.
    * @param pv an object containing the new property value
    * @throws InvalidPropertyException if there is no such property or
    * if the property isn't writable
    * @throws PropertyAccessException if the property was valid but the
    * accessor method failed or a type mismatch occurred
    */
    @Override
    public void setPropertyValue(PropertyValue pv) throws BeansException {
    PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
    if (tokens == null) {
    String propertyName = pv.getName();
    AbstractNestablePropertyAccessor nestedPa;
    try {
    nestedPa = getPropertyAccessorForPropertyPath(propertyName);
    }
    catch (NotReadablePropertyException ex) {
    throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
    "Nested property in path '" + propertyName + "' does not exist", ex);
    }
    tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
    if (nestedPa == this) {
    pv.getOriginalPropertyValue().resolvedTokens = tokens;
    }
    nestedPa.setPropertyValue(tokens, pv);
    }
    else {
    setPropertyValue(tokens, pv);
    }
    }
    org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue(org.springframework.beans.PropertyValue)【org.springframework.beans.BeanWrapperImpl】


    /**
    * Set the specified value as current property value.
    * @param pv an object containing the new property value
    * @throws InvalidPropertyException if there is no such property or
    * if the property isn't writable
    * @throws PropertyAccessException if the property was valid but the
    * accessor method failed or a type mismatch occurred
    */
    @Override
    public void setPropertyValue(PropertyValue pv) throws BeansException {
    PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
    if (tokens == null) {
    String propertyName = pv.getName();
    AbstractNestablePropertyAccessor nestedPa;
    try {
    nestedPa = getPropertyAccessorForPropertyPath(propertyName);
    }
    catch (NotReadablePropertyException ex) {
    throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
    "Nested property in path '" + propertyName + "' does not exist", ex);
    }
    tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
    if (nestedPa == this) {
    pv.getOriginalPropertyValue().resolvedTokens = tokens;
    }
    nestedPa.setPropertyValue(tokens, pv);【这一行】
    }
    else {
    setPropertyValue(tokens, pv);
    }
    }
    org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue(org.springframework.beans.PropertyValue)【org.springframework.beans.BeanWrapperImpl】


    protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
    if (tokens.keys != null) {
    processKeyedProperty(tokens, pv);
    }
    else {
    processLocalProperty(tokens, pv); 【这一行】
    }
    }
    org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue(org.springframework.beans.AbstractNestablePropertyAccessor.PropertyTokenHolder, org.springframework.beans.PropertyValue)【org.springframework.beans.BeanWrapperImpl】

    private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
    PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
    if (ph == null || !ph.isWritable()) {
    if (pv.isOptional()) {
    if (logger.isDebugEnabled()) {
    logger.debug("Ignoring optional value for property '" + tokens.actualName +
    "' - property not found on bean class [" + getRootClass().getName() + "]");
    }
    return;
    }
    else {
    throw createNotWritablePropertyException(tokens.canonicalName);
    }
    }

    Object oldValue = null;
    try {
    Object originalValue = pv.getValue();
    Object valueToApply = originalValue;
    if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
    if (pv.isConverted()) {
    valueToApply = pv.getConvertedValue();
    }
    else {
    if (isExtractOldValueForEditor() && ph.isReadable()) {
    try {
    oldValue = ph.getValue();
    }
    catch (Exception ex) {
    if (ex instanceof PrivilegedActionException) {
    ex = ((PrivilegedActionException) ex).getException();
    }
    if (logger.isDebugEnabled()) {
    logger.debug("Could not read previous value of property '" +
    this.nestedPath + tokens.canonicalName + "'", ex);
    }
    }
    }
    valueToApply = convertForProperty(
    tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());【这一行】
    }
    pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
    }
    ph.setValue(this.wrappedObject, valueToApply);
    }
    catch (TypeMismatchException ex) {
    throw ex;
    }
    catch (InvocationTargetException ex) {
    PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
    this.rootObject, this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
    if (ex.getTargetException() instanceof ClassCastException) {
    throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());
    }
    else {
    Throwable cause = ex.getTargetException();
    if (cause instanceof UndeclaredThrowableException) {
    // May happen e.g. with Groovy-generated methods
    cause = cause.getCause();
    }
    throw new MethodInvocationException(propertyChangeEvent, cause);
    }
    }
    catch (Exception ex) {
    PropertyChangeEvent pce = new PropertyChangeEvent(
    this.rootObject, this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
    throw new MethodInvocationException(pce, ex);
    }
    }
    org.springframework.beans.AbstractNestablePropertyAccessor#processLocalProperty 【org.springframework.beans.BeanWrapperImpl】


    protected Object convertForProperty(String propertyName, Object oldValue, Object newValue, TypeDescriptor td)
    throws TypeMismatchException {

    return convertIfNecessary(propertyName, oldValue, newValue, td.getType(), td);
    }
    org.springframework.beans.AbstractNestablePropertyAccessor#convertForProperty【org.springframework.beans.BeanWrapperImpl】


    private Object convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class<?> requiredType,
    TypeDescriptor td) throws TypeMismatchException {
    try {
    return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
    }
    catch (ConverterNotFoundException ex) {
    PropertyChangeEvent pce =
    new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
    throw new ConversionNotSupportedException(pce, td.getType(), ex);
    }
    catch (ConversionException ex) {
    PropertyChangeEvent pce =
    new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
    throw new TypeMismatchException(pce, requiredType, ex);
    }
    catch (IllegalStateException ex) {
    PropertyChangeEvent pce =
    new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
    throw new ConversionNotSupportedException(pce, requiredType, ex);
    }
    catch (IllegalArgumentException ex) {
    PropertyChangeEvent pce =
    new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
    throw new TypeMismatchException(pce, requiredType, ex);
    }
    }
    org.springframework.beans.AbstractNestablePropertyAccessor#convertIfNecessary【org.springframework.beans.BeanWrapperImpl】


    /**
    * Convert the value to the required type (if necessary from a String),
    * for the specified property.
    * @param propertyName name of the property
    * @param oldValue the previous value, if available (may be {@code null})
    * @param newValue the proposed new value
    * @param requiredType the type we must convert to
    * (or {@code null} if not known, for example in case of a collection element)
    * @param typeDescriptor the descriptor for the target property or field
    * @return the new value, possibly the result of type conversion
    * @throws IllegalArgumentException if type conversion failed
    */
    @SuppressWarnings("unchecked")
    public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
    Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {

    // Custom editor for this type?
    PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName); 【这一行】

    ConversionFailedException conversionAttemptEx = null;

    // No custom editor but custom ConversionService specified?
    ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
    if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
    TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
    if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
    try {
    return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
    }
    catch (ConversionFailedException ex) {
    // fallback to default conversion logic below
    conversionAttemptEx = ex;
    }
    }
    }

    Object convertedValue = newValue;

    // Value not of required type?
    if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
    if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
    convertedValue instanceof String) {
    TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
    if (elementTypeDesc != null) {
    Class<?> elementType = elementTypeDesc.getType();
    if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
    convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
    }
    }
    }
    if (editor == null) {
    editor = findDefaultEditor(requiredType);
    }
    convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
    }

    boolean standardConversion = false;

    if (requiredType != null) {
    // Try to apply some standard type conversion rules if appropriate.

    if (convertedValue != null) {
    if (Object.class == requiredType) {
    return (T) convertedValue;
    }
    else if (requiredType.isArray()) {
    // Array required -> apply appropriate conversion of elements.
    if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
    convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
    }
    return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
    }
    else if (convertedValue instanceof Collection) {
    // Convert elements to target type, if determined.
    convertedValue = convertToTypedCollection(
    (Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
    standardConversion = true;
    }
    else if (convertedValue instanceof Map) {
    // Convert keys and values to respective target type, if determined.
    convertedValue = convertToTypedMap(
    (Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
    standardConversion = true;
    }
    if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
    convertedValue = Array.get(convertedValue, 0);
    standardConversion = true;
    }
    if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
    // We can stringify any primitive value...
    return (T) convertedValue.toString();
    }
    else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
    if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
    try {
    Constructor<T> strCtor = requiredType.getConstructor(String.class);
    return BeanUtils.instantiateClass(strCtor, convertedValue);
    }
    catch (NoSuchMethodException ex) {
    // proceed with field lookup
    if (logger.isTraceEnabled()) {
    logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
    }
    }
    catch (Exception ex) {
    if (logger.isDebugEnabled()) {
    logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
    }
    }
    }
    String trimmedValue = ((String) convertedValue).trim();
    if (requiredType.isEnum() && "".equals(trimmedValue)) {
    // It's an empty enum identifier: reset the enum value to null.
    return null;
    }
    convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
    standardConversion = true;
    }
    else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
    convertedValue = NumberUtils.convertNumberToTargetClass(
    (Number) convertedValue, (Class<Number>) requiredType);
    standardConversion = true;
    }
    }
    else {
    // convertedValue == null
    if (javaUtilOptionalEmpty != null && requiredType == javaUtilOptionalEmpty.getClass()) {
    convertedValue = javaUtilOptionalEmpty;
    }
    }

    if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
    if (conversionAttemptEx != null) {
    // Original exception from former ConversionService call above...
    throw conversionAttemptEx;
    }
    else if (conversionService != null) {
    // ConversionService not tried before, probably custom editor found
    // but editor couldn't produce the required type...
    TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
    if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
    return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
    }
    }

    // Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
    StringBuilder msg = new StringBuilder();
    msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
    msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
    if (propertyName != null) {
    msg.append(" for property '").append(propertyName).append("'");
    }
    if (editor != null) {
    msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
    "] returned inappropriate value of type '").append(
    ClassUtils.getDescriptiveType(convertedValue)).append("'");
    throw new IllegalArgumentException(msg.toString());
    }
    else {
    msg.append(": no matching editors or conversion strategy found");
    throw new IllegalStateException(msg.toString());
    }
    }
    }

    if (conversionAttemptEx != null) {
    if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
    throw conversionAttemptEx;
    }
    logger.debug("Original ConversionService attempt failed - ignored since " +
    "PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
    }

    return (T) convertedValue;
    }
    org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class<T>, org.springframework.core.convert.TypeDescriptor)【org.springframework.beans.TypeConverterDelegate】


    /**
    * Find a custom property editor for the given type and property.
    * @param requiredType the type of the property (can be {@code null} if a property
    * is given but should be specified in any case for consistency checking)
    * @param propertyPath the path of the property (name or nested path), or
    * {@code null} if looking for an editor for all properties of the given type
    * @return the registered editor, or {@code null} if none
    */
    @Override
    public PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath) {
    Class<?> requiredTypeToUse = requiredType;
    if (propertyPath != null) {
    if (this.customEditorsForPath != null) {
    // Check property-specific editor first.
    PropertyEditor editor = getCustomEditor(propertyPath, requiredType);
    if (editor == null) {
    List<String> strippedPaths = new LinkedList<String>();
    addStrippedPropertyPaths(strippedPaths, "", propertyPath);
    for (Iterator<String> it = strippedPaths.iterator(); it.hasNext() && editor == null;) {
    String strippedPath = it.next();
    editor = getCustomEditor(strippedPath, requiredType);
    }
    }
    if (editor != null) {
    return editor;
    }
    }
    if (requiredType == null) {
    requiredTypeToUse = getPropertyType(propertyPath);
    }
    }
    // No property-specific editor -> check type-specific editor.
    return getCustomEditor(requiredTypeToUse);
    }
    org.springframework.beans.PropertyEditorRegistrySupport#findCustomEditor【org.springframework.beans.BeanWrapperImpl】 【关键点】

    Accept : ProducesRequest
    Content-Type : ConsumesRequest

    /**
    * 确认下默认的ContentType是否是application/json
    */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer
    .defaultContentType(MediaType.APPLICATION_JSON)
    .favorPathExtension(true);
    }

    SpringMVC 4.2之后:
    Spring MVC提供了一个特殊的ViewResolver,ContentNegotiatingViewResolver,它不是自己处理View,而是代理给不同的ViewResolver来处理不同的View,所以它有最高的优先级。
    SpringBoot Web相关配置的第一步自动配置的ViewResolver是ContentNegotiatingViewResolver:
    (一) ContentNegotiatingViewResolver
    这是Spring MVC提供的一个特殊的ViewResolver,ContentNegotiatingViewResolver不是自己处理View,而是代理给不同的ViewResolver来处理不同的View,所以它有最高的优先级。
    (二) BeanNameViewResolver
    在控制器(@Controller)中的一个方法返值的字符串(视图名)会根据BeanNameViewResolver去查找Bean的名称为返回字符串的View来渲染视图
    (三) InternalResourceViewResolver
    这个是一个极为常用的ViewResolver,主要通过设置前缀,后缀,以及控制器中方法来返回视图名的字符串,以得到实际页面


    /*
    * Configure ContentNegotiatingViewResolver ?? 这个配置有什么意义呢??
    */
    @Bean
    public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
    resolver.setContentNegotiationManager(manager);

    // Define all possible view resolvers
    List<ViewResolver> resolvers = new ArrayList<>();

    resolvers.add(csvViewResolver());
    resolvers.add(excelViewResolver());
    resolvers.add(pdfViewResolver());

    resolver.setViewResolvers(resolvers);
    return resolver;
    }


    try {
    String tmpdir = System.getProperty("java.io.tmpdir");
    File file = new File(tmpdir, LocalDateTime.now().toString("yyyyMMddHHmmssSSS") + ".xls");
    log.info("file path :{}", file.getAbsolutePath());
    OutputStream stream = new FileOutputStream(file);
    workbook.write(stream);
    stream.close();
    } catch (IOException e) {
    log.error(e.getMessage(), e);
    }

    ExceptionHandler初始化的地方:
    /**
    * Returns a {@link HandlerExceptionResolverComposite} containing a list of exception
    * resolvers obtained either through {@link #configureHandlerExceptionResolvers} or
    * through {@link #addDefaultHandlerExceptionResolvers}.
    * <p><strong>Note:</strong> This method cannot be made final due to CGLIB constraints.
    * Rather than overriding it, consider overriding {@link #configureHandlerExceptionResolvers}
    * which allows for providing a list of resolvers.
    */
    @Bean
    public HandlerExceptionResolver handlerExceptionResolver() {
    List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<HandlerExceptionResolver>();
    configureHandlerExceptionResolvers(exceptionResolvers);
    if (exceptionResolvers.isEmpty()) {
    addDefaultHandlerExceptionResolvers(exceptionResolvers);
    }
    extendHandlerExceptionResolvers(exceptionResolvers);
    HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
    composite.setOrder(0);
    composite.setExceptionResolvers(exceptionResolvers);
    return composite;
    }
    org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#handlerExceptionResolver


    /**
    * Override this method to configure the list of
    * {@link HandlerExceptionResolver HandlerExceptionResolvers} to use.
    * <p>Adding resolvers to the list turns off the default resolvers that would otherwise
    * be registered by default. Also see {@link #addDefaultHandlerExceptionResolvers}
    * that can be used to add the default exception resolvers.
    * @param exceptionResolvers a list to add exception resolvers to (initially an empty list)
    */
    @Override
    protected void configureHandlerExceptionResolvers(
    List<HandlerExceptionResolver> exceptionResolvers) {
    super.configureHandlerExceptionResolvers(exceptionResolvers);
    if (exceptionResolvers.isEmpty()) {
    addDefaultHandlerExceptionResolvers(exceptionResolvers);
    }
    if (this.mvcProperties.isLogResolvedException()) {
    for (HandlerExceptionResolver resolver : exceptionResolvers) {
    if (resolver instanceof AbstractHandlerExceptionResolver) {
    ((AbstractHandlerExceptionResolver) resolver)
    .setWarnLogCategory(resolver.getClass().getName());
    }
    }
    }
    }
    org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.EnableWebMvcConfiguration#configureHandlerExceptionResolvers
    org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.EnableWebMvcConfiguration 【内部静态类】


    /**
    * A method available to subclasses for adding default
    * {@link HandlerExceptionResolver HandlerExceptionResolvers}.
    * <p>Adds the following exception resolvers:
    * <ul>
    * <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
    * {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
    * <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with
    * {@link org.springframework.web.bind.annotation.ResponseStatus}.
    * <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring exception types
    * </ul>
    */
    protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
    exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager());
    exceptionHandlerResolver.setMessageConverters(getMessageConverters());
    exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
    exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
    if (jackson2Present) {
    exceptionHandlerResolver.setResponseBodyAdvice(
    Collections.<ResponseBodyAdvice<?>>singletonList(new JsonViewResponseBodyAdvice()));
    }
    exceptionHandlerResolver.setApplicationContext(this.applicationContext);
    exceptionHandlerResolver.afterPropertiesSet();
    exceptionResolvers.add(exceptionHandlerResolver);

    ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
    responseStatusResolver.setMessageSource(this.applicationContext);
    exceptionResolvers.add(responseStatusResolver);

    exceptionResolvers.add(new DefaultHandlerExceptionResolver());
    }
    org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#addDefaultHandlerExceptionResolvers

    /**
    * Invoked by the containing {@code BeanFactory} after it has set all bean properties
    * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
    * <p>This method allows the bean instance to perform validation of its overall
    * configuration and final initialization when all bean properties have been set.
    * @throws Exception in the event of misconfiguration (such as failure to set an
    * essential property) or if initialization fails for any other reason
    */
    @Override
    public void afterPropertiesSet() {
    // Do this first, it may add ResponseBodyAdvice beans
    initExceptionHandlerAdviceCache();

    if (this.argumentResolvers == null) {
    List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
    this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
    List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
    this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
    }

    private void initExceptionHandlerAdviceCache() {
    if (getApplicationContext() == null) {
    return;
    }
    if (logger.isDebugEnabled()) {
    logger.debug("Looking for exception mappings: " + getApplicationContext());
    }

    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    AnnotationAwareOrderComparator.sort(adviceBeans);

    for (ControllerAdviceBean adviceBean : adviceBeans) {
    ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
    if (resolver.hasExceptionMappings()) {
    this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
    if (logger.isInfoEnabled()) {
    logger.info("Detected @ExceptionHandler methods in " + adviceBean);
    }
    }
    if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {
    this.responseBodyAdvice.add(adviceBean);
    if (logger.isInfoEnabled()) {
    logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean);
    }
    }
    }
    }
    org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#afterPropertiesSet

    /**
    * Encapsulates information about an {@linkplain ControllerAdvice @ControllerAdvice}
    * Spring-managed bean without necessarily requiring it to be instantiated.
    *
    * <p>The {@link #findAnnotatedBeans(ApplicationContext)} method can be used to
    * discover such beans. However, a {@code ControllerAdviceBean} may be created
    * from any object, including ones without an {@code @ControllerAdvice}.
    *
    * @author Rossen Stoyanchev
    * @author Brian Clozel
    * @author Juergen Hoeller
    * @since 3.2
    */
    public class ControllerAdviceBean implements Ordered {

    。。。

    /**
    * Find the names of beans annotated with
    * {@linkplain ControllerAdvice @ControllerAdvice} in the given
    * ApplicationContext and wrap them as {@code ControllerAdviceBean} instances.
    */
    public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
    List<ControllerAdviceBean> beans = new ArrayList<ControllerAdviceBean>();
    for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class)) {
    if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
    beans.add(new ControllerAdviceBean(name, applicationContext));
    }
    }
    return beans;
    }
    org.springframework.web.method.ControllerAdviceBean#findAnnotatedBeans



    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    AnnotationAwareOrderComparator.sort(adviceBeans);
    for (ControllerAdviceBean adviceBean : adviceBeans) {

    this = {ExceptionHandlerExceptionResolver@6666}
    adviceBeans = {ArrayList@6728} size = 4
    0 = {ControllerAdviceBean@6729} "JSONPResponseBodyAdvice"
    1 = {ControllerAdviceBean@6740} "fastJsonpResponseBodyAdvice"
    2 = {ControllerAdviceBean@6741} "globalExceptionHandler"
    3 = {ControllerAdviceBean@6742} "fastJsonViewResponseBodyAdvice"

    /**
    * Allows customizing the response after the execution of an {@code @ResponseBody}
    * or a {@code ResponseEntity} controller method but before the body is written
    * with an {@code HttpMessageConverter}.
    *
    * <p>Implementations may be registered directly with
    * {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver}
    * or more likely annotated with {@code @ControllerAdvice} in which case they
    * will be auto-detected by both.
    *
    * @author Rossen Stoyanchev
    * @since 4.1
    */
    public interface ResponseBodyAdvice<T> {
    org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice

    配置内容协调的地方:
    /**
    * Return a {@link ContentNegotiationManager} instance to use to determine
    * requested {@linkplain MediaType media types} in a given request.
    */
    @Bean
    @Override
    public ContentNegotiationManager mvcContentNegotiationManager() {
    ContentNegotiationManager manager = super.mvcContentNegotiationManager();
    List<ContentNegotiationStrategy> strategies = manager.getStrategies();
    ListIterator<ContentNegotiationStrategy> iterator = strategies.listIterator();
    while (iterator.hasNext()) {
    ContentNegotiationStrategy strategy = iterator.next();
    if (strategy instanceof PathExtensionContentNegotiationStrategy) {
    iterator.set(new OptionalPathExtensionContentNegotiationStrategy(
    strategy));
    }
    }
    return manager;
    }
    org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.EnableWebMvcConfiguration#mvcContentNegotiationManager

    /**
    * Return a {@link ContentNegotiationManager} instance to use to determine
    * requested {@linkplain MediaType media types} in a given request.
    */
    @Bean
    public ContentNegotiationManager mvcContentNegotiationManager() {
    if (this.contentNegotiationManager == null) {
    ContentNegotiationConfigurer configurer = new ContentNegotiationConfigurer(this.servletContext);
    configurer.mediaTypes(getDefaultMediaTypes());
    configureContentNegotiation(configurer);
    this.contentNegotiationManager = configurer.buildContentNegotiationManager();
    }
    return this.contentNegotiationManager;
    }
    org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#mvcContentNegotiationManager

    商业闭环已经验证

    package org.springframework.web.servlet.config.annotation;
    /**
    * Creates a {@code ContentNegotiationManager} and configures it with
    * one or more {@link ContentNegotiationStrategy} instances.
    *
    * <p>As an alternative you can also rely on the set of defaults described below
    * which can be turned on or off or customized through the methods of this
    * builder:
    *
    * <table>
    * <tr>
    * <th>Configurer Property</th>
    * <th>Underlying Strategy</th>
    * <th>Default Setting</th>
    * </tr>
    * <tr>
    * <td>{@link #favorPathExtension}</td>
    * <td>{@link PathExtensionContentNegotiationStrategy Path Extension strategy}</td>
    * <td>On</td>
    * </tr>
    * <tr>
    * <td>{@link #favorParameter}</td>
    * <td>{@link ParameterContentNegotiationStrategy Parameter strategy}</td>
    * <td>Off</td>
    * </tr>
    * <tr>
    * <td>{@link #ignoreAcceptHeader}</td>
    * <td>{@link HeaderContentNegotiationStrategy Header strategy}</td>
    * <td>On</td>
    * </tr>
    * <tr>
    * <td>{@link #defaultContentType}</td>
    * <td>{@link FixedContentNegotiationStrategy Fixed content strategy}</td>
    * <td>Not set</td>
    * </tr>
    * <tr>
    * <td>{@link #defaultContentTypeStrategy}</td>
    * <td>{@link ContentNegotiationStrategy}</td>
    * <td>Not set</td>
    * </tr>
    * </table>
    *
    * <p>The order in which strategies are configured is fixed. You can only turn
    * them on or off.
    *
    * <p>For the path extension and parameter strategies you may explicitly add
    * {@link #mediaType MediaType mappings}. Those will be used to resolve path
    * extensions and/or a query parameter value such as "json" to a concrete media
    * type such as "application/json".
    *
    * <p>The path extension strategy will also use {@link ServletContext#getMimeType}
    * and the Java Activation framework (JAF), if available, to resolve a path
    * extension to a MediaType. You may however {@link #useJaf suppress} the use
    * of JAF.
    *
    * @author Rossen Stoyanchev
    * @author Brian Clozel
    * @author Juergen Hoeller
    * @since 3.2
    * @see ContentNegotiationManagerFactoryBean
    */
    public class ContentNegotiationConfigurer {

    /**
    * Build a {@link ContentNegotiationManager} based on this configurer's settings.
    * @since 4.3.12
    * @see ContentNegotiationManagerFactoryBean#getObject()
    */
    protected ContentNegotiationManager buildContentNegotiationManager() {
    this.factory.addMediaTypes(this.mediaTypes);
    this.factory.afterPropertiesSet();
    return this.factory.getObject();
    }
    org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer#buildContentNegotiationManager

    /**
    * Factory to create a {@code ContentNegotiationManager} and configure it with
    * one or more {@link ContentNegotiationStrategy} instances via simple setters.
    * The following table shows setters, resulting strategy instances, and if in
    * use by default:
    *
    * <table>
    * <tr>
    * <th>Property Setter</th>
    * <th>Underlying Strategy</th>
    * <th>Default Setting</th>
    * </tr>
    * <tr>
    * <td>{@link #setFavorPathExtension}</td>
    * <td>{@link PathExtensionContentNegotiationStrategy Path Extension strategy}</td>
    * <td>On</td>
    * </tr>
    * <tr>
    * <td>{@link #setFavorParameter favorParameter}</td>
    * <td>{@link ParameterContentNegotiationStrategy Parameter strategy}</td>
    * <td>Off</td>
    * </tr>
    * <tr>
    * <td>{@link #setIgnoreAcceptHeader ignoreAcceptHeader}</td>
    * <td>{@link HeaderContentNegotiationStrategy Header strategy}</td>
    * <td>On</td>
    * </tr>
    * <tr>
    * <td>{@link #setDefaultContentType defaultContentType}</td>
    * <td>{@link FixedContentNegotiationStrategy Fixed content strategy}</td>
    * <td>Not set</td>
    * </tr>
    * <tr>
    * <td>{@link #setDefaultContentTypeStrategy defaultContentTypeStrategy}</td>
    * <td>{@link ContentNegotiationStrategy}</td>
    * <td>Not set</td>
    * </tr>
    * </table>
    *
    * <p>The order in which strategies are configured is fixed. Setters may only
    * turn individual strategies on or off. If you need a custom order for any
    * reason simply instantiate {@code ContentNegotiationManager} directly.
    *
    * <p>For the path extension and parameter strategies you may explicitly add
    * {@link #setMediaTypes MediaType mappings}. This will be used to resolve path
    * extensions or a parameter value such as "json" to a media type such as
    * "application/json".
    *
    * <p>The path extension strategy will also use {@link ServletContext#getMimeType}
    * and the Java Activation framework (JAF), if available, to resolve a path
    * extension to a MediaType. You may {@link #setUseJaf suppress} the use of JAF.
    *
    * @author Rossen Stoyanchev
    * @author Brian Clozel
    * @since 3.2
    */
    public class ContentNegotiationManagerFactoryBean
    implements FactoryBean<ContentNegotiationManager>, ServletContextAware, InitializingBean {


    private boolean favorPathExtension = true;

    private boolean favorParameter = false;

    private boolean ignoreAcceptHeader = false;

    private Map<String, MediaType> mediaTypes = new HashMap<String, MediaType>();

    private boolean ignoreUnknownPathExtensions = true;

    private Boolean useJaf;

    private String parameterName = "format";

    。。。

    @Override
    public void afterPropertiesSet() {
    List<ContentNegotiationStrategy> strategies = new ArrayList<ContentNegotiationStrategy>();

    if (this.favorPathExtension) {
    PathExtensionContentNegotiationStrategy strategy;
    if (this.servletContext != null && !isUseJafTurnedOff()) {
    strategy = new ServletPathExtensionContentNegotiationStrategy(this.servletContext, this.mediaTypes);
    }
    else {
    strategy = new PathExtensionContentNegotiationStrategy(this.mediaTypes);
    }
    strategy.setIgnoreUnknownExtensions(this.ignoreUnknownPathExtensions);
    if (this.useJaf != null) {
    strategy.setUseJaf(this.useJaf);
    }
    strategies.add(strategy);
    }

    if (this.favorParameter) {
    ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(this.mediaTypes);
    strategy.setParameterName(this.parameterName);
    strategies.add(strategy);
    }

    if (!this.ignoreAcceptHeader) { 【添加内容协调的地方】
    strategies.add(new HeaderContentNegotiationStrategy());
    }

    if (this.defaultNegotiationStrategy != null) {
    strategies.add(this.defaultNegotiationStrategy);
    }

    this.contentNegotiationManager = new ContentNegotiationManager(strategies);
    }
    org.springframework.web.accept.ContentNegotiationManagerFactoryBean#afterPropertiesSet

    hm->AbstractHandlerMapping.java->AbstractHandlerMethodMapping.java


    public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean
    /**
    * Look up a handler instance for the given URL path.
    * <p>Supports direct matches, e.g. a registered "/test" matches "/test",
    * and various Ant-style pattern matches, e.g. a registered "/t*" matches
    * both "/test" and "/team". For details, see the AntPathMatcher class.
    * <p>Looks for the most exact pattern, where most exact is defined as
    * the longest path pattern.
    * @param urlPath URL the bean is mapped to
    * @param request current HTTP request (to expose the path within the mapping to)
    * @return the associated handler instance, or {@code null} if not found
    * @see #exposePathWithinMapping
    * @see org.springframework.util.AntPathMatcher
    */
    org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#lookupHandler 【AbstractUrlHandlerMapping extends AbstractHandlerMapping】
    private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
    /**/favicon.ico【org.springframework.web.servlet.handler.SimpleUrlHandlerMapping】

    /**
    * Implementation of the {@link org.springframework.web.servlet.HandlerMapping}
    * interface to map from URLs to request handler beans. Supports both mapping to bean
    * instances and mapping to bean names; the latter is required for non-singleton handlers.
    *
    * <p>The "urlMap" property is suitable for populating the handler map with
    * bean references, e.g. via the map element in XML bean definitions.
    *
    * <p>Mappings to bean names can be set via the "mappings" property, in a form
    * accepted by the {@code java.util.Properties} class, like as follows:<br>
    * {@code
    * /welcome.html=ticketController
    * /show.html=ticketController
    * }<br>
    * The syntax is {@code PATH=HANDLER_BEAN_NAME}.
    * If the path doesn't begin with a slash, one is prepended.
    *
    * <p>Supports direct matches (given "/test" -> registered "/test") and "*"
    * pattern matches (given "/test" -> registered "/t*"). Note that the default
    * is to map within the current servlet mapping if applicable; see the
    * {@link #setAlwaysUseFullPath "alwaysUseFullPath"} property. For details on the
    * pattern options, see the {@link org.springframework.util.AntPathMatcher} javadoc.

    * @author Rod Johnson
    * @author Juergen Hoeller
    * @see #setMappings
    * @see #setUrlMap
    * @see BeanNameUrlHandlerMapping
    */
    public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { 【map from URLs to request handler beans】

    org.springframework.web.servlet.handler.SimpleUrlHandlerMapping 匹配到了/**/favicon.ico



    /**
    * The lookup handler method, maps the SEOMapper method to the request URL.
    * <p>If no mapping is found, or if the URL is disabled, it will simply drop through
    * to the standard 404 handling.</p>
    *
    * @param urlPath the path to match.
    * @param request the http servlet request.
    * @return The HandlerMethod if one was found.
    */
    springfox.documentation.spring.web.PropertySourcedRequestMappingHandlerMapping#lookupHandlerMethod
    /v2/api-docs


    PropertySourcedRequestMappingHandlerMapping extends RequestMappingHandlerMapping

    /**
    * Match the given URI to a map of variable values. Keys in the returned map are variable names,
    * values are variable values, as occurred in the given URI.
    * <p>Example:
    * <pre class="code">
    * UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
    * System.out.println(template.match("http://example.com/hotels/1/bookings/42"));
    * </pre>
    * will print: <blockquote>{@code {hotel=1, booking=42}}</blockquote>
    * @param uri the URI to match to
    * @return a map of variable values
    */
    public Map<String, String> match(String uri) {
    Assert.notNull(uri, "'uri' must not be null");
    Map<String, String> result = new LinkedHashMap<String, String>(this.variableNames.size());
    Matcher matcher = this.matchPattern.matcher(uri);
    if (matcher.find()) {
    for (int i = 1; i <= matcher.groupCount(); i++) {
    String name = this.variableNames.get(i - 1);
    String value = matcher.group(i);
    result.put(name, value);
    }
    }
    return result;
    }

    org.springframework.web.util.UriTemplate#match

    public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo>

    org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#getMatchingMapping

    public class EndpointHandlerMapping extends AbstractEndpointHandlerMapping<MvcEndpoint> {

    org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping
    0 = "/help/info"
    1 = "/help/info.json"
    2 = "/help/auditevents"
    3 = "/help/auditevents.json"
    4 = "/help/mappings"
    5 = "/help/mappings.json"
    6 = "/help/autoconfig"
    7 = "/help/autoconfig.json"
    8 = "/help/beans"
    9 = "/help/beans.json"
    10 = "/help/env"
    11 = "/help/env.json"
    12 = "/help/trace"
    13 = "/help/trace.json"
    14 = "/help/metrics"
    15 = "/help/metrics.json"
    16 = "/help/dump"
    17 = "/help/dump.json"
    18 = "/help/health"
    19 = "/help/health.json"
    20 = "/help/configprops"
    21 = "/help/configprops.json"
    22 = "/help/flyway"
    23 = "/help/flyway.json"
    24 = "/help/heapdump"
    25 = "/help/heapdump.json"
    26 = "/help/loggers"
    27 = "/help/loggers.json"

    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
    所有带有RequestMapping注解的接口:
    0 = "/deferred"
    1 = "/webAsyncTask"
    2 = "/city"
    3 = "/city/list"
    4 = "/csrf"
    5 = "/etag/list"
    6 = "/session/get"
    7 = "/session/set"
    8 = "/test/json"
    9 = "/test/mock/{userId}/auth"
    10 = "/test/mock/{userId}/detail"
    11 = "/test/mock/{userId}"
    12 = "/test/mock/addHead/{name}"
    13 = "/test/mvc/ma"
    14 = "/change/user/{userId}"
    15 = "/change/user"
    16 = "/tx/student/list"
    17 = "/tx/student/save"
    18 = "/tx/student/{id}"
    19 = "/post/data/form"
    20 = "/post/data/json"
    21 = "/get"
    22 = "/add"
    23 = "/getHash"
    24 = "/set/hash/null"
    25 = "/setList/{element}"
    26 = "/getList"
    27 = "/redis/{key}"
    28 = "/setHash/{field}/{value}"
    29 = "/user/{username}"
    30 = "/user"
    31 = "/v1/mvc/valids"
    32 = "/v1/mvc/valids/valid"
    33 = "/v1/mvc/valids/validated"
    34 = "/admin/user"
    35 = "/home"
    36 = "/"
    37 = "/captcha.jpg"
    38 = "/location/data.json"
    39 = "/location/map.html"
    40 = "/location/amap.html"
    41 = "/location/amapGps.json"
    42 = "/profile/upload"
    43 = "/search"
    44 = "/table/rowspan"
    45 = "/test/upload"
    46 = "/swagger"
    47 = "/swagger-resources/configuration/security"
    48 = "/swagger-resources"
    49 = "/swagger-resources/configuration/ui"
    50 = "/error"

    /**
    * Expose URI template variables, matrix variables, and producible media types in the request.
    * @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
    * @see HandlerMapping#MATRIX_VARIABLES_ATTRIBUTE
    * @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
    */
    org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#handleMatch

    Object handler : org.springframework.web.method.HandlerMethod


    /**
    * Create a new HandlerExecutionChain.
    * @param handler the handler object to execute
    */
    public HandlerExecutionChain(Object handler) {
    this(handler, (HandlerInterceptor[]) null);
    }

    org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain

    /**
    * Build a {@link HandlerExecutionChain} for the given handler, including
    * applicable interceptors.
    * <p>The default implementation builds a standard {@link HandlerExecutionChain}
    * with the given handler, the handler mapping's common interceptors, and any
    * {@link MappedInterceptor}s matching to the current request URL. Interceptors
    * are added in the order they were registered. Subclasses may override this
    * in order to extend/rearrange the list of interceptors.
    * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a
    * pre-built {@link HandlerExecutionChain}. This method should handle those
    * two cases explicitly, either building a new {@link HandlerExecutionChain}
    * or extending the existing chain.
    * <p>For simply adding an interceptor in a custom subclass, consider calling
    * {@code super.getHandlerExecutionChain(handler, request)} and invoking
    * {@link HandlerExecutionChain#addInterceptor} on the returned chain object.
    * @param handler the resolved handler instance (never {@code null})
    * @param request current HTTP request
    * @return the HandlerExecutionChain (never {@code null})
    * @see #getAdaptedInterceptors()
    */
    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
    (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

    /**
    * Create a new HandlerExecutionChain.
    * @param handler the handler object to execute
    */
    public HandlerExecutionChain(Object handler) {
    this(handler, (HandlerInterceptor[]) null);
    }

    /**
    * Create a new HandlerExecutionChain.
    * @param handler the handler object to execute
    * @param interceptors the array of interceptors to apply
    * (in the given order) before the handler itself executes
    */
    public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {
    if (handler instanceof HandlerExecutionChain) {
    HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
    this.handler = originalChain.getHandler();
    this.interceptorList = new ArrayList<HandlerInterceptor>();
    CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
    CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
    }
    else {
    this.handler = handler;
    this.interceptors = interceptors;
    }
    }


    adaptedInterceptors = {ArrayList@11931} size = 5
    0 = {MyInterceptor1@12487}
    1 = {MyInterceptor2@12495}
    2 = {MyInterceptor3@12502}
    3 = {ConversionServiceExposingInterceptor@12503}
    4 = {ResourceUrlProviderExposingInterceptor@12504}


    /**
    * Return the path within the web application for the given request.
    * <p>Detects include request URL if called within a RequestDispatcher include.
    * @param request current HTTP request
    * @return the path within the web application
    */
    public String getPathWithinApplication(HttpServletRequest request) {
    org.springframework.web.util.UrlPathHelper#getPathWithinApplication



    // An interceptor array specified through the constructor
    CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);

    存放URL的数据及相应HttpMethod的对象。
    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$Match
    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.Match
    /**
    * A thin wrapper around a matched HandlerMethod and its mapping, for the purpose of
    * comparing the best match with a comparator in the context of the current request.
    */
    private class Match {

    private final T mapping;

    private final HandlerMethod handlerMethod;

    public Match(T mapping, HandlerMethod handlerMethod) {
    this.mapping = mapping;
    this.handlerMethod = handlerMethod;
    }

    @Override
    public String toString() {
    return this.mapping.toString();
    }
    }

    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MatchComparator
    private class MatchComparator implements Comparator<Match> {

    private final Comparator<T> comparator;

    public MatchComparator(Comparator<T> comparator) {
    this.comparator = comparator;
    }

    @Override
    public int compare(Match match1, Match match2) {
    return this.comparator.compare(match1.mapping, match2.mapping);
    }
    }
    有多直接,有多简单,有多直指人心。为什么把第一个版本做的多复杂,因为不自信。太肉,肯定没有暴发力
    一堆恐惧,一堆不自信
    private static class EmptyHandler {

    public void handle() {
    throw new UnsupportedOperationException("Not implemented");
    }
    }
    org.springframework.web.method.HandlerMethod
    对方要的确定性,会不会造成很大的压力。大佬对别人的压力感,并不是太在意
    饥渴的用户。能够给这些用户给予确定性的满足

    private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
    class org.springframework.web.servlet.i18n.LocaleChangeInterceptor
    class org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor
    class org.springframework.web.servlet.resource.ResourceUrlProviderExposingInterceptor


    安全区是没有壁垒的
    m:意识到风险
    zj:有风控意识
    upper:能操作有风险的执行

    富贵险中求,控制核心资源,定下有利用自己的规则。体量大的,先到的,不怕死的
    public abstract class CorsUtils {

    /**
    * Returns {@code true} if the request is a valid CORS one.
    */
    public static boolean isCorsRequest(HttpServletRequest request) {
    return (request.getHeader(HttpHeaders.ORIGIN) != null);
    }
    先被嫌弃,再被抛弃,然后慢慢。。。
    (1)守住根基-》安全
    (2)砍产品

    关键任务,跨越生死


    情绪纠偏,换个视角

    org.springframework.web.method.HandlerMethod$HandlerMethodParameter


    org.springframework.web.servlet.HandlerExecutionChain


    /**
    * Return the HandlerAdapter for this handler object.
    * @param handler the handler object to find an adapter for
    * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
    */
    org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter

    0 = {RequestMappingHandlerAdapter@11772}
    1 = {HttpRequestHandlerAdapter@11773}
    2 = {SimpleControllerHandlerAdapter@11774}


    【0】 RequestMappingHandlerAdapter 【org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter】:
    /**
    * An {@link AbstractHandlerMethodAdapter} that supports {@link HandlerMethod}s
    * with their method argument and return type signature, as defined via
    * {@code @RequestMapping}.
    *
    * <p>Support for custom argument and return value types can be added via
    * {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}.
    * Or alternatively, to re-configure all argument and return value types,
    * use {@link #setArgumentResolvers} and {@link #setReturnValueHandlers}.
    *
    * @author Rossen Stoyanchev
    * @author Juergen Hoeller
    * @since 3.1
    * @see HandlerMethodArgumentResolver
    * @see HandlerMethodReturnValueHandler
    */
    public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
    implements BeanFactoryAware, InitializingBean {

    【org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#supports】
    /**
    * This implementation expects the handler to be an {@link HandlerMethod}.
    * @param handler the handler instance to check
    * @return whether or not this adapter can adapt the given handler
    */
    @Override
    public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }


    /**
    * Provides a method for invoking the handler method for a given request after resolving its
    * method argument values through registered {@link HandlerMethodArgumentResolver}s.
    *
    * <p>Argument resolution often requires a {@link WebDataBinder} for data binding or for type
    * conversion. Use the {@link #setDataBinderFactory(WebDataBinderFactory)} property to supply
    * a binder factory to pass to argument resolvers.
    *
    * <p>Use {@link #setHandlerMethodArgumentResolvers} to customize the list of argument resolvers.
    *
    * @author Rossen Stoyanchev
    * @author Juergen Hoeller
    * @since 3.1
    */
    public class InvocableHandlerMethod extends HandlerMethod {


    /**
    * Extends {@link InvocableHandlerMethod} with the ability to handle return
    * values through a registered {@link HandlerMethodReturnValueHandler} and
    * also supports setting the response status based on a method-level
    * {@code @ResponseStatus} annotation.
    *
    * <p>A {@code null} return value (including void) may be interpreted as the
    * end of request processing in combination with a {@code @ResponseStatus}
    * annotation, a not-modified check condition
    * (see {@link ServletWebRequest#checkNotModified(long)}), or
    * a method argument that provides access to the response stream.
    *
    * @author Rossen Stoyanchev
    * @author Juergen Hoeller
    * @since 3.1
    */
    public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {

    private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call");


    ((ApplicationContextFacade) ((ServletContextResource) resource).getServletContext()).context.getContext().getDocBase():
    docBase : C:UsersTANGCH~1AppDataLocalTemp omcat-docbase.8903617127552296966.8080
    C:UsersTANGCH~1AppDataLocalTemp omcat-docbase.8903617127552296966.8080


    /**
    * Records model and view related decisions made by
    * {@link HandlerMethodArgumentResolver}s and
    * {@link HandlerMethodReturnValueHandler}s during the course of invocation of
    * a controller method.
    *
    * <p>The {@link #setRequestHandled} flag can be used to indicate the request
    * has been handled directly and view resolution is not required.
    *
    * <p>A default {@link Model} is automatically created at instantiation.
    * An alternate model instance may be provided via {@link #setRedirectModel}
    * for use in a redirect scenario. When {@link #setRedirectModelScenario} is set
    * to {@code true} signalling a redirect scenario, the {@link #getModel()}
    * returns the redirect model instead of the default model.
    *
    * @author Rossen Stoyanchev
    * @author Juergen Hoeller
    * @since 3.1
    */
    public class ModelAndViewContainer {

    【1】 HttpRequestHandlerAdapter 【org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter】
    /**
    * Adapter to use the plain {@link org.springframework.web.HttpRequestHandler}
    * interface with the generic {@link org.springframework.web.servlet.DispatcherServlet}.
    * Supports handlers that implement the {@link LastModified} interface.
    *
    * <p>This is an SPI class, not used directly by application code.
    *
    * @author Juergen Hoeller
    * @since 2.0
    * @see org.springframework.web.servlet.DispatcherServlet
    * @see org.springframework.web.HttpRequestHandler
    * @see LastModified
    * @see SimpleControllerHandlerAdapter
    */
    public class HttpRequestHandlerAdapter implements HandlerAdapter {

    @Override
    public boolean supports(Object handler) {
    return (handler instanceof HttpRequestHandler);
    }

    /**
    * {@code HttpRequestHandler} that serves static resources in an optimized way
    * according to the guidelines of Page Speed, YSlow, etc.
    *
    * <p>The {@linkplain #setLocations "locations"} property takes a list of Spring
    * {@link Resource} locations from which static resources are allowed to be served
    * by this handler. Resources could be served from a classpath location, e.g.
    * "classpath:/META-INF/public-web-resources/", allowing convenient packaging
    * and serving of resources such as .js, .css, and others in jar files.
    *
    * <p>This request handler may also be configured with a
    * {@link #setResourceResolvers(List) resourcesResolver} and
    * {@link #setResourceTransformers(List) resourceTransformer} chains to support
    * arbitrary resolution and transformation of resources being served. By default
    * a {@link PathResourceResolver} simply finds resources based on the configured
    * "locations". An application can configure additional resolvers and transformers
    * such as the {@link VersionResourceResolver} which can resolve and prepare URLs
    * for resources with a version in the URL.
    *
    * <p>This handler also properly evaluates the {@code Last-Modified} header
    * (if present) so that a {@code 304} status code will be returned as appropriate,
    * avoiding unnecessary overhead for resources that are already cached by the client.
    *
    * @author Keith Donald
    * @author Jeremy Grelle
    * @author Juergen Hoeller
    * @author Arjen Poutsma
    * @author Brian Clozel
    * @author Rossen Stoyanchev
    * @since 3.0.4
    */
    public class ResourceHttpRequestHandler extends WebContentGenerator
    implements HttpRequestHandler, EmbeddedValueResolverAware, InitializingBean, CorsConfigurationSource {

    HTML页面404报错的代码及相关判断逻辑:
    /**
    * Processes a resource request.
    * <p>Checks for the existence of the requested resource in the configured list of locations.
    * If the resource does not exist, a {@code 404} response will be returned to the client.
    * If the resource exists, the request will be checked for the presence of the
    * {@code Last-Modified} header, and its value will be compared against the last-modified
    * timestamp of the given resource, returning a {@code 304} status code if the
    * {@code Last-Modified} value is greater. If the resource is newer than the
    * {@code Last-Modified} value, or the header is not present, the content resource
    * of the resource will be written to the response with caching headers
    * set to expire one year in the future.
    */
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

    // For very general mappings (e.g. "/") we need to check 404 first
    Resource resource = getResource(request);
    if (resource == null) {
    logger.trace("No matching resource found - returning 404");
    response.sendError(HttpServletResponse.SC_NOT_FOUND);
    return;
    }
    org.springframework.web.servlet.resource.ResourceHttpRequestHandler#handleRequest

    response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");


    org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle

    org.springframework.web.servlet.handler.AbstractUrlHandlerMapping$PathExposingHandlerInterceptor
    org.springframework.web.servlet.resource.ResourceUrlProviderExposingInterceptor


    静态资源【resource】页面,直接在Response中填入数据,也不需要返回ModelAndView


    /**
    * A {@link RequestCondition} that consists of the following other conditions:
    * <ol>
    * <li>{@link PatternsRequestCondition}
    * <li>{@link RequestMethodsRequestCondition}
    * <li>{@link ParamsRequestCondition}
    * <li>{@link HeadersRequestCondition}
    * <li>{@link ConsumesRequestCondition}
    * <li>{@link ProducesRequestCondition}
    * <li>{@code RequestCondition} (optional, custom request condition)
    * </ol>
    *
    * @author Arjen Poutsma
    * @author Rossen Stoyanchev
    * @since 3.1
    */
    public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {

    private final String name;

    private final PatternsRequestCondition patternsCondition;

    private final RequestMethodsRequestCondition methodsCondition;

    private final ParamsRequestCondition paramsCondition;

    private final HeadersRequestCondition headersCondition;

    private final ConsumesRequestCondition consumesCondition;

    private final ProducesRequestCondition producesCondition;

    private final RequestConditionHolder customConditionHolder;

    org.springframework.web.servlet.mvc.method.RequestMappingInfo


    /**
    * A logical disjunction (' || ') request condition to match a request's 'Accept' header
    * to a list of media type expressions. Two kinds of media type expressions are
    * supported, which are described in {@link RequestMapping#produces()} and
    * {@link RequestMapping#headers()} where the header name is 'Accept'.
    * Regardless of which syntax is used, the semantics are the same.
    *
    * @author Arjen Poutsma
    * @author Rossen Stoyanchev
    * @since 3.1
    */
    public final class ProducesRequestCondition extends AbstractRequestCondition<ProducesRequestCondition> {

    org.springframework.web.servlet.mvc.condition.ProducesRequestCondition


    /**
    * Look up a handler for the given request, falling back to the default
    * handler if no specific one is found.
    * @param request current HTTP request
    * @return the corresponding handler instance, or the default handler
    * @see #getHandlerInternal
    */
    @Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    Object handler = getHandlerInternal(request);
    if (handler == null) {
    handler = getDefaultHandler();
    }
    if (handler == null) {
    return null;
    }
    // Bean name or resolved handler?
    if (handler instanceof String) {
    String handlerName = (String) handler;
    handler = getApplicationContext().getBean(handlerName);
    }

    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    if (CorsUtils.isCorsRequest(request)) {
    CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
    CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
    executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }
    return executionChain;
    }
    org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler

    // Handler method lookup

    /**
    * Look up a handler method for the given request.
    */
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    if (logger.isDebugEnabled()) {
    logger.debug("Looking up handler method for path " + lookupPath);
    }
    this.mappingRegistry.acquireReadLock();
    try {
    HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    if (logger.isDebugEnabled()) {
    if (handlerMethod != null) {
    logger.debug("Returning handler method [" + handlerMethod + "]");
    }
    else {
    logger.debug("Did not find handler method for [" + lookupPath + "]");
    }
    }
    return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
    this.mappingRegistry.releaseReadLock();
    }
    }
    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal

    /**
    * Look up the best-matching handler method for the current request.
    * If multiple matches are found, the best match is selected.
    * @param lookupPath mapping lookup path within the current servlet mapping
    * @param request the current request
    * @return the best-matching handler method, or {@code null} if no match
    * @see #handleMatch(Object, String, HttpServletRequest)
    * @see #handleNoMatch(Set, String, HttpServletRequest)
    */
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<Match>();
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
    addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
    // No choice but to go through all mappings...
    addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    }

    if (!matches.isEmpty()) {
    Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
    Collections.sort(matches, comparator);
    if (logger.isTraceEnabled()) {
    logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
    lookupPath + "] : " + matches);
    }
    Match bestMatch = matches.get(0);
    if (matches.size() > 1) {
    if (CorsUtils.isPreFlightRequest(request)) {
    return PREFLIGHT_AMBIGUOUS_MATCH;
    }
    Match secondBestMatch = matches.get(1);
    if (comparator.compare(bestMatch, secondBestMatch) == 0) {
    Method m1 = bestMatch.handlerMethod.getMethod();
    Method m2 = secondBestMatch.handlerMethod.getMethod();
    throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
    request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
    }
    }
    request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
    handleMatch(bestMatch.mapping, lookupPath, request);
    return bestMatch.handlerMethod;
    }
    else {
    return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
    }
    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod


    private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
    for (T mapping : mappings) {
    T match = getMatchingMapping(mapping, request);
    if (match != null) {
    matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
    }
    }
    }
    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#addMatchingMappings 【为什么有一个匹配后,不直接跳出,而是把所有的都遍历完???】

    【匹配URL的关键代码】
    /**
    * Checks if all conditions in this request mapping info match the provided request and returns
    * a potentially new request mapping info with conditions tailored to the current request.
    * <p>For example the returned instance may contain the subset of URL patterns that match to
    * the current request, sorted with best matching patterns on top.
    * @return a new instance in case all conditions match; or {@code null} otherwise
    */
    @Override
    public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
    RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
    ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
    HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
    ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
    ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);

    if (methods == null || params == null || headers == null || consumes == null || produces == null) {
    return null;
    }

    PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
    if (patterns == null) {
    return null;
    }

    RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
    if (custom == null) {
    return null;
    }

    return new RequestMappingInfo(this.name, patterns,
    methods, params, headers, consumes, produces, custom.getCondition());
    }
    org.springframework.web.servlet.mvc.method.RequestMappingInfo#getMatchingCondition


    【org.springframework.web.cors.CorsUtils】
    /**
    * Utility class for CORS request handling based on the
    * <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>.
    *
    * @author Sebastien Deleuze
    * @since 4.2
    */
    public abstract class CorsUtils {

    /**
    * Returns {@code true} if the request is a valid CORS one.
    */
    public static boolean isCorsRequest(HttpServletRequest request) {
    return (request.getHeader(HttpHeaders.ORIGIN) != null);
    }

    /**
    * Returns {@code true} if the request is a valid CORS pre-flight one.
    */
    public static boolean isPreFlightRequest(HttpServletRequest request) {
    return (isCorsRequest(request) && HttpMethod.OPTIONS.matches(request.getMethod()) &&
    request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null);
    }

    }

    /**
    * The CORS {@code Access-Control-Request-Method} request header field name.
    * @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>
    */
    public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
    org.springframework.http.HttpHeaders#ACCESS_CONTROL_REQUEST_METHOD

    /**
    * The HTTP {@code Origin} header field name.
    * @see <a href="http://tools.ietf.org/html/rfc6454">RFC 6454</a>
    */
    public static final String ORIGIN = "Origin";
    org.springframework.http.HttpHeaders#ORIGIN

    【匹配HttpMethod:严格匹配】
    /**
    * Check if any of the HTTP request methods match the given request and
    * return an instance that contains the matching HTTP request method only.
    * @param request the current request
    * @return the same instance if the condition is empty (unless the request
    * method is HTTP OPTIONS), a new condition with the matched request method,
    * or {@code null} if there is no match or the condition is empty and the
    * request method is OPTIONS.
    */
    @Override
    public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
    if (CorsUtils.isPreFlightRequest(request)) {
    return matchPreFlight(request);
    }

    if (getMethods().isEmpty()) {
    if (RequestMethod.OPTIONS.name().equals(request.getMethod()) &&
    !DispatcherType.ERROR.equals(request.getDispatcherType())) {

    return null; // No implicit match for OPTIONS (we handle it)
    }
    return this;
    }

    return matchRequestMethod(request.getMethod());
    }
    org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition#getMatchingCondition


    package javax.servlet;

    /**
    * @since Servlet 3.0
    */
    public enum DispatcherType {
    FORWARD,
    INCLUDE,
    REQUEST,
    ASYNC,
    ERROR
    }


    private RequestMethodsRequestCondition matchRequestMethod(String httpMethodValue) {
    HttpMethod httpMethod = HttpMethod.resolve(httpMethodValue);
    if (httpMethod != null) {
    for (RequestMethod method : getMethods()) {
    if (httpMethod.matches(method.name())) {
    return new RequestMethodsRequestCondition(method);
    }
    }
    if (httpMethod == HttpMethod.HEAD && getMethods().contains(RequestMethod.GET)) {
    return GET_CONDITION;
    }
    }
    return null;
    }
    org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition#matchRequestMethod


    /**
    * Resolve the given method value to an {@code HttpMethod}.
    * @param method the method value as a String
    * @return the corresponding {@code HttpMethod}, or {@code null} if not found
    * @since 4.2.4
    */
    public static HttpMethod resolve(String method) {
    return (method != null ? mappings.get(method) : null);
    }
    org.springframework.http.HttpMethod#resolve


    package org.springframework.http;

    import java.util.HashMap;
    import java.util.Map;

    /**
    * Java 5 enumeration of HTTP request methods. Intended for use
    * with {@link org.springframework.http.client.ClientHttpRequest}
    * and {@link org.springframework.web.client.RestTemplate}.
    *
    * @author Arjen Poutsma
    * @author Juergen Hoeller
    * @since 3.0
    */
    public enum HttpMethod {

    GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;


    private static final Map<String, HttpMethod> mappings = new HashMap<String, HttpMethod>(16);

    static {
    for (HttpMethod httpMethod : values()) {
    mappings.put(httpMethod.name(), httpMethod);
    }
    }


    /**
    * Resolve the given method value to an {@code HttpMethod}.
    * @param method the method value as a String
    * @return the corresponding {@code HttpMethod}, or {@code null} if not found
    * @since 4.2.4
    */
    public static HttpMethod resolve(String method) {
    return (method != null ? mappings.get(method) : null);
    }


    /**
    * Determine whether this {@code HttpMethod} matches the given
    * method value.
    * @param method the method value as a String
    * @return {@code true} if it matches, {@code false} otherwise
    * @since 4.2.4
    */
    public boolean matches(String method) {
    return (this == resolve(method));
    }

    }

    【匹配HttpMethod:严格匹配,还有正则表达式 】
    org.springframework.web.servlet.mvc.condition.ParamsRequestCondition#getMatchingCondition
    /**
    * Returns "this" instance if the request matches all param expressions;
    * or {@code null} otherwise.
    */
    @Override
    public ParamsRequestCondition getMatchingCondition(HttpServletRequest request) {
    for (ParamExpression expression : expressions) {
    if (!expression.match(request)) {
    return null;
    }
    }
    return this;
    }


    public final boolean match(HttpServletRequest request) {
    boolean isMatch;
    if (this.value != null) {
    isMatch = matchValue(request);
    }
    else {
    isMatch = matchName(request);
    }
    return (this.isNegated ? !isMatch : isMatch);
    }
    org.springframework.web.servlet.mvc.condition.AbstractNameValueExpression#match


    【匹配QueryString:严格匹配,还有通配符 】
    /**
    * Returns "this" instance if the request matches all expressions;
    * or {@code null} otherwise.
    */
    @Override
    public HeadersRequestCondition getMatchingCondition(HttpServletRequest request) {
    if (CorsUtils.isPreFlightRequest(request)) {
    return PRE_FLIGHT_MATCH;
    }
    for (HeaderExpression expression : expressions) {
    if (!expression.match(request)) {
    return null;
    }
    }
    return this;
    }
    org.springframework.web.servlet.mvc.condition.HeadersRequestCondition#getMatchingCondition


    /**
    * Parses and matches a single param expression to a request.
    */
    static class ParamExpression extends AbstractNameValueExpression<String> {

    ParamExpression(String expression) {
    super(expression);
    }

    @Override
    protected boolean isCaseSensitiveName() {
    return true;
    }

    @Override
    protected String parseValue(String valueExpression) {
    return valueExpression;
    }

    @Override
    protected boolean matchName(HttpServletRequest request) {
    return WebUtils.hasSubmitParameter(request, this.name);
    }

    @Override
    protected boolean matchValue(HttpServletRequest request) {
    return ObjectUtils.nullSafeEquals(this.value, request.getParameter(this.name));
    }
    }
    org.springframework.web.servlet.mvc.condition.ParamsRequestCondition.ParamExpression

    【匹配'Content-Type' header:严格匹配,还有通配符 】
    /**
    * Checks if any of the contained media type expressions match the given
    * request 'Content-Type' header and returns an instance that is guaranteed
    * to contain matching expressions only. The match is performed via
    * {@link MediaType#includes(MediaType)}.
    * @param request the current request
    * @return the same instance if the condition contains no expressions;
    * or a new condition with matching expressions only;
    * or {@code null} if no expressions match
    */
    @Override
    public ConsumesRequestCondition getMatchingCondition(HttpServletRequest request) {
    if (CorsUtils.isPreFlightRequest(request)) {
    return PRE_FLIGHT_MATCH;
    }
    if (isEmpty()) {
    return this;
    }

    MediaType contentType;
    try {
    contentType = (StringUtils.hasLength(request.getContentType()) ?
    MediaType.parseMediaType(request.getContentType()) :
    MediaType.APPLICATION_OCTET_STREAM);
    }
    catch (InvalidMediaTypeException ex) {
    return null;
    }

    Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<ConsumeMediaTypeExpression>(this.expressions);
    for (Iterator<ConsumeMediaTypeExpression> iterator = result.iterator(); iterator.hasNext();) {
    ConsumeMediaTypeExpression expression = iterator.next();
    if (!expression.match(contentType)) {
    iterator.remove();
    }
    }
    return (!result.isEmpty() ? new ConsumesRequestCondition(result) : null);
    }
    org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition#getMatchingCondition


    /**
    * Parses and matches a single media type expression to a request's 'Content-Type' header.
    */
    static class ConsumeMediaTypeExpression extends AbstractMediaTypeExpression {

    ConsumeMediaTypeExpression(String expression) {
    super(expression);
    }

    ConsumeMediaTypeExpression(MediaType mediaType, boolean negated) {
    super(mediaType, negated);
    }

    public final boolean match(MediaType contentType) {
    boolean match = getMediaType().includes(contentType);
    return (!isNegated() ? match : !match);
    }
    }
    org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition.ConsumeMediaTypeExpression
    流量*30%=实际可能得到的粉丝数

    判断MediaType是否匹配的逻辑:
    /**
    * Indicate whether this MIME Type includes the given MIME Type.
    * <p>For instance, {@code text/*} includes {@code text/plain} and {@code text/html},
    * and {@code application/*+xml} includes {@code application/soap+xml}, etc.
    * This method is <b>not</b> symmetric.
    * @param other the reference MIME Type with which to compare
    * @return {@code true} if this MIME Type includes the given MIME Type;
    * {@code false} otherwise
    */
    public boolean includes(MimeType other) {
    if (other == null) {
    return false;
    }
    if (isWildcardType()) {
    // */* includes anything
    return true;
    }
    else if (getType().equals(other.getType())) {
    if (getSubtype().equals(other.getSubtype())) {
    return true;
    }
    if (isWildcardSubtype()) {
    // Wildcard with suffix, e.g. application/*+xml
    int thisPlusIdx = getSubtype().lastIndexOf('+');
    if (thisPlusIdx == -1) {
    return true;
    }
    else {
    // application/*+xml includes application/soap+xml
    int otherPlusIdx = other.getSubtype().indexOf('+');
    if (otherPlusIdx != -1) {
    String thisSubtypeNoSuffix = getSubtype().substring(0, thisPlusIdx);
    String thisSubtypeSuffix = getSubtype().substring(thisPlusIdx + 1);
    String otherSubtypeSuffix = other.getSubtype().substring(otherPlusIdx + 1);
    if (thisSubtypeSuffix.equals(otherSubtypeSuffix) && WILDCARD_TYPE.equals(thisSubtypeNoSuffix)) {
    return true;
    }
    }
    }
    }
    }
    return false;
    }
    org.springframework.util.MimeType#includes


    【匹配'Accept' header:严格匹配,还有通配符 】
    /**
    * Checks if any of the contained media type expressions match the given
    * request 'Content-Type' header and returns an instance that is guaranteed
    * to contain matching expressions only. The match is performed via
    * {@link MediaType#isCompatibleWith(MediaType)}.
    * @param request the current request
    * @return the same instance if there are no expressions;
    * or a new condition with matching expressions;
    * or {@code null} if no expressions match.
    */
    @Override
    public ProducesRequestCondition getMatchingCondition(HttpServletRequest request) {
    if (CorsUtils.isPreFlightRequest(request)) {
    return PRE_FLIGHT_MATCH;
    }
    if (isEmpty()) {
    return this;
    }

    List<MediaType> acceptedMediaTypes;
    try {
    acceptedMediaTypes = getAcceptedMediaTypes(request);
    }
    catch (HttpMediaTypeException ex) {
    return null;
    }

    Set<ProduceMediaTypeExpression> result = new LinkedHashSet<ProduceMediaTypeExpression>(expressions);
    for (Iterator<ProduceMediaTypeExpression> iterator = result.iterator(); iterator.hasNext();) {
    ProduceMediaTypeExpression expression = iterator.next();
    if (!expression.match(acceptedMediaTypes)) {
    iterator.remove();
    }
    }
    if (!result.isEmpty()) {
    return new ProducesRequestCondition(result, this.contentNegotiationManager);
    }
    else if (acceptedMediaTypes.contains(MediaType.ALL)) {
    return EMPTY_CONDITION;
    }
    else {
    return null;
    }
    }
    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#mappingRegistry 【存放了请求url的列表信息,可以查看在初始化时,是否就是开关生效的时间】


    获取@RequestMapping中produces参数中存放的mediaType
    private List<MediaType> getAcceptedMediaTypes(HttpServletRequest request) throws HttpMediaTypeNotAcceptableException {
    List<MediaType> mediaTypes = this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
    return mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes;
    }
    org.springframework.web.servlet.mvc.condition.ProducesRequestCondition#getAcceptedMediaTypes

    /**
    * Resolve the given request to a list of media types. The returned list is
    * ordered by specificity first and by quality parameter second.
    * @param webRequest the current request
    * @return the requested media types or an empty list (never {@code null})
    * @throws HttpMediaTypeNotAcceptableException if the requested media
    * types cannot be parsed
    */
    @Override
    public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
    for (ContentNegotiationStrategy strategy : this.strategies) { 【这些strategies是在WebConfig中配置 ignoreedAccepted中配置】
    List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
    if (mediaTypes.isEmpty() || mediaTypes.equals(MEDIA_TYPE_ALL)) {
    continue;
    }
    return mediaTypes;
    }
    return Collections.emptyList();
    }
    org.springframework.web.accept.ContentNegotiationManager#resolveMediaTypes

    上面用到的strategies在此处配置:
    /**
    * Return a {@link ContentNegotiationManager} instance to use to determine
    * requested {@linkplain MediaType media types} in a given request.
    */
    @Bean
    @Override
    public ContentNegotiationManager mvcContentNegotiationManager() {
    ContentNegotiationManager manager = super.mvcContentNegotiationManager();
    List<ContentNegotiationStrategy> strategies = manager.getStrategies();
    ListIterator<ContentNegotiationStrategy> iterator = strategies.listIterator();
    while (iterator.hasNext()) {
    ContentNegotiationStrategy strategy = iterator.next();
    if (strategy instanceof PathExtensionContentNegotiationStrategy) {
    iterator.set(new OptionalPathExtensionContentNegotiationStrategy(
    strategy));
    }
    }
    return manager;
    }
    org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.EnableWebMvcConfiguration#mvcContentNegotiationManager


    package org.springframework.web.accept;
    /**
    * A {@code ContentNegotiationStrategy} that checks the 'Accept' request header.
    *
    * @author Rossen Stoyanchev
    * @author Juergen Hoeller
    * @since 3.2
    */
    public class HeaderContentNegotiationStrategy implements ContentNegotiationStrategy {

    /**
    * {@inheritDoc}
    * @throws HttpMediaTypeNotAcceptableException if the 'Accept' header cannot be parsed
    */
    @Override
    public List<MediaType> resolveMediaTypes(NativeWebRequest request)
    throws HttpMediaTypeNotAcceptableException {

    String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT);
    if (headerValueArray == null) {
    return Collections.<MediaType>emptyList();
    }

    List<String> headerValues = Arrays.asList(headerValueArray);
    try {
    List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerValues);
    MediaType.sortBySpecificityAndQuality(mediaTypes);
    return mediaTypes;
    }
    catch (InvalidMediaTypeException ex) {
    throw new HttpMediaTypeNotAcceptableException(
    "Could not parse 'Accept' header " + headerValues + ": " + ex.getMessage());
    }
    }

    }

    /**
    * Resolve the given request to a list of media types. The returned list is
    * ordered by specificity first and by quality parameter second.
    * @param webRequest the current request
    * @return the requested media types or an empty list (never {@code null})
    * @throws HttpMediaTypeNotAcceptableException if the requested media
    * types cannot be parsed
    */
    @Override
    public List<MediaType> resolveMediaTypes(NativeWebRequest request)
    throws HttpMediaTypeNotAcceptableException {

    String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT);
    if (headerValueArray == null) {
    return Collections.<MediaType>emptyList();
    }

    List<String> headerValues = Arrays.asList(headerValueArray);
    try {
    List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerValues);
    MediaType.sortBySpecificityAndQuality(mediaTypes);
    return mediaTypes;
    }
    catch (InvalidMediaTypeException ex) {
    throw new HttpMediaTypeNotAcceptableException(
    "Could not parse 'Accept' header " + headerValues + ": " + ex.getMessage());
    }
    }
    org.springframework.web.accept.HeaderContentNegotiationStrategy#resolveMediaTypes

    /**
    * Parse the given list of (potentially) comma-separated strings into a
    * list of {@code MediaType} objects.
    * <p>This method can be used to parse an Accept or Content-Type header.
    * @param mediaTypes the string to parse
    * @return the list of media types
    * @throws InvalidMediaTypeException if the media type value cannot be parsed
    * @since 4.3.2
    */
    public static List<MediaType> parseMediaTypes(List<String> mediaTypes) {
    if (CollectionUtils.isEmpty(mediaTypes)) {
    return Collections.<MediaType>emptyList();
    }
    else if (mediaTypes.size() == 1) {
    return parseMediaTypes(mediaTypes.get(0));
    }
    else {
    List<MediaType> result = new ArrayList<MediaType>(8);
    for (String mediaType : mediaTypes) {
    result.addAll(parseMediaTypes(mediaType));
    }
    return result;
    }
    }
    org.springframework.http.MediaType#parseMediaTypes(java.util.List<java.lang.String>)


    /**
    * Parse the given comma-separated string into a list of {@code MediaType} objects.
    * <p>This method can be used to parse an Accept or Content-Type header.
    * @param mediaTypes the string to parse
    * @return the list of media types
    * @throws InvalidMediaTypeException if the media type value cannot be parsed
    */
    public static List<MediaType> parseMediaTypes(String mediaTypes) {
    if (!StringUtils.hasLength(mediaTypes)) {
    return Collections.emptyList();
    }
    String[] tokens = StringUtils.tokenizeToStringArray(mediaTypes, ",");
    List<MediaType> result = new ArrayList<MediaType>(tokens.length);
    for (String token : tokens) {
    result.add(parseMediaType(token));
    }
    return result;
    }
    org.springframework.http.MediaType#parseMediaTypes(java.lang.String)

    /**
    * Sorts the given list of {@code MediaType} objects by specificity as the
    * primary criteria and quality value the secondary.
    * @see MediaType#sortBySpecificity(List)
    * @see MediaType#sortByQualityValue(List)
    */
    public static void sortBySpecificityAndQuality(List<MediaType> mediaTypes) {
    Assert.notNull(mediaTypes, "'mediaTypes' must not be null");
    if (mediaTypes.size() > 1) {
    Collections.sort(mediaTypes, new CompoundComparator<MediaType>(
    MediaType.SPECIFICITY_COMPARATOR, MediaType.QUALITY_VALUE_COMPARATOR)); 【这个用法比较6】
    }
    }
    org.springframework.http.MediaType#sortBySpecificityAndQuality

    //Iterator使用for循环进行遍历;只所以采用iterator进行遍历,是想进行remove操作啊
    Set<ProduceMediaTypeExpression> result = new LinkedHashSet<ProduceMediaTypeExpression>(expressions);
    for (Iterator<ProduceMediaTypeExpression> iterator = result.iterator(); iterator.hasNext();) {
    ProduceMediaTypeExpression expression = iterator.next();
    if (!expression.match(acceptedMediaTypes)) {
    iterator.remove();
    }
    }


    /**
    * Checks if any of the patterns match the given request and returns an instance
    * that is guaranteed to contain matching patterns, sorted via
    * {@link PathMatcher#getPatternComparator(String)}.
    * <p>A matching pattern is obtained by making checks in the following order:
    * <ul>
    * <li>Direct match
    * <li>Pattern match with ".*" appended if the pattern doesn't already contain a "."
    * <li>Pattern match
    * <li>Pattern match with "/" appended if the pattern doesn't already end in "/"
    * </ul>
    * @param request the current request
    * @return the same instance if the condition contains no patterns;
    * or a new condition with sorted matching patterns;
    * or {@code null} if no patterns match.
    */
    @Override
    public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) {

    if (this.patterns.isEmpty()) {
    return this;
    }

    String lookupPath = this.pathHelper.getLookupPathForRequest(request);
    List<String> matches = getMatchingPatterns(lookupPath);

    return matches.isEmpty() ? null :
    new PatternsRequestCondition(matches, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
    this.useTrailingSlashMatch, this.fileExtensions);
    }
    org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingCondition


    /**
    * Decode the supplied URI string and strips any extraneous portion after a ';'.
    */
    private String decodeAndCleanUriString(HttpServletRequest request, String uri) {
    uri = removeSemicolonContent(uri);
    uri = decodeRequestString(request, uri);
    uri = getSanitizedPath(uri);
    return uri;
    }
    org.springframework.web.util.UrlPathHelper#decodeAndCleanUriString


    /**
    * A comparator that chains a sequence of one or more Comparators.
    *
    * <p>A compound comparator calls each Comparator in sequence until a single
    * Comparator returns a non-zero result, or the comparators are exhausted and
    * zero is returned.
    *
    * <p>This facilitates in-memory sorting similar to multi-column sorting in SQL.
    * The order of any single Comparator in the list can also be reversed.
    *
    * @author Keith Donald
    * @author Juergen Hoeller
    * @since 1.2.2
    */
    @SuppressWarnings({"serial", "rawtypes"})
    public class CompoundComparator<T> implements Comparator<T>, Serializable {
    org.springframework.util.comparator.CompoundComparator


    【匹配QueryString :严格匹配,还有通配符 】
    /**
    * Parses and matches a single param expression to a request.
    */
    static class ParamExpression extends AbstractNameValueExpression<String> {

    ParamExpression(String expression) {
    super(expression);
    }

    @Override
    protected boolean isCaseSensitiveName() {
    return true;
    }

    @Override
    protected String parseValue(String valueExpression) {
    return valueExpression;
    }

    @Override
    protected boolean matchName(HttpServletRequest request) {
    return WebUtils.hasSubmitParameter(request, this.name);
    }

    @Override
    protected boolean matchValue(HttpServletRequest request) {
    return ObjectUtils.nullSafeEquals(this.value, request.getParameter(this.name));
    }
    }
    org.springframework.web.servlet.mvc.condition.ParamsRequestCondition.ParamExpression

    /**
    * A logical conjunction (' && ') request condition that matches a request against
    * a set parameter expressions with syntax defined in {@link RequestMapping#params()}.
    *
    * @author Arjen Poutsma
    * @author Rossen Stoyanchev
    * @since 3.1
    */
    public final class ParamsRequestCondition extends AbstractRequestCondition<ParamsRequestCondition> {


    /** Name suffixes in case of image buttons */
    public static final String[] SUBMIT_IMAGE_SUFFIXES = {".x", ".y"};
    org.springframework.web.util.WebUtils#SUBMIT_IMAGE_SUFFIXES

    /**
    * Check if a specific input type="submit" parameter was sent in the request,
    * either via a button (directly with name) or via an image (name + ".x" or
    * name + ".y").
    * @param request current HTTP request
    * @param name name of the parameter
    * @return if the parameter was sent
    * @see #SUBMIT_IMAGE_SUFFIXES
    */
    public static boolean hasSubmitParameter(ServletRequest request, String name) {
    Assert.notNull(request, "Request must not be null");
    if (request.getParameter(name) != null) {
    return true;
    }
    for (String suffix : SUBMIT_IMAGE_SUFFIXES) {
    if (request.getParameter(name + suffix) != null) {
    return true;
    }
    }
    return false;
    }
    org.springframework.web.util.WebUtils#hasSubmitParameter

    // Actually invoke the handler. 【DispatcherServlet - 967】
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    org.springframework.web.servlet.HandlerAdapter#handle


    /**
    * This implementation expects the handler to be an {@link HandlerMethod}.
    */
    @Override
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {

    return handleInternal(request, response, (HandlerMethod) handler);
    }
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle


    /**
    * Use the given handler method to handle the request.
    * @param request current HTTP request
    * @param response current HTTP response
    * @param handlerMethod handler method to use. This object must have previously been passed to the
    * {@link #supportsInternal(HandlerMethod)} this interface, which must have returned {@code true}.
    * @return ModelAndView object with the name of the view and the required model data,
    * or {@code null} if the request has been handled directly
    * @throws Exception in case of errors
    */
    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    checkRequest(request);

    // Execute invokeHandlerMethod in synchronized block if required.
    if (this.synchronizeOnSession) {
    HttpSession session = request.getSession(false);
    if (session != null) {
    Object mutex = WebUtils.getSessionMutex(session);
    synchronized (mutex) {
    mav = invokeHandlerMethod(request, response, handlerMethod);
    }
    }
    else {
    // No HttpSession available -> no mutex necessary
    mav = invokeHandlerMethod(request, response, handlerMethod);
    }
    }
    else {
    // No synchronization on session demanded at all...
    mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
    applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
    }
    else {
    prepareResponse(response);
    }
    }

    return mav;
    }
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal

    /**
    * Check the given request for supported methods and a required session, if any.
    * @param request current HTTP request
    * @throws ServletException if the request cannot be handled because a check failed
    * @since 4.2
    */
    protected final void checkRequest(HttpServletRequest request) throws ServletException {

    org.springframework.web.servlet.support.WebContentGenerator#checkRequest


    /**
    * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
    * if view resolution is required.
    * @since 4.2
    * @see #createInvocableHandlerMethod(HandlerMethod)
    */
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    invocableMethod.setDataBinderFactory(binderFactory);
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

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

    AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
    asyncWebRequest.setTimeout(this.asyncRequestTimeout);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.setTaskExecutor(this.taskExecutor);
    asyncManager.setAsyncWebRequest(asyncWebRequest);
    asyncManager.registerCallableInterceptors(this.callableInterceptors);
    asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

    if (asyncManager.hasConcurrentResult()) {
    Object result = asyncManager.getConcurrentResult();
    mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
    asyncManager.clearConcurrentResult();
    if (logger.isDebugEnabled()) {
    logger.debug("Found concurrent result value [" + result + "]");
    }
    invocableMethod = invocableMethod.wrapConcurrentResult(result);
    }

    invocableMethod.invokeAndHandle(webRequest, mavContainer);
    if (asyncManager.isConcurrentHandlingStarted()) {
    return null;
    }

    return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
    webRequest.requestCompleted();
    }
    }
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod


    /**
    * An {@link AbstractHandlerMethodAdapter} that supports {@link HandlerMethod}s
    * with their method argument and return type signature, as defined via
    * {@code @RequestMapping}.
    *
    * <p>Support for custom argument and return value types can be added via
    * {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}.
    * Or alternatively, to re-configure all argument and return value types,
    * use {@link #setArgumentResolvers} and {@link #setReturnValueHandlers}.
    *
    * @author Rossen Stoyanchev
    * @author Juergen Hoeller
    * @since 3.1
    * @see HandlerMethodArgumentResolver
    * @see HandlerMethodReturnValueHandler
    */
    public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
    implements BeanFactoryAware, InitializingBean {
    。。。
    private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<Class<?>, Set<Method>>(64);
    。。。

    private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
    Class<?> handlerType = handlerMethod.getBeanType(); 【 RequestValidatedDemoController 】
    Set<Method> methods = this.initBinderCache.get(handlerType);
    if (methods == null) {
    methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
    this.initBinderCache.put(handlerType, methods);
    }
    List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
    // Global methods first
    for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
    if (entry.getKey().isApplicableToBeanType(handlerType)) {
    Object bean = entry.getKey().resolveBean();
    for (Method method : entry.getValue()) {
    initBinderMethods.add(createInitBinderMethod(bean, method));
    }
    }
    }
    for (Method method : methods) {
    Object bean = handlerMethod.getBean();
    initBinderMethods.add(createInitBinderMethod(bean, method));
    }
    return createDataBinderFactory(initBinderMethods);
    }
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDataBinderFactory

    Set<Method> methods = this.initBinderCache.get(handlerType);
    0 = {ConcurrentHashMap$MapEntry@11462} "class com.tangcheng.learning.web.api.RequestValidatedDemoController" -> " size = 0"
    1 = {ConcurrentHashMap$MapEntry@11463} "class org.springframework.boot.autoconfigure.web.BasicErrorController" -> " size = 0"
    2 = {ConcurrentHashMap$MapEntry@11464} "class springfox.documentation.swagger.web.ApiResourceController" -> " size = 0"
    3 = {ConcurrentHashMap$MapEntry@11465} "class springfox.documentation.swagger2.web.Swagger2Controller" -> " size = 0"
    4 = {ConcurrentHashMap$MapEntry@11466} "class com.tangcheng.learning.web.controller.IndexController" -> " size = 0"

    /**
    * Template method to create a new InitBinderDataBinderFactory instance.
    * <p>The default implementation creates a ServletRequestDataBinderFactory.
    * This can be overridden for custom ServletRequestDataBinder subclasses.
    * @param binderMethods {@code @InitBinder} methods
    * @return the InitBinderDataBinderFactory instance to use
    * @throws Exception in case of invalid state or arguments
    */
    protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
    throws Exception {

    return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
    }
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#createDataBinderFactory


    /**
    * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
    * if view resolution is required.
    * @since 4.2
    * @see #createInvocableHandlerMethod(HandlerMethod)
    */
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod

    org.springframework.web.method.support.HandlerMethodArgumentResolverComposite
    org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite
    org.springframework.core.DefaultParameterNameDiscoverer


    /**
    * Invoke the method and handle the return value through one of the
    * configured {@link HandlerMethodReturnValueHandler}s.
    * @param webRequest the current request
    * @param mavContainer the ModelAndViewContainer for this request
    * @param providedArgs "given" arguments matched by type (not resolved)
    */
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {

    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); 【获取返回结果】
    setResponseStatus(webRequest); 【 设置@ResponseStatus 注解配置的数据 】

    if (returnValue == null) {
    if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
    mavContainer.setRequestHandled(true);
    return;
    }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) {
    mavContainer.setRequestHandled(true);
    return;
    }

    mavContainer.setRequestHandled(false);
    try {
    this.returnValueHandlers.handleReturnValue(
    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {
    if (logger.isTraceEnabled()) {
    logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
    }
    throw ex;
    }
    }
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle


    /**
    * Set the response status according to the {@link ResponseStatus} annotation.
    */
    private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
    HttpStatus status = getResponseStatus();
    if (status == null) {
    return;
    }

    String reason = getResponseStatusReason();
    if (StringUtils.hasText(reason)) {
    webRequest.getResponse().sendError(status.value(), reason);
    }
    else {
    webRequest.getResponse().setStatus(status.value());
    }

    // To be picked up by RedirectView
    webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
    }
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#setResponseStatus

    private void evaluateResponseStatus() {
    ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
    if (annotation == null) {
    annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
    }
    if (annotation != null) {
    this.responseStatus = annotation.code(); 【 配置的status code 】
    this.responseStatusReason = annotation.reason();
    }
    }
    org.springframework.web.method.HandlerMethod#evaluateResponseStatus


    /**
    * Handle the given return value by adding attributes to the model and
    * setting a view or setting the
    * {@link ModelAndViewContainer#setRequestHandled} flag to {@code true}
    * to indicate the response has been handled directly.
    * @param returnValue the value returned from the handler method
    * @param returnType the type of the return value. This type must have
    * previously been passed to {@link #supportsReturnType} which must
    * have returned {@code true}.
    * @param mavContainer the ModelAndViewContainer for the current request
    * @param webRequest the current request
    * @throws Exception if the return value handling results in an error
    */
    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

    mavContainer.setRequestHandled(true);
    if (returnValue == null) {
    return;
    }

    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

    Assert.isInstanceOf(HttpEntity.class, returnValue);
    HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;

    HttpHeaders outputHeaders = outputMessage.getHeaders();
    HttpHeaders entityHeaders = responseEntity.getHeaders();
    if (!entityHeaders.isEmpty()) {
    for (Map.Entry<String, List<String>> entry : entityHeaders.entrySet()) {
    if (HttpHeaders.VARY.equals(entry.getKey()) && outputHeaders.containsKey(HttpHeaders.VARY)) {
    List<String> values = getVaryRequestHeadersToAdd(outputHeaders, entityHeaders);
    if (!values.isEmpty()) {
    outputHeaders.setVary(values);
    }
    }
    else {
    outputHeaders.put(entry.getKey(), entry.getValue());
    }
    }
    }

    if (responseEntity instanceof ResponseEntity) {
    int returnStatus = ((ResponseEntity<?>) responseEntity).getStatusCodeValue();
    outputMessage.getServletResponse().setStatus(returnStatus);
    if (returnStatus == 200) {
    if (SAFE_METHODS.contains(inputMessage.getMethod())
    && isResourceNotModified(inputMessage, outputMessage)) {
    // Ensure headers are flushed, no body should be written.
    outputMessage.flush();
    // Skip call to converters, as they may update the body.
    return;
    }
    }
    }

    // Try even with null body. ResponseBodyAdvice could get involved.
    writeWithMessageConverters(responseEntity.getBody(), returnType, inputMessage, outputMessage);

    // Ensure headers are flushed even if no body was written.
    outputMessage.flush();
    }
    org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor#handleReturnValue

    /**
    * Invoke the method after resolving its argument values in the context of the given request.
    * <p>Argument values are commonly resolved through {@link HandlerMethodArgumentResolver}s.
    * The {@code providedArgs} parameter however may supply argument values to be used directly,
    * i.e. without argument resolution. Examples of provided argument values include a
    * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
    * Provided argument values are checked before argument resolvers.
    * @param request the current request
    * @param mavContainer the ModelAndViewContainer for this request
    * @param providedArgs "given" arguments matched by type, not resolved
    * @return the raw value returned by the invoked method
    * @throws Exception raised if no suitable argument resolver can be found,
    * or if the method raised an exception
    */
    public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {

    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
    logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
    "' with arguments " + Arrays.toString(args));
    }
    Object returnValue = doInvoke(args);
    if (logger.isTraceEnabled()) {
    logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
    "] returned [" + returnValue + "]");
    }
    return returnValue;
    }
    org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest


    /**
    * Get the method argument values for the current request. 【处理正常请求、处理异常时,都会使用这个方法。哦,对啊,执行ExceptionHandler中的方法和执行Controller中的方法,相关处理逻辑应该是一样的】
    */
    private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {

    MethodParameter[] parameters = getMethodParameters();
    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
    MethodParameter parameter = parameters[i];
    parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
    args[i] = resolveProvidedArgument(parameter, providedArgs);
    if (args[i] != null) {
    continue;
    }
    if (this.argumentResolvers.supportsParameter(parameter)) {
    try {
    args[i] = this.argumentResolvers.resolveArgument(
    parameter, mavContainer, request, this.dataBinderFactory); 【调用参数解析的位置在这个地方 此处会抛捕获MethodArgumentNotValidException异常】
    continue;
    }
    catch (Exception ex) {
    if (logger.isDebugEnabled()) {
    logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
    }
    throw ex;
    }
    }
    if (args[i] == null) {
    throw new IllegalStateException("Could not resolve method parameter at index " +
    parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
    ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
    }
    }
    return args;
    }
    org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues


    /**
    * Make the given method accessible, explicitly setting it accessible if
    * necessary. The {@code setAccessible(true)} method is only called
    * when actually necessary, to avoid unnecessary conflicts with a JVM
    * SecurityManager (if active).
    * @param method the method to make accessible
    * @see java.lang.reflect.Method#setAccessible
    */
    public static void makeAccessible(Method method) {
    if ((!Modifier.isPublic(method.getModifiers()) ||
    !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) {
    method.setAccessible(true);
    }
    }
    org.springframework.util.ReflectionUtils#makeAccessible(java.lang.reflect.Method)


    /**
    * Invoke the handler method with the given argument values.
    */
    protected Object doInvoke(Object... args) throws Exception {
    ReflectionUtils.makeAccessible(getBridgedMethod());
    try {
    return getBridgedMethod().invoke(getBean(), args);
    }
    catch (IllegalArgumentException ex) {
    assertTargetBean(getBridgedMethod(), getBean(), args);
    String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
    throw new IllegalStateException(getInvocationErrorMessage(text, args), ex);
    }
    catch (InvocationTargetException ex) {
    // Unwrap for HandlerExceptionResolvers ...
    Throwable targetException = ex.getTargetException();
    if (targetException instanceof RuntimeException) {
    throw (RuntimeException) targetException;
    }
    else if (targetException instanceof Error) {
    throw (Error) targetException;
    }
    else if (targetException instanceof Exception) {
    throw (Exception) targetException;
    }
    else {
    String text = getInvocationErrorMessage("Failed to invoke handler method", args);
    throw new IllegalStateException(text, targetException);
    }
    }
    }
    org.springframework.web.method.support.InvocableHandlerMethod#doInvoke 【此处是通过反射调用异常处理方法的地方。Controller和ExceptionHandler中都是在此处执行的】


    /**
    * Whether the given {@linkplain MethodParameter method parameter} is supported by any registered
    * {@link HandlerMethodArgumentResolver}.
    */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
    return (getArgumentResolver(parameter) != null);
    }
    org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#supportsParameter

    /**
    * Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
    */
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
    for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
    if (logger.isTraceEnabled()) {
    logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
    parameter.getGenericParameterType() + "]");
    }
    if (methodArgumentResolver.supportsParameter(parameter)) {
    result = methodArgumentResolver;
    this.argumentResolverCache.put(parameter, result);
    break;
    }
    }
    }
    return result;
    }
    org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver


    /**
    * Resolve the argument from the model or if not found instantiate it with
    * its default if it is available. The model attribute is then populated
    * with request values via data binding and optionally validated
    * if {@code @java.validation.Valid} is present on the argument.
    * @throws BindException if data binding and validation result in an error
    * and the next method parameter is not of type {@link Errors}.
    * @throws Exception if WebDataBinder initialization fails.
    */
    @Override
    public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

    String name = ModelFactory.getNameForParameter(parameter);
    Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) :
    createAttribute(name, parameter, binderFactory, webRequest));

    if (!mavContainer.isBindingDisabled(name)) {
    ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); 【ModelAttribute.class注解生效的地方】
    if (ann != null && !ann.binding()) {
    mavContainer.setBindingDisabled(name);
    }
    }

    WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
    if (binder.getTarget() != null) {
    if (!mavContainer.isBindingDisabled(name)) {
    bindRequestParameters(binder, webRequest);
    }
    validateIfApplicable(binder, parameter);
    if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
    throw new BindException(binder.getBindingResult());
    }
    }

    // Add resolved attribute and BindingResult at the end of the model
    Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
    mavContainer.removeAttributes(bindingResultModel);
    mavContainer.addAllAttributes(bindingResultModel);

    return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
    }
    org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument


    /**
    * Actual implementation of the binding process, working with the
    * passed-in MutablePropertyValues instance.
    * @param mpvs the property values to bind,
    * as MutablePropertyValues instance
    * @see #checkAllowedFields
    * @see #checkRequiredFields
    * @see #applyPropertyValues
    */
    protected void doBind(MutablePropertyValues mpvs) {
    checkAllowedFields(mpvs);
    checkRequiredFields(mpvs);
    applyPropertyValues(mpvs);
    }
    org.springframework.validation.DataBinder#doBind

    /**
    * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
    * @throws IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
    */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
    throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
    }
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
    org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument


    /**
    * Resolves method parameters by delegating to a list of registered {@link HandlerMethodArgumentResolver}s.
    * Previously resolved method parameters are cached for faster lookups.
    *
    * @author Rossen Stoyanchev
    * @author Juergen Hoeller
    * @since 3.1
    */
    public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {

    protected final Log logger = LogFactory.getLog(getClass());

    private final List<HandlerMethodArgumentResolver> argumentResolvers =
    new LinkedList<HandlerMethodArgumentResolver>();

    private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
    new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>(256);

    。。。


    /**
    * Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
    */
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
    for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
    if (logger.isTraceEnabled()) {
    logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
    parameter.getGenericParameterType() + "]");
    }
    if (methodArgumentResolver.supportsParameter(parameter)) {
    result = methodArgumentResolver;
    this.argumentResolverCache.put(parameter, result); 【这个List和Map配置使用的方案很6:因为项目都在往微服务走,项目的范围比较单一,常用的argumentResolver是确定的】
    break;
    }
    }
    }
    return result;
    }
    org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver

    /**
    * Throws MethodArgumentNotValidException if validation fails.
    * @throws HttpMessageNotReadableException if {@link RequestBody#required()}
    * is {@code true} and there is no body content or if there is no suitable
    * converter to read the content with.
    */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

    parameter = parameter.nestedIfOptional();
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    String name = Conventions.getVariableNameForParameter(parameter);

    WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
    if (arg != null) {
    validateIfApplicable(binder, parameter);
    if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
    throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); 【 如果参数校验不通过时,抛这个异常:】
    }
    }
    mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());

    return adaptArgumentIfNecessary(arg, parameter);
    }
    org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument

    /**
    * Create the method argument value of the expected parameter type by
    * reading from the given request.
    * @param <T> the expected type of the argument value to be created
    * @param webRequest the current request
    * @param parameter the method parameter descriptor (may be {@code null})
    * @param paramType the type of the argument value to be created
    * @return the created method argument value
    * @throws IOException if the reading from the request fails
    * @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
    */
    @Override
    protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
    Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

    HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);

    Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
    if (arg == null) {
    if (checkRequired(parameter)) {
    throw new HttpMessageNotReadableException("Required request body is missing: " +
    parameter.getMethod().toGenericString());
    }
    }
    return arg;
    }
    org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#readWithMessageConverters

    /**
    * Create the method argument value of the expected parameter type by reading
    * from the given HttpInputMessage.
    * @param <T> the expected type of the argument value to be created
    * @param inputMessage the HTTP input message representing the current request
    * @param parameter the method parameter descriptor (may be {@code null})
    * @param targetType the target type, not necessarily the same as the method
    * parameter type, e.g. for {@code HttpEntity<String>}.
    * @return the created method argument value
    * @throws IOException if the reading from the request fails
    * @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
    */
    @SuppressWarnings("unchecked")
    protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
    Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { 【将request中的数据读出为 JAVA Bean】

    MediaType contentType;
    boolean noContentType = false;
    try {
    contentType = inputMessage.getHeaders().getContentType();
    }
    catch (InvalidMediaTypeException ex) {
    throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    if (contentType == null) {
    noContentType = true;
    contentType = MediaType.APPLICATION_OCTET_STREAM;
    }

    Class<?> contextClass = (parameter != null ? parameter.getContainingClass() : null);
    Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
    if (targetClass == null) {
    ResolvableType resolvableType = (parameter != null ?
    ResolvableType.forMethodParameter(parameter) : ResolvableType.forType(targetType));
    targetClass = (Class<T>) resolvableType.resolve();
    }

    HttpMethod httpMethod = ((HttpRequest) inputMessage).getMethod();
    Object body = NO_VALUE;

    try {
    inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);

    for (HttpMessageConverter<?> converter : this.messageConverters) {
    Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
    if (converter instanceof GenericHttpMessageConverter) {
    GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
    if (genericConverter.canRead(targetType, contextClass, contentType)) {
    if (logger.isDebugEnabled()) {
    logger.debug("Read [" + targetType + "] as "" + contentType + "" with [" + converter + "]");
    }
    if (inputMessage.getBody() != null) {
    inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
    body = genericConverter.read(targetType, contextClass, inputMessage);
    body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
    }
    else {
    body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
    }
    break;
    }
    }
    else if (targetClass != null) {
    if (converter.canRead(targetClass, contentType)) {
    if (logger.isDebugEnabled()) {
    logger.debug("Read [" + targetType + "] as "" + contentType + "" with [" + converter + "]");
    }
    if (inputMessage.getBody() != null) {
    inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
    body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
    body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
    }
    else {
    body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
    }
    break;
    }
    }
    }
    }
    catch (IOException ex) {
    throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
    }

    if (body == NO_VALUE) {
    if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
    (noContentType && inputMessage.getBody() == null)) {
    return null;
    }
    throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
    }

    return body;
    }
    org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(org.springframework.http.HttpInputMessage, org.springframework.core.MethodParameter, java.lang.reflect.Type)

    /**
    * Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
    *
    * @author Arjen Poutsma
    * @author Juergen Hoeller
    * @since 3.0
    */
    public interface HttpMessageConverter<T> {

    /**
    * Indicates whether the given class can be read by this converter.
    * @param clazz the class to test for readability
    * @param mediaType the media type to read (can be {@code null} if not specified);
    * typically the value of a {@code Content-Type} header.
    * @return {@code true} if readable; {@code false} otherwise
    */
    boolean canRead(Class<?> clazz, MediaType mediaType);

    。。。


    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
    return (BufferedImage.class == clazz && isReadable(mediaType));
    }

    private boolean isReadable(MediaType mediaType) {
    if (mediaType == null) {
    return true;
    }
    Iterator<ImageReader> imageReaders = ImageIO.getImageReadersByMIMEType(mediaType.toString());
    return imageReaders.hasNext();
    }
    org.springframework.http.converter.BufferedImageHttpMessageConverter#canRead


    /**
    * A base class for resolving method argument values by reading from the body of
    * a request with {@link HttpMessageConverter}s.
    *
    * @author Arjen Poutsma
    * @author Rossen Stoyanchev
    * @author Juergen Hoeller
    * @since 3.1
    */
    public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {

    private static final Set<HttpMethod> SUPPORTED_METHODS =
    EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);

    private static final Object NO_VALUE = new Object();

    。。。

    /**
    * Validate the binding target if applicable.
    * <p>The default implementation checks for {@code @javax.validation.Valid},
    * Spring's {@link org.springframework.validation.annotation.Validated},
    * and custom annotations whose name starts with "Valid".
    * @param binder the DataBinder to be used
    * @param parameter the method parameter descriptor
    * @since 4.1.5
    * @see #isBindExceptionRequired
    */
    protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { 【校验逻辑在此】
    Annotation[] annotations = parameter.getParameterAnnotations();
    for (Annotation ann : annotations) {
    Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
    if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
    Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
    Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
    binder.validate(validationHints);
    break;
    }
    }
    }
    org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#validateIfApplicable ---》ExtendedServletRequestDataBinder


    package org.springframework.validation;

    /**
    * A validator for application-specific objects.
    *
    * <p>This interface is totally divorced from any infrastructure
    * or context; that is to say it is not coupled to validating
    * only objects in the web tier, the data-access tier, or the
    * whatever-tier. As such it is amenable to being used in any layer
    * of an application, and supports the encapsulation of validation
    * logic as a first-class citizen in its own right.
    *
    * <p>Find below a simple but complete {@code Validator}
    * implementation, which validates that the various {@link String}
    * properties of a {@code UserLogin} instance are not empty
    * (that is they are not {@code null} and do not consist
    * wholly of whitespace), and that any password that is present is
    * at least {@code 'MINIMUM_PASSWORD_LENGTH'} characters in length.
    *
    * <pre class="code"> public class UserLoginValidator implements Validator {
    *
    * private static final int MINIMUM_PASSWORD_LENGTH = 6;
    *
    * public boolean supports(Class clazz) {
    * return UserLogin.class.isAssignableFrom(clazz);
    * }
    *
    * public void validate(Object target, Errors errors) {
    * ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "field.required");
    * ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "field.required");
    * UserLogin login = (UserLogin) target;
    * if (login.getPassword() != null
    * && login.getPassword().trim().length() < MINIMUM_PASSWORD_LENGTH) {
    * errors.rejectValue("password", "field.min.length",
    * new Object[]{Integer.valueOf(MINIMUM_PASSWORD_LENGTH)},
    * "The password must be at least [" + MINIMUM_PASSWORD_LENGTH + "] characters in length.");
    * }
    * }
    * }</pre>
    *
    * <p>See also the Spring reference manual for a fuller discussion of
    * the {@code Validator} interface and its role in an enterprise
    * application.
    *
    * @author Rod Johnson
    * @see Errors
    * @see ValidationUtils
    */
    public interface Validator {

    /**
    * Can this {@link Validator} {@link #validate(Object, Errors) validate}
    * instances of the supplied {@code clazz}?
    * <p>This method is <i>typically</i> implemented like so:
    * <pre class="code">return Foo.class.isAssignableFrom(clazz);</pre>
    * (Where {@code Foo} is the class (or superclass) of the actual
    * object instance that is to be {@link #validate(Object, Errors) validated}.)
    * @param clazz the {@link Class} that this {@link Validator} is
    * being asked if it can {@link #validate(Object, Errors) validate}
    * @return {@code true} if this {@link Validator} can indeed
    * {@link #validate(Object, Errors) validate} instances of the
    * supplied {@code clazz}
    */
    boolean supports(Class<?> clazz);

    /**
    * Validate the supplied {@code target} object, which must be
    * of a {@link Class} for which the {@link #supports(Class)} method
    * typically has (or would) return {@code true}.
    * <p>The supplied {@link Errors errors} instance can be used to report
    * any resulting validation errors.
    * @param target the object that is to be validated (can be {@code null})
    * @param errors contextual state about the validation process (never {@code null})
    * @see ValidationUtils
    */
    void validate(Object target, Errors errors);
    }

    /**
    * Validate the supplied {@code target} object, which must be
    * of a {@link Class} for which the {@link #supports(Class)} method
    * typically has (or would) return {@code true}.
    * <p>The supplied {@link Errors errors} instance can be used to report
    * any resulting validation errors.
    * @param target the object that is to be validated (can be {@code null})
    * @param errors contextual state about the validation process (never {@code null})
    * @see ValidationUtils
    */
    @Override
    public void validate(Object target, Errors errors, Object... validationHints) {
    this.target.validate(target, errors, validationHints);
    }
    org.springframework.boot.autoconfigure.web.WebMvcValidator#validate(java.lang.Object, org.springframework.validation.Errors, java.lang.Object...) 【关键校验逻辑在此】

    org.springframework.validation.beanvalidation.LocalValidatorFactoryBean

    org.springframework.validation.beanvalidation.SpringValidatorAdapter#validate(java.lang.Object, org.springframework.validation.Errors, java.lang.Object...)
    org.springframework.validation.beanvalidation.LocalValidatorFactoryBean【核心在此,调用父类SpringValidatorAdapter的validate方法】

    /**
    * Validates all constraints on {@code object}.
    *
    * @param object object to validate
    * @param groups the group or list of groups targeted for validation (defaults to
    * {@link Default})
    * @return constraint violations or an empty set if none
    * @throws IllegalArgumentException if object is {@code null}
    * or if {@code null} is passed to the varargs groups
    * @throws ValidationException if a non recoverable error happens
    * during the validation process
    */
    @Override
    public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
    Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );

    if ( !beanMetaDataManager.isConstrained( object.getClass() ) ) {
    return Collections.emptySet();
    }

    ValidationOrder validationOrder = determineGroupValidationOrder( groups );
    ValidationContext<T> validationContext = getValidationContext().forValidate( object );

    ValueContext<?, Object> valueContext = ValueContext.getLocalExecutionContext(
    object,
    beanMetaDataManager.getBeanMetaData( object.getClass() ),
    PathImpl.createRootPath()
    );

    return validateInContext( valueContext, validationContext, validationOrder );
    }
    org.hibernate.validator.internal.engine.ValidatorImpl#validate【核心校验逻辑在此】

    /**
    * Validates the given object using the available context information.
    *
    * @param valueContext the current validation context
    * @param context the global validation context
    * @param validationOrder Contains the information which and in which order groups have to be executed
    * @param <T> The root bean type
    *
    * @return Set of constraint violations or the empty set if there were no violations.
    */
    private <T, U> Set<ConstraintViolation<T>> validateInContext(ValueContext<U, Object> valueContext, ValidationContext<T> context, ValidationOrder validationOrder) {
    if ( valueContext.getCurrentBean() == null ) {
    return Collections.emptySet();
    }

    BeanMetaData<U> beanMetaData = beanMetaDataManager.getBeanMetaData( valueContext.getCurrentBeanType() );
    if ( beanMetaData.defaultGroupSequenceIsRedefined() ) {
    validationOrder.assertDefaultGroupSequenceIsExpandable( beanMetaData.getDefaultGroupSequence( valueContext.getCurrentBean() ) );
    }

    // process first single groups. For these we can optimise object traversal by first running all validations on the current bean
    // before traversing the object.
    Iterator<Group> groupIterator = validationOrder.getGroupIterator();
    while ( groupIterator.hasNext() ) {
    Group group = groupIterator.next();
    valueContext.setCurrentGroup( group.getDefiningClass() );
    validateConstraintsForCurrentGroup( context, valueContext );
    if ( shouldFailFast( context ) ) {
    return context.getFailingConstraints();
    }
    }
    groupIterator = validationOrder.getGroupIterator();
    while ( groupIterator.hasNext() ) {
    Group group = groupIterator.next();
    valueContext.setCurrentGroup( group.getDefiningClass() );
    validateCascadedConstraints( context, valueContext );
    if ( shouldFailFast( context ) ) {
    return context.getFailingConstraints();
    }
    }

    // now we process sequences. For sequences I have to traverse the object graph since I have to stop processing when an error occurs.
    Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();
    while ( sequenceIterator.hasNext() ) {
    Sequence sequence = sequenceIterator.next();
    for ( GroupWithInheritance groupOfGroups : sequence ) {
    int numberOfViolations = context.getFailingConstraints().size();

    for ( Group group : groupOfGroups ) {
    valueContext.setCurrentGroup( group.getDefiningClass() );

    validateConstraintsForCurrentGroup( context, valueContext );
    if ( shouldFailFast( context ) ) {
    return context.getFailingConstraints();
    }

    validateCascadedConstraints( context, valueContext );
    if ( shouldFailFast( context ) ) {
    return context.getFailingConstraints();
    }
    }
    if ( context.getFailingConstraints().size() > numberOfViolations ) {
    break;
    }
    }
    }
    return context.getFailingConstraints();
    }
    org.hibernate.validator.internal.engine.ValidatorImpl#validateInContext

    private boolean isValidationRequired(ValidationContext<?> validationContext,
    ValueContext<?, ?> valueContext,
    MetaConstraint<?> metaConstraint) {
    if ( validationContext.hasMetaConstraintBeenProcessed(
    valueContext.getCurrentBean(),
    valueContext.getPropertyPath(),
    metaConstraint
    ) ) {
    return false;
    }

    if ( !metaConstraint.getGroupList().contains( valueContext.getCurrentGroup() ) ) {
    return false;
    }
    return isReachable(
    validationContext,
    valueContext.getCurrentBean(),
    valueContext.getPropertyPath(),
    metaConstraint.getElementType()
    );
    }
    org.hibernate.validator.internal.engine.ValidatorImpl#isValidationRequired


    private boolean validateMetaConstraint(ValidationContext<?> validationContext, ValueContext<?, Object> valueContext, MetaConstraint<?> metaConstraint) {
    if ( isValidationRequired( validationContext, valueContext, metaConstraint ) ) {
    if ( valueContext.getCurrentBean() != null ) {
    Object valueToValidate = getBeanMemberValue(
    valueContext.getCurrentBean(),
    metaConstraint.getLocation().getMember()
    );
    valueContext.setCurrentValidatedValue( valueToValidate );
    }
    return metaConstraint.validateConstraint( validationContext, valueContext );
    }
    return true;
    }
    org.hibernate.validator.internal.engine.ValidatorImpl#validateMetaConstraint 【层级校验的逻辑在此】


    public boolean validateConstraint(ValidationContext<?> executionContext, ValueContext<?, ?> valueContext) {
    valueContext.setElementType( getElementType() );
    valueContext.setDeclaredTypeOfValidatedElement( location.getTypeForValidatorResolution() );

    boolean validationResult = constraintTree.validateConstraints( executionContext, valueContext );
    executionContext.markConstraintProcessed( valueContext.getCurrentBean(), valueContext.getPropertyPath(), this );

    return validationResult;
    }
    org.hibernate.validator.internal.metadata.core.MetaConstraint#validateConstraint 【具体的验证细节】


    【递归校验的核心在此:Validates all composing constraints recursively.】
    /**
    * Validates all composing constraints recursively.
    *
    * @param executionContext Meta data about top level validation
    * @param valueContext Meta data for currently validated value
    * @param constraintViolations Used to accumulate constraint violations
    *
    * @return Returns an instance of {@code CompositionResult} relevant for boolean composition of constraints
    */
    private <T> CompositionResult validateComposingConstraints(ValidationContext<T> executionContext,
    ValueContext<?, ?> valueContext,
    Set<ConstraintViolation<T>> constraintViolations) {
    CompositionResult compositionResult = new CompositionResult( true, false );
    List<ConstraintTree<?>> children = getChildren();
    for ( ConstraintTree<?> tree : children ) {
    Set<ConstraintViolation<T>> tmpViolations = newHashSet();
    tree.validateConstraints( executionContext, valueContext, tmpViolations );
    constraintViolations.addAll( tmpViolations );

    if ( tmpViolations.isEmpty() ) {
    compositionResult.setAtLeastOneTrue( true );
    // no need to further validate constraints, because at least one validation passed
    if ( descriptor.getCompositionType() == OR ) {
    break;
    }
    }
    else {
    compositionResult.setAllTrue( false );
    if ( descriptor.getCompositionType() == AND
    && ( executionContext.isFailFastModeEnabled() || descriptor.isReportAsSingleViolation() ) ) {
    break;
    }
    }
    }
    return compositionResult;
    }
    org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree#validateComposingConstraints


    package org.hibernate.validator.internal.engine.constraintvalidation;
    /**
    * Due to constraint composition a single constraint annotation can lead to a whole constraint tree being validated.
    * This class encapsulates such a tree.
    *
    * @author Hardy Ferentschik
    * @author Federico Mancini
    * @author Dag Hovland
    * @author Kevin Pollet &lt;kevin.pollet@serli.com&gt; (C) 2012 SERLI
    */
    public class ConstraintTree<A extends Annotation> {

    private static final String TYPE_USE = "TYPE_USE";

    private static final Log log = LoggerFactory.make();

    private final ConstraintTree<?> parent;
    private final List<ConstraintTree<?>> children;

    。。。

    private ConstraintTree(ConstraintDescriptorImpl<A> descriptor, ConstraintTree<?> parent) {
    this.parent = parent;
    this.descriptor = descriptor;

    final Set<ConstraintDescriptorImpl<?>> composingConstraints = descriptor.getComposingConstraintImpls();
    children = newArrayList( composingConstraints.size() );

    for ( ConstraintDescriptorImpl<?> composingDescriptor : composingConstraints ) {
    ConstraintTree<?> treeNode = createConstraintTree( composingDescriptor );
    children.add( treeNode );
    }
    }

    。。。

    public final List<ConstraintTree<?>> getChildren() {
    return children;
    }
    org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree#getChildren

    /**
    * Resolves method arguments annotated with {@code @RequestBody} and handles return
    * values from methods annotated with {@code @ResponseBody} by reading and writing
    * to the body of the request or response with an {@link HttpMessageConverter}.
    *
    * <p>An {@code @RequestBody} method argument is also validated if it is annotated
    * with {@code @javax.validation.Valid}. In case of validation failure,
    * {@link MethodArgumentNotValidException} is raised and results in an HTTP 400
    * response status code if {@link DefaultHandlerExceptionResolver} is configured.
    *
    * @author Arjen Poutsma
    * @author Rossen Stoyanchev
    * @author Juergen Hoeller
    * @since 3.1
    */
    public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {


    。。。


    /**
    * Throws MethodArgumentNotValidException if validation fails.
    * @throws HttpMessageNotReadableException if {@link RequestBody#required()}
    * is {@code true} and there is no body content or if there is no suitable
    * converter to read the content with.
    */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument


    /**
    * Return the nested generic type of the method/constructor parameter.
    * @return the parameter type (never {@code null})
    * @since 4.2
    * @see #getNestingLevel()
    */
    public Type getNestedGenericParameterType() {
    if (this.nestingLevel > 1) {
    Type type = getGenericParameterType();
    for (int i = 2; i <= this.nestingLevel; i++) {
    if (type instanceof ParameterizedType) {
    Type[] args = ((ParameterizedType) type).getActualTypeArguments();
    Integer index = getTypeIndexForLevel(i);
    type = args[index != null ? index : args.length - 1];
    }
    }
    return type;
    }
    else {
    return getGenericParameterType();
    }
    }
    org.springframework.core.MethodParameter#getNestedGenericParameterType //可能就是这个方法了,将string参数转换成对象

    /**
    * Create a new {@link WebDataBinder} for the given target object and
    * initialize it through a {@link WebBindingInitializer}.
    * @throws Exception in case of invalid state or arguments
    */
    @Override
    public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
    throws Exception {
    org.springframework.web.bind.support.DefaultDataBinderFactory#createBinder

    org.springframework.web.servlet.handler.SimpleUrlHandlerMapping下生成的handler: org.springframework.web.servlet.resource.ResourceHttpRequestHandler
    org.springframework.web.servlet.handler.AbstractUrlHandlerMapping$PathExposingHandlerInterceptor


    org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(org.springframework.http.HttpInputMessage, org.springframework.core.MethodParameter, java.lang.reflect.Type)

    /**
    * Create the method argument value of the expected parameter type by reading
    * from the given HttpInputMessage.
    * @param <T> the expected type of the argument value to be created
    * @param inputMessage the HTTP input message representing the current request
    * @param parameter the method parameter descriptor (may be {@code null})
    * @param targetType the target type, not necessarily the same as the method
    * parameter type, e.g. for {@code HttpEntity<String>}.
    * @return the created method argument value
    * @throws IOException if the reading from the request fails
    * @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
    */
    @SuppressWarnings("unchecked")
    protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
    Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {


    this.messageConverters:
    0 = {FastJsonHttpMessageConverter@15208}
    1 = {ByteArrayHttpMessageConverter@15217}
    2 = {StringHttpMessageConverter@15218}
    3 = {StringHttpMessageConverter@15219}
    4 = {ResourceHttpMessageConverter@15220}
    5 = {SourceHttpMessageConverter@15221}
    6 = {AllEncompassingFormHttpMessageConverter@15222}
    7 = {MappingJackson2HttpMessageConverter@15223}
    8 = {MappingJackson2HttpMessageConverter@15224}
    9 = {Jaxb2RootElementHttpMessageConverter@15225}

    com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter

    /**
    * A specialization of {@link HttpMessageConverter} that can convert an HTTP request
    * into a target object of a specified generic type and a source object of a specified
    * generic type into an HTTP response.
    *
    * @author Arjen Poutsma
    * @author Rossen Stoyanchev
    * @author Sebastien Deleuze
    * @since 3.2
    * @see org.springframework.core.ParameterizedTypeReference
    */
    public interface GenericHttpMessageConverter<T> extends HttpMessageConverter<T> {

    org.springframework.http.converter.GenericHttpMessageConverter

    public class MimeType implements Comparable<MimeType>, Serializable {
    private static final long serialVersionUID = 4085923477777865903L;
    protected static final String WILDCARD_TYPE = "*";


    org.springframework.boot.autoconfigure.web.WebMvcValidator
    /**
    * A {@link SmartValidator} exposed as a bean for WebMvc use. Wraps existing
    * {@link SpringValidatorAdapter} instances so that only the Spring's {@link Validator}
    * type is exposed. This prevents such a bean to expose both the Spring and JSR-303
    * validator contract at the same time.
    *
    * @author Stephane Nicoll
    * @author Phillip Webb
    */
    class WebMvcValidator implements SmartValidator, ApplicationContextAware,
    InitializingBean, DisposableBean {

    /**
    * Binder that allows for setting property values onto a target object,
    * including support for validation and binding result analysis.
    * The binding process can be customized through specifying allowed fields,
    * required fields, custom editors, etc.
    *
    * <p>Note that there are potential security implications in failing to set an array
    * of allowed fields. In the case of HTTP form POST data for example, malicious clients
    * can attempt to subvert an application by supplying values for fields or properties
    * that do not exist on the form. In some cases this could lead to illegal data being
    * set on command objects <i>or their nested objects</i>. For this reason, it is
    * <b>highly recommended to specify the {@link #setAllowedFields allowedFields} property</b>
    * on the DataBinder.
    *
    * <p>The binding results can be examined via the {@link BindingResult} interface,
    * extending the {@link Errors} interface: see the {@link #getBindingResult()} method.
    * Missing fields and property access exceptions will be converted to {@link FieldError FieldErrors},
    * collected in the Errors instance, using the following error codes:
    *
    * <ul>
    * <li>Missing field error: "required"
    * <li>Type mismatch error: "typeMismatch"
    * <li>Method invocation error: "methodInvocation"
    * </ul>
    *
    * <p>By default, binding errors get resolved through the {@link BindingErrorProcessor}
    * strategy, processing for missing fields and property access exceptions: see the
    * {@link #setBindingErrorProcessor} method. You can override the default strategy
    * if needed, for example to generate different error codes.
    *
    * <p>Custom validation errors can be added afterwards. You will typically want to resolve
    * such error codes into proper user-visible error messages; this can be achieved through
    * resolving each error via a {@link org.springframework.context.MessageSource}, which is
    * able to resolve an {@link ObjectError}/{@link FieldError} through its
    * {@link org.springframework.context.MessageSource#getMessage(org.springframework.context.MessageSourceResolvable, java.util.Locale)}
    * method. The list of message codes can be customized through the {@link MessageCodesResolver}
    * strategy: see the {@link #setMessageCodesResolver} method. {@link DefaultMessageCodesResolver}'s
    * javadoc states details on the default resolution rules.
    *
    * <p>This generic data binder can be used in any kind of environment.
    * It is typically used by Spring web MVC controllers, via the web-specific
    * subclasses {@link org.springframework.web.bind.ServletRequestDataBinder}
    * and {@link org.springframework.web.portlet.bind.PortletRequestDataBinder}.
    *
    * @author Rod Johnson
    * @author Juergen Hoeller
    * @author Rob Harrop
    * @author Stephane Nicoll
    * @author Kazuki Shimizu
    * @see #setAllowedFields
    * @see #setRequiredFields
    * @see #registerCustomEditor
    * @see #setMessageCodesResolver
    * @see #setBindingErrorProcessor
    * @see #bind
    * @see #getBindingResult
    * @see DefaultMessageCodesResolver
    * @see DefaultBindingErrorProcessor
    * @see org.springframework.context.MessageSource
    * @see org.springframework.web.bind.ServletRequestDataBinder
    */
    public class DataBinder implements PropertyEditorRegistry, TypeConverter {


    org.springframework.validation.beanvalidation.SpringValidatorAdapter#validate(java.lang.Object, org.springframework.validation.Errors, java.lang.Object...)


    public final class Class<T> implements java.io.Serializable,
    GenericDeclaration,
    Type,
    AnnotatedElement {


    collection.toArray(new Class<?>[collection.size()]);


    /**
    * This implementation calls {@link #initStrategies}.
    */
    @Override
    protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
    }
    org.springframework.web.servlet.DispatcherServlet#onRefresh


    /**
    * Initialize the strategy objects that this servlet uses.
    * <p>May be overridden in subclasses in order to initialize further strategy objects.
    */
    protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
    }
    org.springframework.web.servlet.DispatcherServlet#initStrategies


    /**
    * Initialize the HandlerExceptionResolver used by this class.
    * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
    * we default to no exception resolver.
    */
    private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null;

    if (this.detectAllHandlerExceptionResolvers) {
    // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
    Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
    .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
    if (!matchingBeans.isEmpty()) {
    this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
    // We keep HandlerExceptionResolvers in sorted order.
    AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
    }
    }
    else {
    try {
    HandlerExceptionResolver her =
    context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
    this.handlerExceptionResolvers = Collections.singletonList(her);
    }
    catch (NoSuchBeanDefinitionException ex) {
    // Ignore, no HandlerExceptionResolver is fine too.
    }
    }

    // Ensure we have at least some HandlerExceptionResolvers, by registering
    // default HandlerExceptionResolvers if no other resolvers are found.
    if (this.handlerExceptionResolvers == null) {
    this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
    if (logger.isDebugEnabled()) {
    logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");
    }
    }
    }
    org.springframework.web.servlet.DispatcherServlet#initHandlerExceptionResolvers

    /**
    * Handle the result of handler selection and handler invocation, which is
    * either a ModelAndView or an Exception to be resolved to a ModelAndView.
    */
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
    HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

    boolean errorView = false;

    if (exception != null) {
    if (exception instanceof ModelAndViewDefiningException) {
    logger.debug("ModelAndViewDefiningException encountered", exception);
    mv = ((ModelAndViewDefiningException) exception).getModelAndView();
    }
    else {
    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
    mv = processHandlerException(request, response, handler, exception);
    errorView = (mv != null);
    }
    }

    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
    render(mv, request, response);
    if (errorView) {
    WebUtils.clearErrorRequestAttributes(request);
    }
    }
    else {
    if (logger.isDebugEnabled()) {
    logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
    "': assuming HandlerAdapter completed request handling");
    }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    // Concurrent handling started during a forward
    return;
    }

    if (mappedHandler != null) {
    mappedHandler.triggerAfterCompletion(request, response, null);
    }
    }
    org.springframework.web.servlet.DispatcherServlet#processDispatchResult


    /**
    * Determine an error ModelAndView via the registered HandlerExceptionResolvers.
    * @param request current HTTP request
    * @param response current HTTP response
    * @param handler the executed handler, or {@code null} if none chosen at the time of the exception
    * (for example, if multipart resolution failed)
    * @param ex the exception that got thrown during handler execution
    * @return a corresponding ModelAndView to forward to
    * @throws Exception if no error ModelAndView found
    */
    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
    Object handler, Exception ex) throws Exception {

    // Check registered HandlerExceptionResolvers...
    ModelAndView exMv = null;
    for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
    exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
    if (exMv != null) {
    break;
    }
    }
    if (exMv != null) {
    if (exMv.isEmpty()) {
    request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
    return null;
    }
    // We might still need view name translation for a plain error model...
    if (!exMv.hasView()) {
    exMv.setViewName(getDefaultViewName(request));
    }
    if (logger.isDebugEnabled()) {
    logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
    }
    WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
    return exMv;
    }

    throw ex;
    }
    org.springframework.web.servlet.DispatcherServlet#processHandlerException

    this.handlerExceptionResolvers :
    [
    org.springframework.boot.autoconfigure.web.DefaultErrorAttributes@68c808ce,
    org.springframework.web.servlet.handler.HandlerExceptionResolverComposite@521981d9
    ]


    /**
    * Resolve the exception by iterating over the list of configured exception resolvers.
    * The first one to return a ModelAndView instance wins. Otherwise {@code null} is returned.
    */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
    Object handler,Exception ex) {

    if (this.resolvers != null) {
    for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
    ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
    if (mav != null) {
    return mav;
    }
    }
    }
    return null;
    }
    org.springframework.web.servlet.handler.HandlerExceptionResolverComposite#resolveException
    this.resolvers :
    result = {ArrayList@16734} size = 3
    0 = {ExceptionHandlerExceptionResolver@16738}
    1 = {ResponseStatusExceptionResolver@16739}
    2 = {DefaultHandlerExceptionResolver@16740}


    /**
    * Check whether this resolver is supposed to apply (i.e. if the supplied handler
    * matches any of the configured {@linkplain #setMappedHandlers handlers} or
    * {@linkplain #setMappedHandlerClasses handler classes}), and then delegate
    * to the {@link #doResolveException} template method.
    */
    @Override
    public ModelAndView resolveException(
    HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

    if (shouldApplyTo(request, handler)) {
    prepareResponse(ex, response);
    ModelAndView result = doResolveException(request, response, handler, ex);
    if (result != null) {
    // Print warn message when warn logger is not enabled...
    if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
    logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
    }
    // warnLogger with full stack trace (requires explicit config)
    logException(ex, request);
    }
    return result;
    }
    else {
    return null;
    }
    }
    org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver#resolveException 【org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver】

    @Override
    protected final ModelAndView doResolveException(
    HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

    return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
    }
    org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver#doResolveException


    /**
    * Abstract base class for
    * {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver}
    * implementations that support handling exceptions from handlers of type {@link HandlerMethod}.
    *
    * @author Rossen Stoyanchev
    * @since 3.1
    */
    public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {

    /**
    * Checks if the handler is a {@link HandlerMethod} and then delegates to the
    * base class implementation of {@code #shouldApplyTo(HttpServletRequest, Object)}
    * passing the bean of the {@code HandlerMethod}. Otherwise returns {@code false}.
    */
    @Override
    protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
    if (handler == null) {
    return super.shouldApplyTo(request, handler);
    }
    else if (handler instanceof HandlerMethod) {
    HandlerMethod handlerMethod = (HandlerMethod) handler;
    handler = handlerMethod.getBean(); 【HandlerMethod中获取具体bean的办法】
    return super.shouldApplyTo(request, handler);
    }
    else {
    return false;
    }
    }

    。。。

    }
    org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver#shouldApplyTo 【org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver】


    /**
    * Check whether this resolver is supposed to apply to the given handler.
    * <p>The default implementation checks against the configured
    * {@linkplain #setMappedHandlers handlers} and
    * {@linkplain #setMappedHandlerClasses handler classes}, if any.
    * @param request current HTTP request
    * @param handler the executed handler, or {@code null} if none chosen
    * at the time of the exception (for example, if multipart resolution failed)
    * @return whether this resolved should proceed with resolving the exception
    * for the given request and handler
    * @see #setMappedHandlers
    * @see #setMappedHandlerClasses
    */
    protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
    if (handler != null) {
    if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
    return true;
    }
    if (this.mappedHandlerClasses != null) {
    for (Class<?> handlerClass : this.mappedHandlerClasses) {
    if (handlerClass.isInstance(handler)) {
    return true;
    }
    }
    }
    }
    // Else only apply if there are no explicit handler mappings.
    return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
    }
    org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver#shouldApplyTo


    /**
    * Prepare the response for the exceptional case.
    * <p>The default implementation prevents the response from being cached,
    * if the {@link #setPreventResponseCaching "preventResponseCaching"} property
    * has been set to "true".
    * @param ex the exception that got thrown during handler execution
    * @param response current HTTP response
    * @see #preventCaching
    */
    protected void prepareResponse(Exception ex, HttpServletResponse response) {
    if (this.preventResponseCaching) {
    preventCaching(response);
    }
    }
    org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver#prepareResponse 【org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver】

    /**
    * An {@link AbstractHandlerMethodExceptionResolver} that resolves exceptions
    * through {@code @ExceptionHandler} methods.
    *
    * <p>Support for custom argument and return value types can be added via
    * {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}.
    * Or alternatively to re-configure all argument and return value types use
    * {@link #setArgumentResolvers} and {@link #setReturnValueHandlers(List)}.
    *
    * @author Rossen Stoyanchev
    * @author Juergen Hoeller
    * @since 3.1
    */
    public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
    implements ApplicationContextAware, InitializingBean {

    。。。

    /**
    * Find an {@code @ExceptionHandler} method and invoke it to handle the raised exception.
    */
    @Override
    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
    HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {

    ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
    if (exceptionHandlerMethod == null) {
    return null;
    }

    exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();

    try {
    if (logger.isDebugEnabled()) {
    logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
    }
    Throwable cause = exception.getCause();
    if (cause != null) {
    // Expose cause as provided argument as well
    exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
    }
    else {
    // Otherwise, just the given exception as-is
    exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
    }
    }
    catch (Throwable invocationEx) {
    // Any other than the original exception is unintended here,
    // probably an accident (e.g. failed assertion or the like).
    if (invocationEx != exception && logger.isWarnEnabled()) {
    logger.warn("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
    }
    // Continue with default processing of the original exception...
    return null;
    }

    if (mavContainer.isRequestHandled()) {
    return new ModelAndView();
    }
    else {
    ModelMap model = mavContainer.getModel();
    HttpStatus status = mavContainer.getStatus();
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
    mav.setViewName(mavContainer.getViewName());
    if (!mavContainer.isViewReference()) {
    mav.setView((View) mavContainer.getView());
    }
    if (model instanceof RedirectAttributes) {
    Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
    RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
    }
    return mav;
    }
    }
    this.returnValueHandlers = {HandlerMethodReturnValueHandlerComposite@9390}
    logger = {SLF4JLocationAwareLog@9676}
    returnValueHandlers = {ArrayList@9677} size = 9
    0 = {ModelAndViewMethodReturnValueHandler@9679}
    1 = {ModelMethodProcessor@9680}
    2 = {ViewMethodReturnValueHandler@9681}
    3 = {HttpEntityMethodProcessor@9682}
    4 = {ModelAttributeMethodProcessor@9683}
    5 = {RequestResponseBodyMethodProcessor@9684}
    6 = {ViewNameMethodReturnValueHandler@9685}
    7 = {MapMethodProcessor@9686}
    8 = {ModelAttributeMethodProcessor@9687}
    this.argumentResolvers = {HandlerMethodArgumentResolverComposite@9389}
    logger = {SLF4JLocationAwareLog@9663}
    argumentResolvers = {LinkedList@9664} size = 9
    0 = {SessionAttributeMethodArgumentResolver@9667}
    1 = {RequestAttributeMethodArgumentResolver@9668}
    2 = {ServletRequestMethodArgumentResolver@9669}
    3 = {ServletResponseMethodArgumentResolver@9670}
    4 = {RedirectAttributesMethodArgumentResolver@9671}
    5 = {ModelMethodProcessor@9672}
    6 = {SortHandlerMethodArgumentResolver@9673} 【jpa排序】
    7 = {PageableHandlerMethodArgumentResolver@9674} 【jpa分页】
    8 = {ProxyingHandlerMethodArgumentResolver@9675}
    argumentResolverCache = {ConcurrentHashMap@9665} size = 0

    。。。


    /**
    * Find an {@code @ExceptionHandler} method for the given exception. The default
    * implementation searches methods in the class hierarchy of the controller first
    * and if not found, it continues searching for additional {@code @ExceptionHandler}
    * methods assuming some {@linkplain ControllerAdvice @ControllerAdvice}
    * Spring-managed beans were detected.
    * @param handlerMethod the method where the exception was raised (may be {@code null})
    * @param exception the raised exception
    * @return a method to handle the exception, or {@code null} if none
    */
    protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
    Class<?> handlerType = null;

    if (handlerMethod != null) {
    // Local exception handler methods on the controller class itself.
    // To be invoked through the proxy, even in case of an interface-based proxy.
    handlerType = handlerMethod.getBeanType();
    ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
    if (resolver == null) {
    resolver = new ExceptionHandlerMethodResolver(handlerType);
    this.exceptionHandlerCache.put(handlerType, resolver);
    }
    Method method = resolver.resolveMethod(exception);
    if (method != null) {
    return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
    }
    // For advice applicability check below (involving base packages, assignable types
    // and annotation presence), use target class instead of interface-based proxy.
    if (Proxy.isProxyClass(handlerType)) {
    handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
    }
    }

    for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) { 【自定义的存在这个地方】
    ControllerAdviceBean advice = entry.getKey();
    if (advice.isApplicableToBeanType(handlerType)) {
    ExceptionHandlerMethodResolver resolver = entry.getValue();
    Method method = resolver.resolveMethod(exception);
    if (method != null) {
    return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
    }
    }
    }

    return null;
    }
    org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#doResolveHandlerMethodException


    this.exceptionHandlerAdviceCache.entrySet() :
    0 = {LinkedHashMap$Entry@9474} "globalExceptionHandler" ->
    key = {ControllerAdviceBean@9475} "globalExceptionHandler"
    value = {ExceptionHandlerMethodResolver@9476}
    mappedMethods = {ConcurrentHashMap@9506} size = 6
    0 = {ConcurrentHashMap$MapEntry@9510} "class org.springframework.validation.BindException" -> "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleBindException(org.springframework.validation.BindException)"
    1 = {ConcurrentHashMap$MapEntry@9511} "class java.lang.IllegalArgumentException" -> "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleIllegalArgumentException(java.lang.IllegalArgumentException)"
    2 = {ConcurrentHashMap$MapEntry@9512} "class org.springframework.web.bind.MethodArgumentNotValidException" -> "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleMethodArgumentNotValidException(org.springframework.web.bind.MethodArgumentNotValidException)"
    3 = {ConcurrentHashMap$MapEntry@9513} "class org.springframework.http.converter.HttpMessageNotReadableException" -> "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleHttpMessageNotReadableException(org.springframework.http.converter.HttpMessageNotReadableException)"
    4 = {ConcurrentHashMap$MapEntry@9514} "class org.json.JSONException" -> "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleJsonException(org.json.JSONException)"
    5 = {ConcurrentHashMap$MapEntry@9515} "class org.springframework.web.bind.ServletRequestBindingException" -> "public java.lang.Object com.tangcheng.learning.web.config.GlobalExceptionHandler.exception(javax.servlet.http.HttpServletRequest,org.springframework.web.bind.ServletRequestBindingException)"
    exceptionLookupCache = {ConcurrentHashMap@9507} size = 0

    /**
    * Check whether the given bean type should be assisted by this
    * {@code @ControllerAdvice} instance.
    * @param beanType the type of the bean to check
    * @see org.springframework.web.bind.annotation.ControllerAdvice
    * @since 4.0
    */
    public boolean isApplicableToBeanType(Class<?> beanType) {
    if (!hasSelectors()) {
    return true;
    }
    else if (beanType != null) {
    for (String basePackage : this.basePackages) {
    if (beanType.getName().startsWith(basePackage)) {
    return true;
    }
    }
    for (Class<?> clazz : this.assignableTypes) {
    if (ClassUtils.isAssignable(clazz, beanType)) {
    return true;
    }
    }
    for (Class<? extends Annotation> annotationClass : this.annotations) {
    if (AnnotationUtils.findAnnotation(beanType, annotationClass) != null) {
    return true;
    }
    }
    }
    return false;
    }
    org.springframework.web.method.ControllerAdviceBean#isApplicableToBeanType

    /**
    * Find a {@link Method} to handle the given exception.
    * Use {@link ExceptionDepthComparator} if more than one match is found.
    * @param exception the exception
    * @return a Method to handle the exception, or {@code null} if none found
    */
    public Method resolveMethod(Exception exception) {
    Method method = resolveMethodByExceptionType(exception.getClass());
    if (method == null) {
    Throwable cause = exception.getCause();
    if (cause != null) {
    method = resolveMethodByExceptionType(cause.getClass());
    }
    }
    return method;
    }
    org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#resolveMethod


    /**
    * Return the {@link Method} mapped to the given exception type, or {@code null} if none.
    */
    private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
    List<Class<? extends Throwable>> matches = new ArrayList<Class<? extends Throwable>>();
    for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
    if (mappedException.isAssignableFrom(exceptionType)) {
    matches.add(mappedException);
    }
    }
    if (!matches.isEmpty()) {
    Collections.sort(matches, new ExceptionDepthComparator(exceptionType));
    return this.mappedMethods.get(matches.get(0));
    }
    else {
    return null;
    }
    }
    org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#getMappedMethod

    private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
    Method oldMethod = this.mappedMethods.put(exceptionType, method);
    if (oldMethod != null && !oldMethod.equals(method)) {
    throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
    exceptionType + "]: {" + oldMethod + ", " + method + "}");
    }
    }
    org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#addExceptionMapping


    /**
    * A constructor that finds {@link ExceptionHandler} methods in the given type.
    * @param handlerType the type to introspect
    */
    public ExceptionHandlerMethodResolver(Class<?> handlerType) {
    for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
    for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
    addExceptionMapping(exceptionType, method);
    }
    }
    }
    org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#ExceptionHandlerMethodResolver


    /**
    * A filter for selecting {@code @ExceptionHandler} methods.
    */
    public static final MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {
    @Override
    public boolean matches(Method method) {
    return (AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null);
    }
    };
    org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#EXCEPTION_HANDLER_METHODS 【 ExceptionHandler.class 注解生效的地方】


    /**
    * Defines the algorithm for searching for metadata-associated methods exhaustively
    * including interfaces and parent classes while also dealing with parameterized methods
    * as well as common scenarios encountered with interface and class-based proxies.
    *
    * <p>Typically, but not necessarily, used for finding annotated handler methods.
    *
    * @author Juergen Hoeller
    * @author Rossen Stoyanchev
    * @since 4.2.3
    */
    public abstract class MethodIntrospector {

    。。。

    /**
    * Select methods on the given target type based on a filter.
    * <p>Callers define methods of interest through the {@code MethodFilter} parameter.
    * @param targetType the target type to search methods on
    * @param methodFilter a {@code MethodFilter} to help
    * recognize handler methods of interest
    * @return the selected methods, or an empty set in case of no match
    */
    public static Set<Method> selectMethods(Class<?> targetType, final ReflectionUtils.MethodFilter methodFilter) {
    return selectMethods(targetType, new MetadataLookup<Boolean>() {
    @Override
    public Boolean inspect(Method method) {
    return (methodFilter.matches(method) ? Boolean.TRUE : null);
    }
    }).keySet();
    }
    org.springframework.core.MethodIntrospector#selectMethods(java.lang.Class<?>, org.springframework.util.ReflectionUtils.MethodFilter)


    /**
    * Extract exception mappings from the {@code @ExceptionHandler} annotation first,
    * and then as a fallback from the method signature itself.
    */
    @SuppressWarnings("unchecked")
    private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
    List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
    detectAnnotationExceptionMappings(method, result);
    if (result.isEmpty()) {
    for (Class<?> paramType : method.getParameterTypes()) {
    if (Throwable.class.isAssignableFrom(paramType)) {
    result.add((Class<? extends Throwable>) paramType);
    }
    }
    }
    if (result.isEmpty()) {
    throw new IllegalStateException("No exception types mapped to " + method);
    }
    return result;
    }
    org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#detectExceptionMappings

    protected void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
    ExceptionHandler ann = AnnotationUtils.findAnnotation(method, ExceptionHandler.class);
    result.addAll(Arrays.asList(ann.value()));
    }
    org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#detectAnnotationExceptionMappings

    org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#mappedMethods
    private final Map<Class<? extends Throwable>, Method> mappedMethods =
    new ConcurrentHashMap<Class<? extends Throwable>, Method>(16);
    this.mappedMethods :
    result = {ConcurrentHashMap@9506} size = 6
    0 = {ConcurrentHashMap$MapEntry@9573} "class org.springframework.validation.BindException" -> "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleBindException(org.springframework.validation.BindException)"
    key = {Class@9581} "class org.springframework.validation.BindException"
    value = {Method@9582} "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleBindException(org.springframework.validation.BindException)"
    1 = {ConcurrentHashMap$MapEntry@9574} "class java.lang.IllegalArgumentException" -> "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleIllegalArgumentException(java.lang.IllegalArgumentException)"
    key = {Class@64} "class java.lang.IllegalArgumentException"
    value = {Method@9583} "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleIllegalArgumentException(java.lang.IllegalArgumentException)"
    2 = {ConcurrentHashMap$MapEntry@9575} "class org.springframework.web.bind.MethodArgumentNotValidException" -> "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleMethodArgumentNotValidException(org.springframework.web.bind.MethodArgumentNotValidException)"
    key = {Class@9226} "class org.springframework.web.bind.MethodArgumentNotValidException"
    value = {Method@9519} "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleMethodArgumentNotValidException(org.springframework.web.bind.MethodArgumentNotValidException)"
    3 = {ConcurrentHashMap$MapEntry@9576} "class org.springframework.http.converter.HttpMessageNotReadableException" -> "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleHttpMessageNotReadableException(org.springframework.http.converter.HttpMessageNotReadableException)"
    key = {Class@9584} "class org.springframework.http.converter.HttpMessageNotReadableException"
    value = {Method@9585} "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleHttpMessageNotReadableException(org.springframework.http.converter.HttpMessageNotReadableException)"
    4 = {ConcurrentHashMap$MapEntry@9577} "class org.json.JSONException" -> "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleJsonException(org.json.JSONException)"
    key = {Class@9586} "class org.json.JSONException"
    value = {Method@9587} "public org.springframework.http.ResponseEntity com.tangcheng.learning.web.config.GlobalExceptionHandler.handleJsonException(org.json.JSONException)"
    5 = {ConcurrentHashMap$MapEntry@9578} "class org.springframework.web.bind.ServletRequestBindingException" -> "public java.lang.Object com.tangcheng.learning.web.config.GlobalExceptionHandler.exception(javax.servlet.http.HttpServletRequest,org.springframework.web.bind.ServletRequestBindingException)"
    key = {Class@9588} "class org.springframework.web.bind.ServletRequestBindingException"
    value = {Method@9589} "public java.lang.Object com.tangcheng.learning.web.config.GlobalExceptionHandler.exception(javax.servlet.http.HttpServletRequest,org.springframework.web.bind.ServletRequestBindingException)"


    package org.springframework.web.servlet;
    /**
    * Holder for both Model and View in the web MVC framework.
    * Note that these are entirely distinct. This class merely holds
    * both to make it possible for a controller to return both model
    * and view in a single return value.
    *
    * <p>Represents a model and view returned by a handler, to be resolved
    * by a DispatcherServlet. The view can take the form of a String
    * view name which will need to be resolved by a ViewResolver object;
    * alternatively a View object can be specified directly. The model
    * is a Map, allowing the use of multiple objects keyed by name.
    *
    * @author Rod Johnson
    * @author Juergen Hoeller
    * @author Rob Harrop
    * @author Rossen Stoyanchev
    * @see DispatcherServlet
    * @see ViewResolver
    * @see HandlerAdapter#handle
    * @see org.springframework.web.servlet.mvc.Controller#handleRequest
    */
    public class ModelAndView {

    /** View instance or view name String */
    private Object view;

    /** Model Map */
    private ModelMap model;

    /** Optional HTTP status for the response */
    private HttpStatus status;

    /** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */
    private boolean cleared = false;

    。。。

    /**
    * Return whether this ModelAndView object is empty,
    * i.e. whether it does not hold any view and does not contain a model.
    */
    public boolean isEmpty() {
    return (this.view == null && CollectionUtils.isEmpty(this.model));
    }
    org.springframework.web.servlet.ModelAndView#isEmpty


    org.springframework.web.bind.MethodArgumentNotValidException


    org.springframework.web.method.HandlerMethod
    org.springframework.web.servlet.HandlerExecutionChain


    package org.springframework.boot.web.filter;
    /**
    * {@link OncePerRequestFilter} to add a {@literal X-Application-Context} header that
    * contains the {@link ApplicationContext#getId() ApplicationContext ID}.
    *
    * @author Phillip Webb
    * @author Venil Noronha
    * @since 1.4.0
    */
    public class ApplicationContextHeaderFilter extends OncePerRequestFilter {

    /**
    * Public constant for {@literal X-Application-Context}.
    */
    public static final String HEADER_NAME = "X-Application-Context";

    private final ApplicationContext applicationContext;

    public ApplicationContextHeaderFilter(ApplicationContext context) {
    this.applicationContext = context;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
    HttpServletResponse response, FilterChain filterChain)
    throws ServletException, IOException {
    response.addHeader(HEADER_NAME, this.applicationContext.getId());
    filterChain.doFilter(request, response);
    }

    }
    org.springframework.boot.web.filter.ApplicationContextHeaderFilter#doFilterInternal

    /**
    * Servlet {@link Filter} that logs all requests to a {@link TraceRepository}.
    *
    * @author Dave Syer
    * @author Wallace Wadge
    * @author Andy Wilkinson
    * @author Venil Noronha
    * @author Madhura Bhave
    */
    public class WebRequestTraceFilter extends OncePerRequestFilter implements Ordered {

    private static final Log logger = LogFactory.getLog(WebRequestTraceFilter.class);

    private boolean dumpRequests = false;

    // Not LOWEST_PRECEDENCE, but near the end, so it has a good chance of catching all
    // enriched headers, but users can add stuff after this if they want to
    private int order = Ordered.LOWEST_PRECEDENCE - 10;

    private final TraceRepository repository;

    private ErrorAttributes errorAttributes;

    private final TraceProperties properties;

    /**
    * Create a new {@link WebRequestTraceFilter} instance.
    * @param repository the trace repository
    * @param properties the trace properties
    */
    public WebRequestTraceFilter(TraceRepository repository, TraceProperties properties) {
    this.repository = repository;
    this.properties = properties;
    }

    /**
    * Debugging feature. If enabled, and trace logging is enabled then web request
    * headers will be logged.
    * @param dumpRequests if requests should be logged
    */
    public void setDumpRequests(boolean dumpRequests) {
    this.dumpRequests = dumpRequests;
    }

    @Override
    public int getOrder() {
    return this.order;
    }

    public void setOrder(int order) {
    this.order = order;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
    HttpServletResponse response, FilterChain filterChain)
    throws ServletException, IOException {
    long startTime = System.nanoTime();
    Map<String, Object> trace = getTrace(request);
    logTrace(request, trace);
    int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
    try {
    filterChain.doFilter(request, response);
    status = response.getStatus();
    }
    finally {
    addTimeTaken(trace, startTime);
    addSessionIdIfNecessary(request, trace);
    enhanceTrace(trace, (status != response.getStatus())
    ? new CustomStatusResponseWrapper(response, status) : response);
    this.repository.add(trace);
    }
    }
    org.springframework.boot.actuate.trace.WebRequestTraceFilter#doFilterInternal


    private void addTimeTaken(Map<String, Object> trace, long startTime) {
    long timeTaken = System.nanoTime() - startTime;
    if (isIncluded(Include.TIME_TAKEN)) {
    add(trace, "timeTaken", "" + TimeUnit.NANOSECONDS.toMillis(timeTaken));
    }
    }
    org.springframework.boot.actuate.trace.WebRequestTraceFilter#addTimeTaken


    package org.springframework.boot.actuate.trace;
    /**
    * In-memory implementation of {@link TraceRepository}.
    *
    * @author Dave Syer
    * @author Olivier Bourgain
    */
    public class InMemoryTraceRepository implements TraceRepository {

    private int capacity = 100;

    private boolean reverse = true;

    private final List<Trace> traces = new LinkedList<Trace>();

    /**
    * Flag to say that the repository lists traces in reverse order.
    * @param reverse flag value (default true)
    */
    public void setReverse(boolean reverse) {
    synchronized (this.traces) {
    this.reverse = reverse;
    }
    }

    /**
    * Set the capacity of the in-memory repository.
    * @param capacity the capacity
    */
    public void setCapacity(int capacity) {
    synchronized (this.traces) {
    this.capacity = capacity;
    }
    }

    @Override
    public List<Trace> findAll() {
    synchronized (this.traces) {
    return Collections.unmodifiableList(new ArrayList<Trace>(this.traces));
    }
    }

    @Override
    public void add(Map<String, Object> map) {
    Trace trace = new Trace(new Date(), map);
    synchronized (this.traces) {
    while (this.traces.size() >= this.capacity) {
    this.traces.remove(this.reverse ? this.capacity - 1 : 0);
    }
    if (this.reverse) {
    this.traces.add(0, trace);
    }
    else {
    this.traces.add(trace);
    }
    }
    }

    }


    package org.springframework.beans.factory;
    /**
    * Interface to be implemented by beans that need to react once all their properties
    * have been set by a {@link BeanFactory}: e.g. to perform custom initialization,
    * or merely to check that all mandatory properties have been set.
    *
    * <p>An alternative to implementing {@code InitializingBean} is specifying a custom
    * init method, for example in an XML bean definition. For a list of all bean
    * lifecycle methods, see the {@link BeanFactory BeanFactory javadocs}.
    *
    * @author Rod Johnson
    * @author Juergen Hoeller
    * @see DisposableBean
    * @see org.springframework.beans.factory.config.BeanDefinition#getPropertyValues()
    * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getInitMethodName()
    */
    public interface InitializingBean {

    /**
    * Invoked by the containing {@code BeanFactory} after it has set all bean properties
    * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
    * <p>This method allows the bean instance to perform validation of its overall
    * configuration and final initialization when all bean properties have been set.
    * @throws Exception in the event of misconfiguration (such as failure to set an
    * essential property) or if initialization fails for any other reason
    */
    void afterPropertiesSet() throws Exception;
    }

    口碑是你为消费者设计的,让消费者讲的话

    召唤出很好的情感

    如果使用权力和金钱做为杠杆,召唤出什么情感


    你掌握了与它连接的关键。情感,温度


    用户不需要思考
    降低思考门槛

    确定性的满足

    成本控制不能超过用户的忍耐底线。7s


    数据是水管道里流的水,是一个常态,是一个结果
    建立一个有代表性的故事


    想要个打火机,还是想在墙上打个洞
    吃饭呢,还是要个氛围,还是交流的机会


    不要用一些让用户不明觉里,要用用户故事的方式


    大明:忠诚度只有10元,如果超过10元,那就不好意思,我要选一个便宜的


    套路:做事情的方式。完整的框架,团队沟通对齐。更有章法的展示的自己,搞定面试官
    做菜谱做菜

    大公司会让你进行角色化生存


    市场竞争和用户分流

    org.springframework.boot.autoconfigure.web.WebMvcValidator

    纸上谈兵,看了好多书也好。
    听着都是对的, 一做,总是不到位


    长在自己身上的微观体感

    org.springframework.validation.beanvalidation.LocalValidatorFactoryBean

    org.hibernate.validator.internal.engine.ValidatorImpl

    /**
    * Copy the given {@code Collection} into a {@code Class} array.
    * <p>The {@code Collection} must contain {@code Class} elements only.
    * @param collection the {@code Collection} to copy
    * @return the {@code Class} array
    * @since 3.1
    * @see StringUtils#toStringArray
    */
    public static Class<?>[] toClassArray(Collection<Class<?>> collection) {
    if (collection == null) {
    return null;
    }
    return collection.toArray(new Class<?>[collection.size()]);
    }

    SpringValidatorAdapter.java

    自我矮化成猪,为了时刻提醒自己

    hibernate 的内置校验数据全部存储这个全局对象中
    org.hibernate.validator.internal.metadata.BeanMetaDataManager

    共同利益绑在一起,只有共同利益。对自己的价值认定也不一定。
    确定的依赖感


    确定的感觉可以给人治疗的感觉

    确定感--》可以依赖

    确定感消失--》会有情感被伤害了的情绪


    对方要的确定性,你还能不能提供

    专业化的能力,专业化的视角

    以互联网为基础设施的高科技公司


    动动手,你所在的小世界,就有一个小的优化的点。
    向世界交付你的价值


    有点开心,有点意犹未尽

    思考框架,应用的案例

    你来泄密,我来分析


    成功之处

    为什么大家喜欢你? 漂亮就行

    念念不忘的,不一定是你见过的最美丽的

    确定--》依赖
    不确定--》伤害


    挤压、围观

    不会念你的名字。 你想让你的名字口口相传,就先让别人会念


    名:你抓住了它的特征,你就可以和他交流
    名:咒--》可以召唤出某种情感


    叫让别人看不懂的名字,是为了隔绝--》隔离措施

    口碑,就是把事情做过头
    用户已经有了,想实际自己或存在侥幸心理

    基于固定存量的竞争
    新体验-旧体验-替换成本

    替换成本基本为0【体量大的】
    用户体验、品牌认知、渠道方便、学习成本【你已经把用户教育好了】

    先占据,再一点点提升用户体验,再一点点挤压你
    两个人拼拳脚,体量大的胜

    IP就是情感触发,就是场景,就是流量。IP就是新流量
    实体生意就这四件事:产品、空间、流量、转化率


    股东成为流量,IP成为流量
    众筹酒店这个杠杆


    地段自带流量,IP也自带流量

    最能给你

    你是你一切社会关系的总合

  • 相关阅读:
    Git ---游离状态下的commit 分支切换与找回,commit之后无法找到历史记录
    mybatis异常invalid comparison: java.util.Date and java.lang.String
    Spring的注解@Qualifier
    Spring @Bean注解的使用
    Java的IO系统
    linkin大话面向对象--GC和jar包
    linkin大话面向对象--闭包和回调
    linkin大话面向对象--内部类
    linkin大话面向对象--枚举
    linkin大话面向对象--接口
  • 原文地址:https://www.cnblogs.com/softidea/p/10120976.html
Copyright © 2020-2023  润新知