赞
踩
SpringMVC调用时序图
1.真正的调用handler的方法是在HandlerAdapter中进行的
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
/**
* ~~设置request中的属性啊,判断文件下载啊啥的,
**/
// 调用Handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
/**
* ~~异步请求啊,错误处理啊,视图映射啊啥的。。。。。。
**/
}
}
//创建请求对象
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//创建WebDataBinder工厂
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//创建Model工厂
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//创建InvocableHandlerMethod,handler方法调用对象
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//设置argumentResolvers用于处理请求参数,实际上是一个组件模式
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
//HandlerMethodReturnValueHandler,用于处理同步或者异步返回结果
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
//设置数据绑定工厂
invocableMethod.setDataBinderFactory(binderFactory);
//设置参数名发现器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//模型和视图容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//设置FlashMap中的值
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//初始化模型
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
/**
*异步处理
**/
//真正的调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
}

1、收集目标Controller中的initBinder注解的方法
2、收集所有ControllerAdvice类型中的有InitBinder的方法
3、调用createDataBinderFactory 并传入收集的initBinder方法
4、初始化Binder顺序initializer.initBinder(dataBinder, webRequest) - > ControllerAdvice注解ControllerinitBinderMethods->该ControllerinitBinderMethods
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
//收集Controller中的有InitBinder注解的方法
Set<Method> methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
//收集ControllerAdvice类型中的有InitBinder的方法
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));
}
}
}
/**
*放入InitMethods集合中,并创建DataBinderFactory
**/
}

initBinderArgumentResolvers方法用于处理InitBinder所注解的方法中可能存在的请求参数转换。例如在initBinder注解的方法中传入Type等来设置使用的类型
private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {
InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
//设置参数处理,initBinder方法中是可以传入request中包含的参数的 binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
return binderMethod;
}
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
throws Exception {
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
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<InvocableHandlerMethod>();
// Global methods first
for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
Object bean = entry.getKey().resolveBean();
for (Method method : entry.getValue()) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
}
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) {
InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);
attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
attrMethod.setDataBinderFactory(factory);
return attrMethod;
}
public void initModel(NativeWebRequest request, ModelAndViewContainer container,
HandlerMethod handlerMethod) throws Exception {
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
container.mergeAttributes(sessionAttributes);
invokeModelAttributeMethods(request, container);
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);
}
}
}

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);
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
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("Error resolving argument", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
throw new IllegalStateException(msg);
}
}
return args;
}

1、resolve types(默认模式下处理所有简单对象)
- @RequestParam-annotated method arguments.
- This excludes Map params where the annotation doesn’t specify a name.
- instead for such params.
- Arguments of type MultipartFile unless annotated with @RequestPart
- Arguments of type javax.servlet.http.Part unless annotated with @ RequestPart
- In default resolution mode, simple type arguments even if not with @ RequestParam
2、how it works
If the method parameter type is Map, the name specified in the annotation is used to resolve the request parameter String value. The value is then converted to a Map via type conversion assuming a suitable Converter or PropertyEditor has been registered. Or if a request parameter name is not specified the RequestParamMapMethodArgumentResolver is used instead to provide access to all request parameters in the form of a map.
A WebDataBinder is invoked to apply type conversion to resolved request header values that don’t yet match the method parameter type.
3、parent Class :AbstractNamedValueMethodArgumentResolver
,通过实现父类resolveName方法来处理Name对应的参数
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
MultipartHttpServletRequest multipartRequest =
WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
Object arg = null;
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
if (arg == null) {
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}

1、处理类型
- Resolves Map method arguments annotated with an @RequestParam where the annotation does not specify a request parameter name.
2、how it works
The created Map contains all request parameter name/value pairs. If the method parameter type is MultiValueMap instead, the created map contains all request parameters and all there values for cases where request parameters have multiple values.
3、parent Class :HandlerMethodArgumentResolver
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
Map<String, String[]> parameterMap = webRequest.getParameterMap();
if (MultiValueMap.class.isAssignableFrom(paramType)) {
MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(parameterMap.size());
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
for (String value : entry.getValue()) {
result.add(entry.getKey(), value);
}
}
return result;
}
else {
Map<String, String> result = new LinkedHashMap<String, String>(parameterMap.size());
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
if (entry.getValue().length > 0) {
result.put(entry.getKey(), entry.getValue()[0]);
}
}
return result;
}
}

