源码剖析-【HandlerAdapter handle 方法解析】
1.handleInternal方法简析
前面分析了SpringMVC获取handler及HandlerAdapter的过程,接下来就要真正开始处理Controller了。
以AbstractHandlerMethodAdapter为例来来分析一下其具体的处理过程。
在此过程中会包含SpringMVC流程处理的的关键部分。例如参数获取及解析、异步处理、调用Controller中的方法、返回视图等等
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response,
HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 1.检测当前请求,验证请求方法合法性和session合法性
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 2.根据synchronizeOnSession值判断,当前请求是否需串行化访问。
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
// 获取最佳互斥锁,即同步当前回话对象;如未能获取到互斥锁,将返回HttpSession对象本身
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
// 即无最佳互斥锁,也未能获取到HttpSession,则当前回话无需串行化访问
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// 3.相应信息不包含Cache-Control
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
这里会涉及到一部分异步操作的代码。具体的处理方法委托给了invokeHandlerMethod方法。
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//WebDataBinderFactory --> 工厂类,为目标对象创建一个WebDataBinder实例
// 1.WebDataBinder继承了DataBinder类,为web请求提供了参数绑定服务
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取ModelFactory:
// 2.ModelFactory可以协助控制器在调用方法之前初始化模型,并在调用之后更新模型
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 创建ServletInvocableHandlerMethod对象
// 3.ServletInvocableHandlerMethod继承并扩展了InvocableHandlerMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 4.尝试绑定参数、返回值解析器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 5.创建ModelAndViewContainer,并初始化Model对象
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 6.异步请求相关
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("Resume with async result ["
+ (result instanceof CharSequence ? "\"" + result + "\"" : result) + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 7.调用Controller中的具体方法并处理返回值
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 8.返回ModelAndView对象
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
// 完成请求后续处理,并将当前请求置为未激活
webRequest.requestCompleted();
}
}
invokeHandlerMethod方法还是很复杂的,下面我们对该方法进行详细的分析
2.getModelFactory方法
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
// 1.处理@SessionAttributes注解
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
// 2.处理@ModelAttribute注解
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
// Global methods first
// 3.优先处理全局@ModelAttribute注解的方法,例如被@ControllerAdvice标注的类中存在被@ModelAttribute注解的方法,则优先处理
this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> {
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
// 4.循环所有标注了@ModelAttribute注解的方法,并创建InvocableHandlerMethod对象
// InvocableHandlerMethod:负责具体的HandlerMethod的调用、参数解析等工作
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
// 5.返回ModelFactory对象
// ModelFactory:协助在控制器方法调用之前初始化模型,并在调用之后更新它。
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
该方法主要作用是处理@ModelAttribute和@SessionAttributes两个注解
3.ModelFactory的initModel初始化
上一步创建了ModelFactory对象实例,接下来看其initModel具体都做了什么工作:
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
// 1.解析并合并@SessionAttributes注解
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
container.mergeAttributes(sessionAttributes);
// 2.调用被@ModelAttribute注解的方法
invokeModelAttributeMethods(request, container);
// 3.查找标注了@ModelAttribute、@SessionAttributes的方法参数,确保其解析过程中不会发生异常
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!container.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
container.addAttribute(name, value);
}
}
}
注意这里会有一个Expected session attribute xxx
的异常,如果类上标注了@SessionAttributes注解,且在方法中标注了@ModelAttribute注解,如果@ModelAttribute为空,则会抛出此异常
4.invokeAndHandle简析
继续分析,接下来应该调用Controller中的具体方法了,但是在调用之前,还要有参数解析、InitBinder方法初始化、InitBinder方法调用等工作,接下来逐步分析。
public void invokeAndHandle(
ServletWebRequest webRequest,
ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 1.调用Controller中的具体方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 2.设置返回状态码
setResponseStatus(webRequest);
// 3.当前请求无返回值或者返回值中包含错误,则将请求完成标识设置为true并返回
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
// 4.当前请求有返回值且无错误信息,则将请求完成标识设置为false,并继续处理当前请求
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 选取合适的HandlerMethodReturnValueHandler,并处理返回值
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
最重要的就是第一步invokeForRequest方法:
public Object invokeForRequest(NativeWebRequest request,
@Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取并解析请求参数
/**
* 注意这里不一定都是解析@RequestMapping方法的参数,
* 也有可能会解析@InitBinder方法的参数
*
* 所以下面的doInvoke方法也并不一定调用具体的@RequestMapping方法,
* 也有可能调用@InitBinder方法进行参数的解析绑定
*/
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 调用方法
return doInvoke(args);
}
该方法看起来很简单,只有两个函数调用,但是其背后的逻辑还是相当复杂的。
接下来的处理分为两步,一是参数处理,二是方法调用。
5.getMethodArgumentValues参数获取及解析
private Object[] getMethodArgumentValues(NativeWebRequest request,
@Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 1.获取方法参数列表,并创建与参数个数相同的Object数组,用来保存解析的参数值
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
// 2.解析参数
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// 这里当解析@InitBinder参数时会指定providedArgs参数,无需纠结...
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 参数解析器是否支持对该参数的解析
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
// 调用参数解析器的解析方法
/**
* SpringMVC的参数解析器顶级接口为HandlerMethodArgumentResolver
* 该接口只提供了两个方法:supportsParameter和resolveArgument
*
* 我们也可以自定义参数解析器,只需实现这两个方法即可
*/
args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
// Leave stack trace for later, e.g. AbstractHandlerExceptionResolver
if (logger.isDebugEnabled()) {
String message = ex.getMessage();
if (message != null && !message.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, message));
}
}
throw ex;
}
}
// 如未能正常解析参数且未抛出异常,则说明当前参数没有合适的参数解析器,抛出 'No suitable resolver' 异常
if (args[i] == null) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
}
return args;
}
从代码中可以看到,具体的参数解析工作委托给了HandlerMethodArgumentResolver,HandlerMethodArgumentResolver是一个接口,其中只有两个方法:
public interface HandlerMethodArgumentResolver {
/**
* 此解析器是否支持给定的方法参数。
*/
boolean supportsParameter(MethodParameter parameter);
/**
* 将方法参数解析为给定请求的参数值。
*/
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
那么看到这里,大家一定也能想到,既然这个类是一个接口,那么必然有多个实现,接下来就应该查找具体的参数解析器、并调用解析器的resolveArgument方法对参数进行解析:
public Object resolveArgument(
MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
@Nullable 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);
}
上述代码就是干这些事情的,接下来以AbstractNamedValueMethodArgumentResolver为例,看一下参数的具体解析过程:
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 1.NamedValueInfo对象包含了name,required,defaultValue三个信息
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
// 获取MethodParameter对象,该对象封装了方法参数的规范
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 2.解析参数名,包括占位符和表达式等
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
// 3.将给定的参数类型和值名称解析为参数值。
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
// 如果未能正常解析
/**
* 如
* 方法参数 : @RequestParam(name = "name") String name
* 请求路径参数后缀 : sayHello?1212
*
* 未指定参数名称,则无法正常解析,接下来要判断NamedValueInfo属性值,并作出后续处理
*/
if (arg == null) {
// 如果默认值不为空,则
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
// 指定了required属性且该参数不是为非不必须,则调动handleMissingValue方法处理缺失值,该方法一般会抛出异常
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
// 最后处理将该参数值处理为null即可
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
// 4.创建WebDataBinder实例
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
// 5.尝试转换参数
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
前面对于参数的各种情况的处理,都比较简单,大家可以多写一些实例,多测试即可;接下来要看convertIfNecessary函数的调用过程。
-
convertIfNecessary方法调用
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam) throws TypeMismatchException {
return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
}
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable MethodParameter methodParam)
throws TypeMismatchException {
return doConvert(value, requiredType, methodParam, null);
}
private <T> T doConvert(@Nullable Object value,@Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam, @Nullable Field field) throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
if (field != null) {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field);
}
else {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
}
catch (ConverterNotFoundException | IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
catch (ConversionException | IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// Custom editor for this type?
// 1、判断有无自定义属性编辑器
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
// No custom editor but custom ConversionService specified?
// 2、判断有无自定义ConversionService
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?
// ClassUtils.isAssignableValue(requiredType, convertedValue)-->判断requiredType和convertedValue的class,是否相同,
// 相同返回->true;否则返回->false
// 3、 如果有自定义属性编辑器或者通过解析出来的值类型与真实的值类型的class不同
// 例如<property name="age" value="3"/>,我们需要将value转换成int时
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;
// 4、执行转换
if (requiredType != null) {
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
// Object类型
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;
}
// map类型
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,不接上面的else if
// 如果经过转换过的值是数组类型,且其长度只有1,那么只取其第0个作为最终转换值
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
// 如果类型是String,并且是java的基本数据类型或者包装类型
// 包括 boolean, byte, char, short, int, long, float, double
// 和 Boolean, Byte, Character, Short, Integer, Long, Float, Double
// 那么直接调用toString()方法返回即可,注意convertedValue是Object类型,不是基本或包装类型,所以是可以调用toString()方法的
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
// 如果转换值是String类的实例,但是我们又不能转换为解析出来的requiredType的实例
// 例如枚举类型值的注入
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);
}
// 删除logger信息
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 (requiredType == Optional.class) {
convertedValue = Optional.empty();
}
}
// 5、 判定requiredType是否可从convertedValue转换而来,并尝试使用conversionService转换,及处理转换异常
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (conversionAttemptEx != null) {
// Original exception from former ConversionService call above...
throw conversionAttemptEx;
}
else if (conversionService != null && typeDescriptor != 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);
}
}
// 到此为止,可以确定类型不匹配,无法转换,抛出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;
}
}
// 6、返回转换值
return (T) convertedValue;
}
6.doInvoke方法调用
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(formatInvokeError(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 {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
继续上面的分析,接下来就应该设置状态码了:
7.setResponseStatus设置相应状态码以及handleReturnValue处理返回值
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
// 获取HttpStatus
HttpStatus status = getResponseStatus();
// 未发现HttpStatus直接返回
if (status == null) {
return;
}
HttpServletResponse response = webRequest.getResponse();
if (response != null) {
String reason = getResponseStatusReason();
if (StringUtils.hasText(reason)) {
/**
* 注意 注意 注意:这里是 sendError , 不是 setError
* 使用指定的状态码并清空缓冲,发送一个错误响应至客户端。如果响应已经被提交,这个方法会抛出IllegalStateException。
* 服务器默认会创建一个HTML格式的服务错误页面作为响应结果,其中包含参数msg指定的文本信息,
* 这个HTML页面的内容类型为“text/html”,保留cookies和其他未修改的响应头信息。
*
* 如果一个对应于传入的错误码的错误页面已经在web.xml中声明,那么这个声明的错误页面将会优先于建议的msg参数服务于客户端。
*/
response.sendError(status.value(), reason);
}
else {
/**
* 设置响应的状态码。
* 这个方法被用于当响应结果正常时(例如,状态码为SC_OK或SC_MOVED_TEMPORARTLY)设置响应状态码。
* 如果发生错误,而且来访者希望调用在web应用中定义的错误页面作为显示,那么应该使用sendError方法代替之。
* 使用setStatus方法之后,容器会清空缓冲并设置Location响应头,保留cookies和其他响应头信息。
*/
response.setStatus(status.value());
}
}
// To be picked up by RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
}
如果@RequestMapping设置了@ResponseStatus注解,这里则要根据注解设置放回状态码。如果getResponseStatusReason方法返回了错误信息,则直接通过sendError方法返回给前端。否则将状态码信息设置到response里即可。
在后续处理根据@RequestMapping方法返回值、相应信息等判断,是否将当前请求设置为已经完成。例如当前请求无需返回视图、或者当前请求的放回状态码包含了错误信息,则无需继续后续处理。
假设当前是有视图或者返回值,接下来应该选取合适的HandlerMethodReturnValueHandler并处理返回值,先来看一下HandlerMethodReturnValueHandler的定义:
public interface HandlerMethodReturnValueHandler {
/**
* 判断当前策略(Handler)是否支持MethodParameter(方法返回类型)
*/
boolean supportsReturnType(MethodParameter returnType);
/**
* 处理返回值,为模型添加属性、视图等想关内容
*/
void handleReturnValue(@Nullable Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception;
}
对于其实现者,只需实现这两个方法即可。这里以ModelAndViewMethodReturnValueHandler为例看其具体的处理过程:
public void handleReturnValue(@Nullable Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
// 选取合适的HandlerMethodReturnValueHandler,如果没有找到则抛出异常
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 处理返回值
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 当前请求返回值为null,无需处理,并且要将当前请求标记已处理
if (returnValue == null) {
mavContainer.setRequestHandled(true);
return;
}
// 处理引用视图
ModelAndView mav = (ModelAndView) returnValue;
if (mav.isReference()) {
String viewName = mav.getViewName();
mavContainer.setViewName(viewName);
if (viewName != null && isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
// 处理普通视图(即我们已经制定了具体的View视图,而无需通过视图解析器再次解析)
else {
View view = mav.getView();
mavContainer.setView(view);
if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
mavContainer.setRedirectModelScenario(true);
}
}
// 处理属性
mavContainer.setStatus(mav.getStatus());
mavContainer.addAllAttributes(mav.getModel());
}
这里又涉及到两个概念,即引用视图以及普通视图(姑且命名为普通视图)。引用视图如没有指定具体的View类型,而只是通过ModelAndView对象的setViewName设置了返回视图的名称,则该视图还需要再次被解析;普通视图正好相反。
到这里invokeAndHandle方法的调用就完成了,接下来是getModelAndViewd对返回的模型做了进一步的处理。
8.getModelAndView方法后续处理
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// 1.更新模型
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
// 2.获取ModelMap并创建ModelAndView
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
// 3.处理引用类型视图和转发类型视图
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}