org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest(NativeWebRequest request,mavContainer,Object... providedArgs) {
// 解析参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);{
// 获取当前HandlerMethod的方法参数的信息
MethodParameter[] parameters = getMethodParameters();
// 没有参数
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
// 定义参数数组
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
// 设置参数名称解析器,获取用户写的参数名称
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// 先找在这之前是不是已经提供了执行参数,只有在异常处理的时候,会将异常信息参数传递过来
// 正常请求的这个HandlerMethod的逻辑这个参数是为空的providedArgs
// 还有@InitBinder注解中也会传递一个参数,叫WebDataBinder对象
// 如果提供了参数,当当前方法需要相同类型的参数,就会直接赋值
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 拿到所有的参数解析器,看一下有哪些可以支持解析这个参数
// 如果都解析不了,那么就抛出异常
if (!this.resolvers.supportsParameter(parameter){
return getArgumentResolver(parameter) != null;{
// 解析过当前参数就需要缓存一下,省的每次都去找参数解析器
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
// 没有缓存就遍历所有的参数解析器,在初始化的时候就已经设置了默认的一些参数解析器
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
// 判断是否支持
if (resolver.supportsParameter(parameter)) {
// 如果支持,就缓存起来,返回结果
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
}) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 开始调用所有的参数解析器解析参数
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);{
// 获取参数解析器,因为上面调用supportsParameter是否支持的时候已经调用了getArgumentResolver缓存过了,parameter作为key,value就是参数解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
// 没有找到就是没有调用supportsParameter方法
if (resolver == null) {
throw new IllegalArgumentException("supportsParameter should be called first.");
}
// 使用找到的参数解析器开始解析参数
// 不同参数解析的逻辑不一样
// 1. 讲解PathVariable
// PathVariableMethodArgumentResolver
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);{
// 获取参数的名称信息,将PathVariable注解信息封装成NamedValueInfo,并进行缓存,防止每次解析都创建
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
// 获取参数类型,支持参数为Optional包裹的参数
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 解析嵌入值和表达式,例如: ${aa.bb},#{aa.bb}},如果没有表达式,就会原样返回
Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
// 根据参数名解析参数值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);{
// 在getHandler的时候,就已经对路径进行了解析,将路径变量保存到了request域中
Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
// 获取参数值
return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
}
// 如果没有解析到值
if (arg == null) {
// 使用默认值再来试一次
if (namedValueInfo.defaultValue != null) {
// 解析嵌入值和表达式,例如: ${aa.bb},#{aa.bb},如果没有表达式,就会原样返回
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
}
// 如果参数值必须,并且也不是Optional包裹的,说明解析不到值
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
// 值缺失处理,该抛异常抛异常,该赋空对象赋空对象,自己处理
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
// 处理空值情况,布尔类型返回给false,基本类型抛异常,其他的原样返回
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
// 参数值为"",且有默认值,解析嵌入值和表达式,例如: ${aa.bb},#{aa.bb}},如果没有表达式,就会原样返回
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
}
// 判断binderFactory是否为空,发起请求之前,都创建过这个对象,所以不为空
// 用于参数的绑定
if (binderFactory != null) {
// 创建WebBinder对象
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);{
// 创建WebDataBinder实例
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);{
return new ExtendedServletRequestDataBinder(target, objectName);
}
// 当前工厂中的WebBindingInitializer是否为空
// 在创建binderFactory的时候,就已经将initializer设置进来了
// return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
// 而getWebBindingInitializer是在添加了@EnableMVC加入RequestMappingAdapter初始化的时候,设置进来的
// 如果是默认的RequestMappingAdapter就没有
// 类型为org.springframework.web.bind.support.ConfigurableWebBindingInitializer
if (this.initializer != null) {
this.initializer.initBinder(dataBinder, webRequest);{
// 初始化Binder,设置一些属性,参数校验器,类型转换起,异常绑定处理器,属性编辑器
binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
if (this.directFieldAccess) {binder.initDirectFieldAccess();}
if (this.messageCodesResolver != null) {binder.setMessageCodesResolver(this.messageCodesResolver);}
if (this.bindingErrorProcessor != null) {binder.setBindingErrorProcessor(this.bindingErrorProcessor);}
if (this.validator != null && binder.getTarget() != null && this.validator.supports(binder.getTarget().getClass())) {
binder.setValidator(this.validator);
}
if (this.conversionService != null) { binder.setConversionService(this.conversionService);}
if (this.propertyEditorRegistrars != null) {
for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
propertyEditorRegistrar.registerCustomEditors(binder);
}
}
}
}
// 这是工厂自身提供的初始化方法
initBinder(dataBinder, webRequest);{
// 遍历所有的InitBinder方法,前面已经找出来了,是Controller自身的和@ControllerAdvice中全局的一起
for (InvocableHandlerMethod binderMethod : this.binderMethods) {
// 判断当前InitBinderMethod是否可用,除非在InitBinder设置了参数
if (isBinderMethodApplicable(binderMethod, dataBinder)) {
// 根据参数执行方法,上面已经详细讲过了
Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);
// 返回值必须是void
if (returnValue != null) {
throw new IllegalStateException("@InitBinder methods must not return a value (should be void): " + binderMethod);
}
}
}
}
return dataBinder;
}
// 创建好了WebDataBinder之后,对参数进行转换
// 对参数进行适配,利用Spring自身提供的类型转换机制来就参数进行类型转换
// 也可以@InitBinder获取的WebDataBinder对象,设置一个相关的转换器来处理Spring没有提供的默认的转换器
// 例如:将String->枚举,Spring可能就没有提供,自己就可以自定义转换器,设置到WebDataBinder binder中
// 这样就能就String转换为枚举类型
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
// 参数类型转换之后,还是没有获取到值的情况
if (arg == null && namedValueInfo.defaultValue == null && namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
}
}
// 处理结果后的参数值
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);{
String key = View.PATH_VARIABLES;
int scope = RequestAttributes.SCOPE_REQUEST;
Map<String, Object> pathVars = (Map<String, Object>) request.getAttribute(key, scope);
if (pathVars == null) {
pathVars = new HashMap<>();
// 将数据保存到请求域中
request.setAttribute(key, pathVars, scope);
}
// 因为可能存在多个路径变量,所以需要使用map
pathVars.put(name, arg);
}
return arg;
}
// 2. 讲解ReqeustBody
// resolver = RequestResponseBodyMethodProcessor
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);{
// 获取到请求参数对象,使用消息转换器读取消息
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());{
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);{
boolean noContentType = false;
MediaType contentType = inputMessage.getHeaders().getContentType();
// 给默认的内容类型
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
// 获取到参数对象所属的类
Class<?> contextClass = parameter.getContainingClass();
// 获取参数的类型,如果给定的参数类型不是Class,返回null
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
// 如果参数类型不是Class
if (targetClass == null) {
// 根据给的参数找到对应类的Class来
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
// 判断输入消息的类型是不是HttpRequest,标注了@RequestBody的情况是ServletServerHttpRequest,符合条件
// 获取到请求类型 POST转HttpMethod对象
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
// 读取的值默认为空对象
Object body = NO_VALUE;
try {
// 根据传递过来的inputMessage消息提创建一个EmptyBodyCheckingHttpInputMessage,内部会自己处理请求体
//比如自己设置了inputMessage
EmptyBodyCheckingHttpInputMessage message = new EmptyBodyCheckingHttpInputMessage(inputMessage);{
this.headers = inputMessage.getHeaders();
// 获取body
InputStream inputStream = inputMessage.getBody();
// 默认就是为false,除非传递的inputMessage参数重写了markSupported为true
if (inputStream.markSupported()) {
inputStream.mark(1);
this.body = (inputStream.read() != -1 ? inputStream : null);
inputStream.reset();
}
else {
// 创建一个新的输入流
PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
// 将请求体的输入村进入
int b = pushbackInputStream.read();
if (b == -1) {
this.body = null;
}
else {
this.body = pushbackInputStream;
pushbackInputStream.unread(b);
}
}
}
// 遍历所有的消息类型转换器对消息进行类型转换
// 这里的消息转换器有三种情况,详见消息转换器HttpMessageConverter原理
for (HttpMessageConverter<?> converter : this.messageConverters) {
// 获取消息转换器的类型
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
// 判断消息转换器是否是GenericHttpMessageConverter,如果不是,返回null,是的话就强转为GenericHttpMessageConverter类型
GenericHttpMessageConverter<?> genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
// 判断当前消息转换器是否可以读取请求数据
if (genericConverter != null ?
// 如果消息类型为GenericHttpMessageConverter,那么直接判断它是否可以读取
genericConverter.canRead(targetType, contextClass, contentType){
// 判断消息转换器是否支持媒体类型
if (!canRead(mediaType)) {
return false;
}
// 将参数类型包装成JavaType类型
JavaType javaType = getJavaType(type, contextClass);
// 获取到合适的ObjectMapper,这个ObjectMapper就是有来序列化的
ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), mediaType);{
// objectMapperRegistrations默认为空,除非自己设置的
if (targetMediaType == null || CollectionUtils.isEmpty(this.objectMapperRegistrations)) {
return this.defaultObjectMapper;
}
// 如果注册表不为空
// 遍历ObjectMapperRegistration注册表,根据媒体类型来获取对应的ObjectMapper
for (Map.Entry<Class<?>, Map<MediaType, ObjectMapper>> typeEntry : getObjectMapperRegistrations().entrySet()) {
if (typeEntry.getKey().isAssignableFrom(targetType)) {
for (Map.Entry<MediaType, ObjectMapper> objectMapperEntry : typeEntry.getValue().entrySet()) {
if (objectMapperEntry.getKey().includes(targetMediaType)) {
return objectMapperEntry.getValue();
}
}
return null;
}
}
// 都没找到,返回默认的
return this.defaultObjectMapper;
}
// 如果没有找到objectMapper,就没有办法序列化
if (objectMapper == null) {
return false;
}
// 判断objectMapper是否可以序列化javaType类型
AtomicReference<Throwable> causeRef = new AtomicReference<>();
if (objectMapper.canDeserialize(javaType, causeRef)) {
return true;
}
return false;
} :
// 否则还要判断参数类型不能为空,再判断它是否可以读取
(targetClass != null && converter.canRead(targetClass, contentType))) {
// 实现了RequestResponseBodyAdvice的执行链,在创建当前参数解析器的时候就已经将
// 在RequestMappingHandlerAdapter初始化的时候,给参数解析器设置的
// this.requestResponseBodyAdvice就是在RequestMappingHandlerAdapter初始化的时候,执行initControllerAdviceCache();方法
// 从@ControllerAdvice中,实现了requestResponseBodyAdvice的类缓存下来了
// resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
RequestResponseBodyAdviceChain advice = this.advice;
// 上面已经将请求体的数据存在EmptyBodyCheckingHttpInputMessage massage中了
if (message.hasBody()) {
// 找到真实要用的HttpInputMessage消息体
// 因为HttpInputMessage可以通过ControllerAdviceBean中实现RequestBodyAdvice来处理,返回新的HttpInputMessage
HttpInputMessage msgToUse = advice.beforeBodyRead(message, parameter, targetType, converterType);{
// 找到匹配的可用的RequestBodyAdvice,因为可能在@ControllerAdvice中设置条件,可能不满足当前Bean的方法
for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
// 判断当前RequestBodyAdvice的supports是否返回的true
if (advice.supports(parameter, targetType, converterType)) {
// 调用接口的beforeBodyRead,在这之前还没开始读取,所以可以改请求体内部的数据
request = advice.beforeBodyRead(request, parameter, targetType, converterType);{
// 我自定义的RequestBodyAdvice,获取body,改变数据,然后又存进去,可达到加密解密的
InputStream body = inputMessage.getBody();
Object object = JSON.parseObject(IoUtil.read(body, Charset.defaultCharset()), targetType);
if (object instanceof A) {
((A) object).setId(-1)
}
// 将数据返回出去
return new MappingJacksonInputMessage(new ByteArrayInputStream(JSON.toJSONString(object).getBytes(StandardCharsets.UTF_8)), inputMessage.getHeaders());
}
}
}
return request;
}
// 调用转换器的read方法
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));{
// 将Bean和参数类型封装成JavaType
// 再使用ObjectMapper读取body
JavaType javaType = getJavaType(type, contextClass);
return readJavaType(javaType, inputMessage);
}
// 上面调用过RequestBodyAdvice的beforeRead方法,现在调用RequestBodyAdvice的advice.afterBodyRead
// 就是一些读取前后的切面
body = advice.afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
// 如果没有请求体
// 上面调用过RequestBodyAdvice的beforeRead,afterRead方法,在这里调用RequestBodyAdvice的advice.handleEmptyBody
// 处理请求体为空的数据
body = advice.handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
}
// 如果body为初始值NO_VALUE,表示没有消息处理器或者没有合适的消息处理器来处理当前参数
if (body == NO_VALUE) {
// 没有请求方法POST,或者请求方法不支持,或者没有请求体,这个时候没有内容是正常的,直接返回null
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || (noContentType && !message.hasBody())) {
return null;
}
// 否则还没解析出参数表示无法处理当前这个请求参数
throw new HttpMediaTypeNotSupportedException(contentType,getSupportedMediaTypes(targetClass != null ? targetClass : Object.class));
}
// 返回处理后的请求体
return body;
}
// 没有读取到参数,但是参数又是必须要的,设置了required=true
if (arg == null && checkRequired(parameter)) {
// 参数不可读的异常
throw new HttpMessageNotReadableException("Required request body is missing: " + parameter.getExecutable().toGenericString(), inputMessage);
}
// 返回解析之后的参数
return arg;
}
// 获取在方法参数写的名称
String name = Conventions.getVariableNameForParameter(parameter);
// 判断binderFactory是否为空,发起请求之前,都创建过这个对象,所以不为空
if (binderFactory != null) {
// 创建Binder实例,在讲解PathVariable的时候代码解释过了
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
// 校验参数,如果有必要就是@NotEmpty等等这种参数
validateIfApplicable(binder, parameter);{
// 找到方法中所有的参数注解,来校验
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation ann : annotations) {
// 调用校验工具类校验
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);{
// 获取注解类型
Class<? extends Annotation> annotationType = ann.annotationType();
// 获取注解全类名称
String annotationName = annotationType.getName();
// 如果加了@javax.validation.Valid这个注解
if ("javax.validation.Valid".equals(annotationName)) {
// 直接返回固定数组
return EMPTY_OBJECT_ARRAY;
}
// 获取@Validated注解信息
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
if (validatedAnn != null) {
// 获取校验的分组信息
Object hints = validatedAnn.value();
// 转换成数组,因为可能填写的是一个class,要包装成数组形成
return convertValidationHints(hints);{
// 如果没有校验分组,直接返回固定数组
if (hints == null) {
return EMPTY_OBJECT_ARRAY;
}
// 返回指定的分组
return (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints});
}
}
// 其他情况,如果注解为Valid开头,反射获取分组名
if (annotationType.getSimpleName().startsWith("Valid")) {
// 和Validated差不多
Object hints = AnnotationUtils.getValue(ann);
return convertValidationHints(hints);
}
// 如果都不是,和@Valid没关系,那就返回null
return null;
}
// 如果上面找校验分组找到了,那就
if (validationHints != null) {
// 按照分组,使用DataBinder开始校验
binder.validate(validationHints);{
// 获取要校验的目标对象
Object target = getTarget();
// 获取在binder中创建的BindingResult获取出来,用来存储校验失败的结果
BindingResult bindingResult = getBindingResult();
// private final List<Validator> validators = new ArrayList<>();
// 获取所有的校验器对参数一一校验
for (Validator validator : getValidators()) {
// 存在校验分组,并且为智能类型SmartValidator,调用SmartValidator的方法
if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
((SmartValidator) validator).validate(target, bindingResult, validationHints);{
if (this.targetValidator != null) {
// 调用目标校验器对参数进行校验,返回Set<ConstraintViolation<Object>> violations
// ConstraintViolation一个描述单个约束失败的类,验证一个对象将会返回ConstraintViolation的集合
processConstraintViolations(this.targetValidator.validate(target), errors);{
// 遍历所有的错误消息
for (ConstraintViolation<Object> violation : violations) {
// 获取错误的字段
String field = determineField(violation);
// 获取字段的错误信息
FieldError fieldError = errors.getFieldError(field);
if (fieldError == null || !fieldError.isBindingFailure()) {
ConstraintDescriptor<?> cd = violation.getConstraintDescriptor();
String errorCode = determineErrorCode(cd);
Object[] errorArgs = getArgumentsForConstraint(errors.getObjectName(), field, cd);
// 判断错误类型是不是BindingResult,如果是,将错误结果放入BindingResult中
// 因为BindingResult也是一个Errors类型
if (errors instanceof BindingResult) {
BindingResult bindingResult = (BindingResult) errors;
String nestedField = bindingResult.getNestedPath() + field;
// 嵌套字段是否为空
if (nestedField.isEmpty()) {
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode);
ObjectError error = new ViolationObjectError(errors.getObjectName(), errorCodes, errorArgs, violation, this);
bindingResult.addError(error);
}
// 非嵌套字段
else {
Object rejectedValue = getRejectedValue(field, violation, bindingResult);
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
FieldError error = new ViolationFieldError(errors.getObjectName(), nestedField,rejectedValue, errorCodes, errorArgs, violation, this);
bindingResult.addError(error);
}
}
else {
// 将数据添加到errors中
errors.rejectValue(field, errorCode, errorArgs, violation.getMessage());
}
}
}
}
}
}
// 其他情况直接调用校验方法
else if (validator != null) {
validator.validate(target, bindingResult);
}
}
}
break;
}
}
}
// 上面校验的结果已经存在BindingResult中了,如果存在错误,
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter){
// 获取参数的索引
int i = parameter.getParameterIndex();
// 获取参数类型
Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
// 如果被校验的参数紧接着的下一个参数不是Errors(BindingResult)对象,
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
// 上面条件为true表示下一个参数是BindingResult
// !hasBindingResult表示校验的参数之后紧挨的不是BindingResult
return !hasBindingResult;
}) {
// 表示校验出错,并且校验的参数紧挨着的对象没有用BindingResult接受错误信息,就抛出异常
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
// 将BindingResult的数据存入mavContainer的属性中,哪怕是没有错误,也会根据这个固定的key存入
// String MODEL_KEY_PREFIX = BindingResult.class.getName() + ".";
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
// 看一下参数是否需要适配并返回
// 如果参数是Optional,我们处理参数之后,也要包装为Optional还给用户
// 如果不是,就原样返回
return adaptArgumentIfNecessary(arg, parameter);
}
// 3. 讲解RequestParam,和1. PathVariable逻辑一样,相同的父类,以下逻辑不同,是抽象的
// 不光会处理普通参数,还会处理文件类型参数
// RequestParamMethodArgumentResolver
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);{
// 根据参数名解析参数值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);{
// 获取原生的HttpServletRequest对象
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if (servletRequest != null) {
// 解析文件参数,先看一下是不是文件
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);{
// 在请求的时候,如果是文件请求,这个对象已经被包装过了
MultipartHttpServletRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
// 判断是否是文件请求的依据有两个,一个是之前校验的请求对象封装成了MultipartHttpServletRequest
// 另一个是根据contentType.toLowerCase().startsWith("multipart/"));
boolean isMultipart = (multipartRequest != null || isMultipartContent(request));
// 获取参数类型,List<MultipartFile>,MultipartFile[]逻辑一样,增加了类型判断,这里省去了重复代码
if (MultipartFile.class == parameter.getNestedParameterType()) {
// 如果你不是文件类型的请求,那么你用这个参数也没有用
if (!isMultipart) {
return null;
}
// 之前没有将HttpServletRequest包装过包装一下
if (multipartRequest == null) {
multipartRequest = new StandardMultipartHttpServletRequest(request);
}
// 通过封装的请求对象获取文件对象
// 根据不同的文件解析器创建文件对象的方式不一样
// 在请求进来的时候,文件已经被解析过程,multipartResolver解析之后,将文件给MultipartHttpServletRequest对象了
// 也就是multipartRequest对象
return multipartRequest.getFile(name);{
// 从请求对象中获取文件流数据
Collection<Part> parts = request.getParts();
this.multipartParameterNames = new LinkedHashSet<>(parts.size());
// 每一个文件就是一个Part
for (Part part : parts) {
// 解析请求头
String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
ContentDisposition disposition = ContentDisposition.parse(headerValue);
// 获取文件名
String filename = disposition.getFilename();
if (filename != null) {
// 创建StandardMultipartFile对象
files.add(part.getName(), new StandardMultipartFile(part, filename));
}
}
// 保存解析到的所有文件对象
setMultipartFiles(files);
}
}
// 如果文件类型直接为Part,数组,集合
else if (Part.class == parameter.getNestedParameterType()) {
if (!isMultipart) {
return null;
}
// 直接从request对象中就能获取
// 如果是数组,或者集合,就要通过request.getParts();再进行遍历包装返回
return request.getPart(name);
}
// 没有解析到文件
else {
return UNRESOLVABLE;
}
}
// 没有解析到文件
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
}
Object arg = null;
MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
// 获取文件参数
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
Object arg = (files.size() == 1 ? files.get(0) : files);
}
}
// 获取普通参数
if (arg == null) {
String[] paramValues = request.getParameterValues(name);
ifObject (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
}
// 4. 讲解没有注解
// 使用的实ServletModelAttributeMethodProcessor,它是在Apdate中添加的兜底的处理器
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);{
// 获取参数名
String name = ModelFactory.getNameForParameter(parameter);
// 获取是否存在ModelAttribute注解
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
// 如果不为空,保存注解的binding信息
if (ann != null) {
// 表示当前参数需要被绑定,需要存到model中
mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
// 如果mavContainer已经存在这个属性
if (mavContainer.containsAttribute(name)) {
// 直接从Model获取值
attribute = mavContainer.getModel().get(name);
}
else {
try {
// 创建参数实例对象,找默认的构造方法进行实例化参数对象
// 在创建对象的过程中也会去参数进行校验等等
attribute = createAttribute(name, parameter, binderFactory, webRequest);{
// 从请求中获取数据,request.getParam
String value = getRequestValueForAttribute(attributeName, request);
if (value != null) {
// 通过获取到的值进行类型转换成目标值
Object attribute = createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);{
// 创建Binder实例
DataBinder binder = binderFactory.createBinder(request, null, attributeName);
// 创建类型转换类,Spring专门提供类型转换的类
ConversionService conversionService = binder.getConversionService();
if (conversionService != null) {
// 接收的数据类型(req获取的都是String)
TypeDescriptor source = TypeDescriptor.valueOf(String.class);
// 需要转换的目标类型(参数类型)
TypeDescriptor target = new TypeDescriptor(parameter);
// 判断是否可以进行转换
// 如果可以转换将数据类型以及目标类型封装成CacheKey,转换器作为值缓存起来,不需要每次都找
// 无参构造找不到对应的转换器,只有有参构造才能找到对应的Convert
// 对于无参构造,都是调用下面的super.createAttribute,来创建空参数的对象,所以无需进行转换
if (conversionService.canConvert(source, target)) {
// 调用binder的转换方法,上面已经讲过了
/ 对参数进行适配,利用Spring自身提供的类型转换机制来就参数进行类型转换
// 也可以@InitBinder获取的WebDataBinder对象,设置一个相关的转换器来处理Spring没有提供的默认的转换器
// 例如:将String->枚举,Spring可能就没有提供,自己就可以自定义转换器,设置到WebDataBinder binder中
// 这样就能就String转换为枚举类型
return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
}
}
return null;
}
// 如果转换成了目标对象,就直接返回
if (attribute != null) {
return attribute;
}
}
// 没有转换成功,也就是上面Spring没有提供对应的类型转换器,这里就对类型进行通用处理,来创建参数对象
return super.createAttribute(attributeName, parameter, binderFactory, request);{
// 获取参数类型
MethodParameter nestedParameter = parameter.nestedIfOptional();
Class<?> clazz = nestedParameter.getNestedParameterType();
// 获取参数类型的构造方法,如果有无参构造方法,优先选择无参构造
Constructor<?> ctor = BeanUtils.getResolvableConstructor(clazz);
// 通过构造方法创建对象
// 如果有默认构造方法,直接使用默认的构造方法
// 如果不是默认构造方法,从request获取对应的值来创建实例对象
Object attribute = constructAttribute(ctor, attributeName, parameter, binderFactory, webRequest);
if (parameter != nestedParameter) {
attribute = Optional.of(attribute);
}
return attribute;
}
}
}
// 发生异常
catch (BindException ex) {
// 如果发生BindException异常了,判断是否有BindingResult接收,如果没有,抛出异常
if (isBindExceptionRequired(parameter)) {
throw ex;
}
// 其他情况,存在BindingResult接收异常信息,该怎么返回怎么返回,错误结果已经绑定到BindingResult中了
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
else {
attribute = ex.getTarget();
}
bindingResult = ex.getBindingResult();
}
}
// 如果没有异常,正常创建了参数对象
if (bindingResult == null) {
// 创建Binder实例,通过Binder来对参数进行校验
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
// 如果ModelAttribute设置了binding=false(默认为true),表示当前ModelAttribute标注的参数不能被绑定数据
if (!mavContainer.isBindingDisabled(name)) {
// 将请求参数的值一一绑定到参数中
bindRequestParameters(binder, webRequest);
}
// 对参数进行校验,如果有异常,会绑定到BindingResult中
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// 创建好了WebDataBinder之后,对参数进行转换,如果有必要的话
// 这个就是利用Spring自身提供的类型转换机制来就参数进行类型转换
if (!parameter.getParameterType().isInstance(attribute)) {
// 对参数进行适配,利用Spring自身提供的类型转换机制来就参数进行类型转换
// 也可以@InitBinder获取的WebDataBinder对象,设置一个相关的转换器来处理Spring没有提供的默认的转换器
// 例如:将String->枚举,Spring可能就没有提供,自己就可以自定义转换器,设置到WebDataBinder binder中
// 这样就能就String转换为枚举类型
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
// 获取BindingResult
bindingResult = binder.getBindingResult();
}
// 同步Model数据,将bindingResult的Model数据同步到mavContainer中
Map<String, Object> bindingResultModel = bindingResult.getModel();{
// 默认就将当前参数的实例对象和BindingResult存入到Model中了
Map<String, Object> model = new LinkedHashMap<>(2);
model.put(getObjectName(), getTarget());
model.put(MODEL_KEY_PREFIX + getObjectName(), this);
return model;
}
// 同步Model数据,将bindingResult的Model数据同步到mavContainer中
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
// 返回处理好的参数
return attribute;
}
}
}
catch (Exception ex) {
throw ex;
}
}
return args;
}
// 反射执行
return doInvoke(args);
}
执行目标方法InvocableHandlerMethod#invokeForRequest原理(最终参数解析,执行目标方法)
最新推荐文章于 2024-08-06 11:46:17 发布