1、resolve types
- Resolves method arguments annotated with an @PathVariable. If the method parameter type is Map, the name specified in the annotation is used to resolve the URI variable String value. The value is then converted to a Map via type conversion, assuming a suitable Converter or PropertyEditor has been registered.
2、how it works
-An @PathVariable is a named value that gets resolved from a URI template variable. It is always required and does not have a default value to fall back on. See the base class org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver for more information on how named values are processed.
-If the method parameter type is Map, the name specified in the annotation is used to resolve the URI variable String value. The value is then converted to a Map via type conversion, assuming a suitable Converter or PropertyEditor has been registered.
A WebDataBinder is invoked to apply type conversion to resolved path variable values that don’t yet match the method parameter type.
3、parent Class :AbstractNamedValueMethodArgumentResolver
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
}
1、处理类型
- Resolves Map method arguments annotated with an @PathVariable where the annotation does not specify a path variable name. The created Map contains all URI template name/value pairs.
2、how it works
resolveMap from uriTemplateVars
3、parent Class :HandlerMethodArgumentResolver
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
@SuppressWarnings("unchecked")
Map<String, String> uriTemplateVars =
(Map<String, String>) webRequest.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
if (!CollectionUtils.isEmpty(uriTemplateVars)) {
return new LinkedHashMap<String, String>(uriTemplateVars);
}
else {
return Collections.emptyMap();
}
}
1、处理类型
- Resolves method arguments annotated with @MatrixVariable.
2、how it works
3、 parent Class : AbstractNamedValueMethodArgumentResolver
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
Map<String, MultiValueMap<String, String>> pathParameters = (Map<String, MultiValueMap<String, String>>)
request.getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
if (CollectionUtils.isEmpty(pathParameters)) {
return null;
}
String pathVar = parameter.getParameterAnnotation(MatrixVariable.class).pathVar();
List<String> paramValues = null;
if (!pathVar.equals(ValueConstants.DEFAULT_NONE)) {
if (pathParameters.containsKey(pathVar)) {
paramValues = pathParameters.get(pathVar).get(name);
}
}
else {
boolean found = false;
paramValues = new ArrayList<String>();
for (MultiValueMap<String, String> params : pathParameters.values()) {
if (params.containsKey(name)) {
if (found) {
String paramType = parameter.getNestedParameterType().getName();
throw new ServletRequestBindingException(
"Found more than one match for URI path parameter '" + name +
"' for parameter type [" + paramType + "]. Use 'pathVar' attribute to disambiguate.");
}
paramValues.addAll(params.get(name));
found = true;
}
}
}
if (CollectionUtils.isEmpty(paramValues)) {
return null;
}
else if (paramValues.size() == 1) {
return paramValues.get(0);
}
else {
return paramValues;
}
}

1、处理类型
- Resolves method arguments of type Map annotated with
2、how it works
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
@SuppressWarnings("unchecked") Map<String, MultiValueMap<String, String>> matrixVariables = (Map<String, MultiValueMap<String, String>>) request.getAttribute( HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); if (CollectionUtils.isEmpty(matrixVariables)) { return Collections.emptyMap(); } MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>(); String pathVariable = parameter.getParameterAnnotation(MatrixVariable.class).pathVar(); if (!pathVariable.equals(ValueConstants.DEFAULT_NONE)) { MultiValueMap<String, String> mapForPathVariable = matrixVariables.get(pathVariable); if (mapForPathVariable == null) { return Collections.emptyMap(); } map.putAll(mapForPathVariable); } else { for (MultiValueMap<String, String> vars : matrixVariables.values()) { for (String name : vars.keySet()) { for (String value : vars.get(name)) { map.add(name, value); } } } } return (isSingleValueMap(parameter) ? map.toSingleValueMap() : map); }
3、 parent Class : HandlerMethodArgumentResolver
1、处理类型,默认模式下处理所有级联对象
- Returns true if the parameter is annotated with ModelAttribute or, if in default resolution mode, for any method parameter that is not a simple type.
2、how it works
Model attributes are obtained from the model or created with a default constructor (and then added to the model). Once created the attribute is populated via data binding to Servlet request parameters. Validation may be applied if the argument is annotated with @javax.validation.Valid. or Spring’s own @org.springframework.validation.annotation.Validated.
When this handler is created with annotationNotRequired=true any non-simple type argument and return value is regarded as a model attribute with or without the presence of an @ModelAttribute.
3、parent Class : ModelAttributeMethodProcessor
该父类有另一个子类型ProxyingHandlerMethodArgumentResolver 用于处理被ModelAttribute注解所注解的接口类型。
以下为父类如何处理参数
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);
}

