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:\Users\TANGCH~1\AppData\Local\Temp\tomcat-docbase.8903617127552296966.8080
C:\Users\TANGCH~1\AppData\Local\Temp\tomcat-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也自带流量

最能给你

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

 

转载于:https://www.cnblogs.com/softidea/p/10120976.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值