SpringMVC的九大组件
/** 文件上传解析器*/
private MultipartResolver multipartResolver;
/** 区域信息解析器;和国际化有关 */
private LocaleResolver localeResolver;
/** 主题解析器;强大的主题效果更换 */
private ThemeResolver themeResolver;
/** Handler映射信息;HandlerMapping */
private List<HandlerMapping> handlerMappings;
/** Handler的适配器 */
private List<HandlerAdapter> handlerAdapters;
/** SpringMVC强大的异常解析功能;异常解析器 */
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** */
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMap+Manager:SpringMVC中运行重定向携带数据的功能 */
private FlashMapManager flashMapManager;
/** 视图解析器; */
private List<ViewResolver> viewResolvers;
补充:SpringMVC在工作的时候,关键位置都是由这些组件完成的;
共同点:九大组件全部都是接口;接口就是规范;提供了非常强大的扩展性;
可以理解是SpringMVC在启动完成后存储各种解析器、适配器的地方
九大组件的初始化
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
组价的初始化细节:以initHandlerMappings举例,上一篇源码的后面也有一样的
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
--
detectAllHandlerMappings默认是ture,当然也可以在配置文件中修改
/**
*可以在web.xml中修改DispatcherServlet某些属性的默认配置;
*<init-param>
* <param-name>detectAllHandlerMappings</param-name>
* <param-value>false</param-value>
* </init-param>
/
--
if (this.detectAllHandlerMappings) {
--
在ApplicationContext找到所有HandlerMapping,包含所有的子类
--
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
--
//如果你的配置文件里什么都没有,此时就会为空
--
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
--
将所有的HandlerMapping按优先级排序
--
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
--
//从context中拿名为handlerMapping,HandlerMapping.class的bean组件,
//并进行加入到handlerMappings,如果还是没有,下面也会有默认的实现策略
--
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
--
//默认的实现策略
//默认的策略配置在DispatcherServlet.properties中已经是写好了的,
//所有的组件基本都是没有就按默认的策略实现。除了MultipartResolver
--
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
总结:如果你的xml文件里什么都没有,也会使用默认的策略进行组件的注册
目标方法的执行(利用反射)
eg:
@RequestMapping(value = "/getBook")
public Book getBook(@RequestParam("name") String name,
Map map,
HttpServletRequest request,
@ModelAttribute("book") Book book) {
System.out.println("getBook-----");
return book;
}
@ModelAttribute
public void initParams(Map map){
map.put("book", new Book("西游记","吴承恩",99,100,100));
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
---->return handleInternal(request, response, (HandlerMethod) handler);
---->mav = invokeHandlerMethod(request, response, handlerMethod);
handleInternal
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
--
定义了一个ModelAndView,后续用以返回
--
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...
--
个人测试,没有锁也没有配置synchronizeOnSession,按默认走这里
--
mav = invokeHandlerMethod(request, response, handlerMethod);
}
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 {
--
包装原生的request, response
--
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
--
获取数据绑定工厂
--
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
--
获取模型工厂,并将标注了@ModelAttribute注解的方法保存在工厂中
--
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
--
//new一个ModelAndView容器,在内部还new BindingAwareModelMap()。
--
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
--
//填充模型,map、model中的数据能在方法运行前就存在,就是因为在此进行了填充
--
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
--
//中间一大段异步的代码,删掉,节约空间
--
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
--
//执行目标方法getBook
--
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
initModel
--
按以下顺序填充模型: 检索列出为@SessionAttributes的“已知”会话属性。
调用@ModelAttribute方法 找到@ModelAttribute方法参数也作为@SessionAttributes列出,
并确保它们在必要时引发异常的模型中存在。
参数:
请求——当前请求
容器——带有要初始化的模型的容器
handlerMethod——用于初始化模型的方法
抛出: 异常-可能由@ModelAttribute方法引起
--
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
container.mergeAttributes(sessionAttributes);
--
//执行标注了@ModelAttribute注解的方法·
--
invokeModelAttributeMethods(request, container);
--
//根据处理的方法的参数,查找是否是属于@SessionAttribute中的value的值
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!container.containsAttribute(name)) {
--
//从后端会话检索指定的属性。 调用此方法时通常期望属性已经存在,
//如果该方法返回null则抛出异常。
//container中有的只是当前请求的,sessionAttributes中有整个会话的、
//所以说不建议使用sessionAttributes注解,因为使用了就想当于保证了
//有某一个值,没有就会报错
//一定要写的话,也可以,但不要标注value的值
--
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
container.addAttribute(name, value);
}
}
}
invokeModelAttributeMethods
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
throws Exception {
--
//获取模型工厂的时候如果扫描到了标注@ModelAttribute注解的方法,
//modelMethods这个ArrayList里就会有值
--
while (!this.modelMethods.isEmpty()) {
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
--
//查找@ModelAttribute标注的name的值,有就进行绑定
--
ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
Assert.state(ann != null, "No ModelAttribute annotation");
if (container.containsAttribute(ann.name())) {
if (!ann.binding()) {
container.setBindingDisabled(ann.name());
}
continue;
}
--
//利用反射执行@ModelAttribute标注的方法,并将可能有的数据保存进defaultModel中
//即保存进入参中的map、model中
--
Object returnValue = modelMethod.invokeForRequest(request, container);
--
//返回值类型如果是void,则不处理
--
if (!modelMethod.isVoid()){
-----
-----
}
invokeAndHandle
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);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
--
//根据你的返回值,返回值类型,选择合适的返回值解析器进行解析以及返回数值绑定
--
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
invokeForRequest执行请求方法
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
--
//解析参数值
--
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
--
//反射执行目标方法
--
return doInvoke(args);
}
getMethodArgumentValues获取方法传递的参数
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
if (ObjectUtils.isEmpty(getMethodParameters())) {
return EMPTY_ARGS;
}
--
//获取方法传递的参数,包括参数携带的注解
--
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] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
--
//给定的方法参数是否被任何注册的HandlerMethodArgumentResolver所支持。
//支持则放入缓存中
--
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
--
//解析参数
//如果在在请求参数中传入的字段有和defaultModel(即之前@ModelAttribute加入的值)的字段一样
//会在内部调用底层的doBind方法进行数据绑定
//eg:例子中在请求参数中加入了name属性,book中又包含有name字段,此时会调用底层的方法进行绑定覆盖
//如果在defaultModel没有这样的值,那么调用createAttribute创建一个实例
--
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled..
if (logger.isDebugEnabled()) {
String error = ex.getMessage();
if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, error));
}
}
throw ex;
}
}
return args;
}
getMethodParameters获取方法的参数值
supportsParameter找到一个已注册的HandlerMethodArgumentResolver,它支持给定的方法参数。
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
--
//具体的实现判断看supportsParameter
--
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
Spring5.0的基本流程总结
1.如果有方法有标注@ModelAttribute注解,在请求执行前,
提前运行此方法,并将执行中要保存在defaultModel中的数据加入进去。
2.如果有ModelAttribute注解标注的参数,会在initModel方法执行的时候调用
container.mergeAttributes(sessionAttributes);合并到defaultModel中。
2.1如果此方法有返回值,那么返回值也会保存在defaultModel中
3.执行目标方法,确定目标方法的值
3.1无论参数是否有注解,统一使用this.resolvers.supportsParameter(parameter)判定,
但如果参数的的首字母小写的值与@SessionAttributes中某个value值一样,
那么会在container中找,找不到就抛异常
3.2如果有注解且标注了name值,那么尝试用标注的name值从defaultModel获取;
如果没有注解,按参数的首字母小写作为name从defaultModel获取。
3.3 如果是自定义类型参数,能从defaultModel中获取则获取并调用doBind进行数据绑定,
如果获取不到,则利用反射创建一个对象。
this.resolvers.supportsParameter(parameter)判定使用的类型适配
4.0版本
invokeHandlerMethod
public String updateBook(@RequestParam(value="author")String author,Map<String, Object> model,
HttpServletRequest request, @ModelAttribute("haha")Book book
)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());执行目标方法的细节;
||
\/
return invokeHandlerMethod(request, response, handler);
||
\/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//拿到方法的解析器
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
//方法解析器根据当前请求地址找到真正的目标方法
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
//创建一个方法执行器;
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
//包装原生的request, response,
ServletWebRequest webRequest = new ServletWebRequest(request, response);
//创建了一个,隐含模型
ExtendedModelMap implicitModel = new BindingAwareModelMap();
//真正执行目标方法;目标方法利用反射执行期间确定参数值,
//提前执行modelattribute等所有的操作都在这个方法中;
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
}
方法执行的细节invokeHandlerMethod
方法执行的细节invokeHandlerMethod
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
try {
boolean debug = logger.isDebugEnabled();
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
if (attrValue != null) {
implicitModel.addAttribute(attrName, attrValue);
}
}
//找到所有@ModelAttribute注解标注的方法;
for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
//先确定modelattribute方法执行时要使用的每一个参数的值;
Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
}
String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
continue;
}
ReflectionUtils.makeAccessible(attributeMethodToInvoke);
//提前运行ModelAttribute,
Object attrValue = attributeMethodToInvoke.invoke(handler, args);
if ("".equals(attrName)) {
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
}
//把提前运行的ModelAttribute方法的返回值也放在隐含模型中
if (!implicitModel.containsAttribute(attrName)) {
implicitModel.addAttribute(attrName, attrValue);
}
}
//再次解析目标方法参数是哪些值
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
}
ReflectionUtils.makeAccessible(handlerMethodToInvoke);
//执行目标方法
return handlerMethodToInvoke.invoke(handler, args);
}
catch (IllegalStateException ex) {
// Internal assertion failed (e.g. invalid signature):
// throw exception with full handler method context...
throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
}
catch (InvocationTargetException ex) {
// User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
ReflectionUtils.rethrowException(ex.getTargetException());
return null;
}
}
确定方法运行时使用的每一个参数的值
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
Class<?>[] paramTypes = handlerMethod.getParameterTypes();
//创建了一个和参数个数一样多的数组,会用来保存每一个参数的值
Object[] args = new Object[paramTypes.length];
for (int i = 0; i < args.length; i++) {
MethodParameter methodParam = new MethodParameter(handlerMethod, i);
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
String paramName = null;
String headerName = null;
boolean requestBodyFound = false;
String cookieName = null;
String pathVarName = null;
String attrName = null;
boolean required = false;
String defaultValue = null;
boolean validate = false;
Object[] validationHints = null;
int annotationsFound = 0;
Annotation[] paramAnns = methodParam.getParameterAnnotations();
//找到目标方法这个参数的所有注解,如果有注解就解析并保存注解的信息;
for (Annotation paramAnn : paramAnns) {
if (RequestParam.class.isInstance(paramAnn)) {
RequestParam requestParam = (RequestParam) paramAnn;
paramName = requestParam.value();
required = requestParam.required();
defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
annotationsFound++;
}
else if (RequestHeader.class.isInstance(paramAnn)) {
RequestHeader requestHeader = (RequestHeader) paramAnn;
headerName = requestHeader.value();
required = requestHeader.required();
defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
annotationsFound++;
}
else if (RequestBody.class.isInstance(paramAnn)) {
requestBodyFound = true;
annotationsFound++;
}
else if (CookieValue.class.isInstance(paramAnn)) {
CookieValue cookieValue = (CookieValue) paramAnn;
cookieName = cookieValue.value();
required = cookieValue.required();
defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
annotationsFound++;
}
else if (PathVariable.class.isInstance(paramAnn)) {
PathVariable pathVar = (PathVariable) paramAnn;
pathVarName = pathVar.value();
annotationsFound++;
}
else if (ModelAttribute.class.isInstance(paramAnn)) {
ModelAttribute attr = (ModelAttribute) paramAnn;
attrName = attr.value();
annotationsFound++;
}
else if (Value.class.isInstance(paramAnn)) {
defaultValue = ((Value) paramAnn).value();
}
else if (paramAnn.annotationType().getSimpleName().startsWith("Valid")) {
validate = true;
Object value = AnnotationUtils.getValue(paramAnn);
validationHints = (value instanceof Object[] ? (Object[]) value : new Object[] {value});
}
}
if (annotationsFound > 1) {
throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
"do not specify more than one such annotation on the same parameter: " + handlerMethod);
}
//没有找到注解的情况;
if (annotationsFound == 0) {
//解析普通参数
Object argValue = resolveCommonArgument(methodParam, webRequest);--
会进入resolveStandardArgument(解析标准参数)
if (argValue != WebArgumentResolver.UNRESOLVED) {
args[i] = argValue;
}
else if (defaultValue != null) {
args[i] = resolveDefaultValue(defaultValue);
}
else {
Class<?> paramType = methodParam.getParameterType();
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
if (!paramType.isAssignableFrom(implicitModel.getClass())) {
throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " +
"Model or Map but is not assignable from the actual model. You may need to switch " +
"newer MVC infrastructure classes to use this argument.");
}
args[i] = implicitModel;
}
else if (SessionStatus.class.isAssignableFrom(paramType)) {
args[i] = this.sessionStatus;
}
else if (HttpEntity.class.isAssignableFrom(paramType)) {
args[i] = resolveHttpEntityRequest(methodParam, webRequest);
}
else if (Errors.class.isAssignableFrom(paramType)) {
throw new IllegalStateException("Errors/BindingResult argument declared " +
"without preceding model attribute. Check your handler method signature!");
}
else if (BeanUtils.isSimpleProperty(paramType)) {
paramName = "";
}
else {
attrName = "";
}
}
}
//确定值的环节
if (paramName != null) {
args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
}
else if (headerName != null) {
args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
}
else if (requestBodyFound) {
args[i] = resolveRequestBody(methodParam, webRequest, handler);
}
else if (cookieName != null) {
args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
}
else if (pathVarName != null) {
args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
}
//确定自定义类型参数的值;还要将请求中的每一个参数赋值给这个对象
else if (attrName != null) {
WebDataBinder binder =
resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
if (binder.getTarget() != null) {
doBind(binder, webRequest, validate, validationHints, !assignBindingResult);
}
args[i] = binder.getTarget();
if (assignBindingResult) {
args[i + 1] = binder.getBindingResult();
i++;
}
implicitModel.putAll(binder.getBindingResult().getModel());
}
}
return args;
}
如果没有注解:
1)、先看是否普通参数(resolveCommonArgument);----就是确定当前的参数是否是原生API;
@Override
protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
if (ServletRequest.class.isAssignableFrom(parameterType) ||
MultipartRequest.class.isAssignableFrom(parameterType)) {
Object nativeRequest = webRequest.getNativeRequest(parameterType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + parameterType.getName() + "]: " + request);
}
return nativeRequest;
}
else if (ServletResponse.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
Object nativeResponse = webRequest.getNativeResponse(parameterType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + parameterType.getName() + "]: " + response);
}
return nativeResponse;
}
else if (HttpSession.class.isAssignableFrom(parameterType)) {
return request.getSession();
}
else if (Principal.class.isAssignableFrom(parameterType)) {
return request.getUserPrincipal();
}
else if (Locale.class.equals(parameterType)) {
return RequestContextUtils.getLocale(request);
}
else if (InputStream.class.isAssignableFrom(parameterType)) {
return request.getInputStream();
}
else if (Reader.class.isAssignableFrom(parameterType)) {
return request.getReader();
}
else if (OutputStream.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getOutputStream();
}
else if (Writer.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getWriter();
}
return super.resolveStandardArgument(parameterType, webRequest);
}
2)、判断是否是Model或者是Map旗下的,如果是将之前创建的隐含模型直接赋值给这个参数;
方法上标注的ModelAttribute注解如果有value值
@ModelAttribute("abc")
hahaMyModelAttribute()
标了 : attrName="abc"
没标:attrName="";attrName就会变为返回值类型首字母小写,比如void ,或者book;
【@ModelAttribute标在方法上的另外一个作用;
可以把方法运行后的返回值按照方法上@ModelAttribute("abc")指定的key放到隐含模型中;
如果没有指定这个key;就用返回值类型的首字母小写】
{haha=Book [id=100, bookName=西游记, author=吴承恩, stock=98, sales=10, price=98.98], void=null}
如何确定方法的每一个参数的值;
标了注解:
保存时哪个注解的详细信息;
如果参数有ModelAttribute注解;
拿到ModelAttribute注解的值让attrName保存
attrName="haha"
没标注解:
1)、先看是否普通参数(是否原生API)
再看是否Model或者Map,如果是就传入隐含模型;
2)、自定义类型的参数没有ModelAttribute 注解
1)、先看是否原生API
2)、再看是否Model或者Map
3)、再看是否是其他类型的比如SessionStatus、HttpEntity、Errors
4)、再看是否简单类型的属性;比如是否Integer,String,基本类型
如果是paramName=“”
5)、attrName="";
如果是自定义类型对象,最终会产生两个效果;
1)、如果这个参数标注了ModelAttribute注解就给attrName赋值为这个注解的value值
2)、如果这个参数没有标注ModelAttribute注解就给attrName赋值"";
确定自定义类型参数的值;
SpringMVC确定POJO值的三步;
1、如果隐含模型中有这个key(标了ModelAttribute注解就是注解指定的value,没标就是参数类型的首字母小写)指定的值;
如果有将这个值赋值给bindObject;
2、如果是SessionAttributes标注的属性,就从session中拿;
3、如果都不是就利用反射创建对象;
private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
// Bind request parameter onto object...
String name = attrName;
if ("".equals(name)) {
//如果attrName是空串;就将参数类型的首字母小写作为值 Book book2121;name=book
name = Conventions.getVariableNameForParameter(methodParam);
}
Class<?> paramType = methodParam.getParameterType();
Object bindObject;确定目标对象的值
if (implicitModel.containsKey(name)) {
bindObject = implicitModel.get(name);
}
else if (this.methodResolver.isSessionAttribute(name, paramType)) {
bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
if (bindObject == null) {
raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
}
}
else {
bindObject = BeanUtils.instantiateClass(paramType);
}
WebDataBinder binder = createBinder(webRequest, bindObject, name);
initBinder(handler, name, binder, webRequest);
return binder;
}
Spring4.0中的基本流程
两件事:
1)、运行流程简单版;
2)、确定方法每个参数的值;
1)、标注解:保存注解的信息;最终得到这个注解应该对应解析的值;
2)、没标注解:
1)、看是否是原生API;
2)、看是否是Model或者是Map,xxxx
3)、都不是,看是否是简单类型;paramName;
4)、给attrName赋值;attrName(参数标了@ModelAttribute("")就是指定的,没标就是"")
确定自定义类型参数:
1)、attrName使用参数的类型首字母小写;或者使用之前@ModelAttribute("")的值
2)、先看隐含模型中有每个这个attrName作为key对应的值;如果有就从隐含模型中获取并赋值
3)、看是否是@SessionAttributes(value="haha");标注的属性,如果是从session中拿;
如果拿不到就会抛异常;
4)、不是@SessionAttributes标注的,利用反射创建一个对象;
5)、拿到之前创建好的对象,使用数据绑定器(WebDataBinder)将请求中的每个数据绑定到这个对象中;
Spring 4版本和Spring 5版本部分的流程差别
有差异的流程 | Spring4.0 | Spring5.0 |
---|---|---|
确定pojo | 如果隐含模型中有这个key(标了ModelAttribute注解就是注解指定的value,没标就是参数类型的首字母小写)指定的值;如果有将这个值赋值给bindObject;如果是SessionAttributes标注的属性,就从session中拿;如果都不是就利用反射创建对象 | 在任何方法执行的时候都会调用initModel方法,将SessionAttributes中的值合并到container中,那么后续就不会再有从session的操作,直接从模型中取,没有就直接创建对象 |
确定入参的参数类型 | 有一大长串的代码进行类型的判定 | 在扫描参数的时候,将入参的所有信息都保存,然后使用this.resolvers.supportsParameter(parameter)增强for循环进行判定 |
SessionAttributes获取值问题 | 同右 | 调用任何一个方法,如果方法参数带有ModelAttribute注解且vlaue值和SessionAttributes中有一样的,那么就会扫描一遍mavcontainer中是否有标注value一样名字的属性,找不到直接抛异常 |
标注ModelAttribute注解方法的返回值为 viod是否保存在defaultModel | 保存 ,其他返回值也保存 , 如String 等 | 不保存 ,只保存其他返回值,如String 等 |