1、处理类型(处理RequestBody请求参数,ResponseBody响应结果)
- Resolves method arguments annotated with @RequestBody and handles return values from methods annotated with @ResponseBody by reading and writing to the body of the request or response with an HttpMessageConverter.
2、how it works
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
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 arg;
}

3、parent Class : AbstractMessageConverterMethodProcessor
1、处理类型
2、how it works
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
RequestPart requestPart = parameter.getParameterAnnotation(RequestPart.class);
boolean isRequired = ((requestPart == null || requestPart.required()) && !parameter.isOptional());
String name = getPartName(parameter, requestPart);
parameter = parameter.nestedIfOptional();
Object arg = null;
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
arg = mpArg;
}
else {
try {
HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, name);
arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());
WebDataBinder binder = binderFactory.createBinder(request, 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());
}
catch (MissingServletRequestPartException ex) {
if (isRequired) {
throw ex;
}
}
catch (MultipartException ex) {
if (isRequired) {
throw ex;
}
}
}
if (arg == null && isRequired) {
if (!MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
throw new MultipartException("Current request is not a multipart request");
}
else {
throw new MissingServletRequestPartException(name);
}
}
if (parameter.isOptional()) {
arg = OptionalResolver.resolveValue(arg);
}
return arg;
}

3、parent Class :AbstractMessageConverterMethodArgumentResolver
1、处理类型
- Resolves method arguments annotated with @RequestHeader except for Map arguments. See RequestHeaderMapMethodArgumentResolver for details on Map arguments annotated with @RequestHeader.
1、处理类型
- Resolves Map method arguments annotated with @RequestHeader. For individual header values annotated with @RequestHeader see RequestHeaderMethodArgumentResolver instead.
1、处理类型
- org.springframework.web.method.annotation.AbstractCookieValueMethodArgumentResolver that resolves cookie values from an HttpServletRequest.
1、处理类型
- Resolves method arguments annotated with @Value.
- An @Value does not have a name but gets resolved from the default value string, which may contain ${…} placeholder or Spring Expression Language #{…} expressions.
1、处理类型
- SessionAttributeMethodArgumentResolver
1、处理类型
- Resolves method arguments annotated with an @RequestAttribute.
1、处理类型
Resolves request-related method argument values of the following types:
WebRequest
ServletRequest
MultipartRequest
HttpSession
Principal
Locale
TimeZone (as of Spring 4.0)
java.time.ZoneId (as of Spring 4.0 and Java 8)
InputStream
Reader
org.springframework.http.HttpMethod (as of Spring 4.0)
1、处理类型
- Resolves response-related method argument values of types:
ServletResponse
OutputStream
Writer
1、处理类型
- Resolves HttpEntity and RequestEntity method argument values and also handles HttpEntity and ResponseEntity return values.
1、处理类型
- Resolves method arguments of type RedirectAttributes.
1、处理类型
- Resolves Model arguments and handles Model return values.
1、处理类型
- Resolves Map method arguments and handles Map return values.
1、处理类型
- Resolves Errors method arguments.
1、处理类型
- Resolves a SessionStatus argument by obtaining it from the ModelAndViewContainer.
1、处理类型
- Resolvers argument values of type UriComponentsBuilder.
三角箭头代表继承关系,菱形箭头代表包含关系
1、数据绑定
bind(PropertyValues pvs)方法,通过HandlerMethodResolver传入属性信息,然后通过AbstractNestablePropertyAccessor处理级联对象,并设置值到 target 中,如此完成数据绑定功能。
2、数据转换。
数据转换实际上都是通过TypeConverterDelegate 委托类型来处理的,该委托类型持有一个PropertyEditorRegistrySupport的引用(BeanWrapperImpl或者SimpleTypeConverter) 通过PropertyEditorRegistrySupport获得CustomEditor和ConversionService和DefaultEditor,
生效顺序:
1、CustomEditor,使用者注册的PropertyEditor。
2、ConversionService
3、DefaultEditor。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。