之前我們討論了Spring中如何通過驗證器來達(dá)到校驗?zāi)康耐芄 F渲杏袔仔形覀兲岬搅薂ModelAttribute注解缺虐。但是割坠,單單理解這個概念還不夠抽诉,總感覺飄如浮萍陨簇。
本文將對@ModelAttribute進(jìn)行解析。將分為兩部分迹淌。首先將介紹此注解的用法河绽。第二部分將通過具體的代碼來分析這個注解和其相應(yīng)的解析器的細(xì)節(jié)己单。
什么是@ModelAttribute注解?
@ModelAttribute注解主要用來將請求轉(zhuǎn)換為使用此注解指定的對象葵姥。例如荷鼠,如果在@ModelAttribute旁邊指定了一個Article實例,則與Article的字段對應(yīng)的所有請求參數(shù)將被用作Article的字段值榔幸。什么意思呢允乐,例如,POST提交后參數(shù)title的值將被設(shè)置為Article的title 字段削咆。這里推薦一篇文章解釋的很清晰:http://blog.csdn.net/hejingyuan6/article/details/49995987**
因此婚脱,此注解允許開發(fā)人員通過請求來持久化一個對象挚赊。沒有它梨睁,Spring認(rèn)為必須創(chuàng)建一個新對象盛嘿。另外,它直接顯示一個對象模型來查看瞻惋。你不需要在方法中再調(diào)用model.setAttribute()厦滤。在視圖部分,可以通過注解中的指定值查找指定對象(例如歼狼,@ModelAttribute(“articleView”)可以在jsp中通過${articleView}獲取相應(yīng)的值)或?qū)ο蟮念惷Q(例如@ModelAttribute()Article article將在視圖層獲取方式就是${article})掏导。
@ModelAttribute注解相關(guān)代碼詳解
還是分兩波來說吧,也參考了不少其他解析的文章羽峰,看了很多相關(guān)評論趟咆,大都覺得各種迷迷糊糊所以就舊版新版都說說咯,反正都是源碼學(xué)習(xí)梅屉,4.3版本之前和之后(4.2之后機制已經(jīng)改了值纱,下面講新版本的時候會看到源碼相關(guān)注釋),4.3版本之后被廢棄掉了坯汤,先談老版本的虐唠。
老版本
總體來看,有三個關(guān)鍵類協(xié)助@ModelAttribute來轉(zhuǎn)換得到我們所需要的預(yù)期對象惰聂。第一個是org.springframework.web.bind.annotation.support.HandlerMethodResolver凿滤。它包含一個Set類型的私有字段,稱為modelAttributeMethods庶近。此字段包含被@ModelAttribute注解了的方法。在init()方法中眷蚓,解析器將所有相關(guān)方法放在此集合中鼻种。
private final Set<Method> modelAttributeMethods = new LinkedHashSet<Method>();
...
/**
* Initialize a new HandlerMethodResolver for the specified handler type.
* @param handlerType the handler class to introspect
*/
public void init(final Class<?> handlerType) {
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
Class<?> specificHandlerType = null;
if (!Proxy.isProxyClass(handlerType)) {
handlerTypes.add(handlerType);
specificHandlerType = handlerType;
}
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (isHandlerMethod(specificMethod) &&
(bridgedMethod == specificMethod || !isHandlerMethod(bridgedMethod))) {
handlerMethods.add(specificMethod);
}
else if (isInitBinderMethod(specificMethod) &&
(bridgedMethod == specificMethod || !isInitBinderMethod(bridgedMethod))) {
initBinderMethods.add(specificMethod);
}
//此處代碼可知
else if (isModelAttributeMethod(specificMethod) &&
(bridgedMethod == specificMethod || !isModelAttributeMethod(bridgedMethod))) {
modelAttributeMethods.add(specificMethod);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
this.typeLevelMapping = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
SessionAttributes sessionAttributes = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
this.sessionAttributesFound = (sessionAttributes != null);
if (this.sessionAttributesFound) {
this.sessionAttributeNames.addAll(Arrays.asList(sessionAttributes.names()));
this.sessionAttributeTypes.addAll(Arrays.asList(sessionAttributes.types()));
}
}
之后,org.springframework.web.bind.annotation.support.HandlerMethodInvoker就可以開始干活了沙热。在其方法invokeHandlerMethod()中叉钥,它從modelAttributeMethods Set遍歷所有方法罢缸。如果之前model屬性沒有解析,它將通過創(chuàng)建對象來將請求參數(shù)綁定到對象的相應(yīng)字段投队。
for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
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);
Object attrValue = attributeMethodToInvoke.invoke(handler, args);
if ("".equals(attrName)) {
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
}
if (!implicitModel.containsAttribute(attrName)) {
implicitModel.addAttribute(attrName, attrValue);
}
}
通過org.springframework.web.method.annotation.ModelAttributeMethodProcessor來做綁定枫疆。更確切地說,它是通過方法protected void bindRequestParameters(WebDataBinder binder敷鸦,NativeWebRequest request)來將請求綁定到目標(biāo)對象息楔。而更準(zhǔn)確地說,它使用WebRequestDataBinder的bind()方法來做到這一點扒披。
/**
* Extension point to bind the request to the target object.
* @param binder the data binder instance to use for the binding
* @param request the current request
*/
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
((WebRequestDataBinder) binder).bind(request);
}
...
/**
* 此處會在新版本的最后提到值依,到時可從此開始看的
* Resolve the argument from the model or if not found instantiate it with
* its default if it is available. The model attribute is then populated
* with request values via data binding and optionally validated
* if {@code @java.validation.Valid} is present on the argument.
* @throws BindException if data binding and validation result in an error
* and the next method parameter is not of type {@link Errors}.
* @throws Exception if WebDataBinder initialization fails.
*/
@Override
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String name = ModelFactory.getNameForParameter(parameter);
Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) :
createAttribute(name, parameter, binderFactory, webRequest));
if (!mavContainer.isBindingDisabled(name)) {
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null && !ann.binding()) {
mavContainer.setBindingDisabled(name);
}
}
//此處來做綁定
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
WebRequestDataBinder的bind()
/**
* Bind the parameters of the given request to this binder's target,
* also binding multipart files in case of a multipart request.
* <p>This call can create field errors, representing basic binding
* errors like a required field (code "required"), or type mismatch
* between value and bean property (code "typeMismatch").
* <p>Multipart files are bound via their parameter name, just like normal
* HTTP parameters: i.e. "uploadedFile" to an "uploadedFile" bean property,
* invoking a "setUploadedFile" setter method.
* <p>The type of the target property for a multipart file can be Part, MultipartFile,
* byte[], or String. The latter two receive the contents of the uploaded file;
* all metadata like original file name, content type, etc are lost in those cases.
* @param request request with parameters to bind (can be multipart)
* @see org.springframework.web.multipart.MultipartRequest
* @see org.springframework.web.multipart.MultipartFile
* @see javax.servlet.http.Part
* @see #bind(org.springframework.beans.PropertyValues)
*/
public void bind(WebRequest request) {
MutablePropertyValues mpvs = new MutablePropertyValues(request.getParameterMap());
if (isMultipartRequest(request) && request instanceof NativeWebRequest) {
MultipartRequest multipartRequest = ((NativeWebRequest) request).getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
else if (servlet3Parts) {
HttpServletRequest serlvetRequest = ((NativeWebRequest) request).getNativeRequest(HttpServletRequest.class);
new Servlet3MultipartHelper(isBindEmptyMultipartFiles()).bindParts(serlvetRequest, mpvs);
}
}
doBind(mpvs);
}
跟著源碼 再追下去的話,會發(fā)現(xiàn)在其父類DataBinder中:
/**
* Actual implementation of the binding process, working with the
* passed-in MutablePropertyValues instance.
* @param mpvs the property values to bind,
* as MutablePropertyValues instance
* @see #checkAllowedFields
* @see #checkRequiredFields
* @see #applyPropertyValues
*/
protected void doBind(MutablePropertyValues mpvs) {
checkAllowedFields(mpvs);
checkRequiredFields(mpvs);
applyPropertyValues(mpvs);
}
DataBinder的applyPropertyValues方法中來對字段值進(jìn)行設(shè)置:
protected void applyPropertyValues(MutablePropertyValues mpvs) {
try {
// Bind request parameters onto target object.
getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
}
catch (PropertyBatchUpdateException ex) {
// Use bind error processor to create FieldErrors.
for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
}
}
}
首先碟案,它得到一個org.springframework.beans.AbstractPropertyAccessor類(getPropertyAccessor)的實現(xiàn)愿险。之后,通過具體實現(xiàn)這個抽象方法public void setPropertyValue(String propertyName价说,Object value)將HTTP請求中找到的值放入解析對象中辆亏。此方法由org.springframework.beans包中的BeanWrapperImpl和DirectFieldAccessor類實現(xiàn)。默認(rèn)情況下鳖目,ModelAttributeMethodProcessor使用的類是org.springframework.beans.BeanWrapperImpl扮叨,這是BeanWrapper的默認(rèn)實現(xiàn)。此默認(rèn)實現(xiàn)可以設(shè)置和獲取bean的屬性(類字段)疑苔。它以這種方式實現(xiàn)一個setPropertyValue方法:
public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyAccessor {
...
@Override
public void setPropertyValue(String propertyName, Object value) throws BeansException {
AbstractNestablePropertyAccessor nestedPa; //此處看下一段代碼一眼便知
try {
nestedPa = getPropertyAccessorForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
}
...
}
/**
* Default {@link BeanWrapper} implementation that should be sufficient
* for all typical use cases. Caches introspection results for efficiency.
*
* <p>Note: Auto-registers default property editors from the
* {@code org.springframework.beans.propertyeditors} package, which apply
* in addition to the JDK's standard PropertyEditors. Applications can call
* the {@link #registerCustomEditor(Class, java.beans.PropertyEditor)} method
* to register an editor for a particular instance (i.e. they are not shared
* across the application). See the base class
* {@link PropertyEditorRegistrySupport} for details.
*
* <p><b>NOTE: As of Spring 2.5, this is - for almost all purposes - an
* internal class.</b> It is just public in order to allow for access from
* other framework packages. For standard application access purposes, use the
* {@link PropertyAccessorFactory#forBeanPropertyAccess} factory method instead.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @author Stephane Nicoll
* @since 15 April 2001
* @see #registerCustomEditor
* @see #setPropertyValues
* @see #setPropertyValue
* @see #getPropertyValue
* @see #getPropertyType
* @see BeanWrapper
* @see PropertyEditorRegistrySupport
*/
public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
結(jié)果被轉(zhuǎn)移到private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv)甫匹,這里就不詳細(xì)介紹了。你只需要知道它是一個設(shè)置一個字段的值的方法惦费。set字段可以是一個簡單的類型(String兵迅,int等),也可以是一個集合(List薪贫,Map)恍箭。
以上介紹了在老版本中關(guān)于@ModelAttribute如何在Spring Web應(yīng)用程序解析的。如上所看到的瞧省,代碼執(zhí)行的基本流程以HandlerMethodResolver對象開頭扯夭,并以ModelAttributeMethodProcessor實例解析的可選對象結(jié)束。整個過程基于數(shù)據(jù)綁定鞍匾,在DataBinder子類中實現(xiàn)交洗。他們通過屬性訪問器(默認(rèn)BeanWrapperImpl)從請求中獲取鍵值對并將其放在目標(biāo)對象中。
新版本
通過上面可以看出橡淑,老版本的代碼其實穿梭的蠻復(fù)雜的构拳,這里就通過新版的代碼再來梳理下:
@ModelAttribute注解的方法是作用于整個Controller的,實際上在執(zhí)行Controller的每個請求時都會執(zhí)行@ModelAttribute注解的方法。
執(zhí)行過程在org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter中查看置森,每次執(zhí)行Controller時都會執(zhí)行@ModelAttribute注解的方法:
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* @since 4.2 可以看到4.2開始啟用了
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//執(zhí)行@ModelAttribute注解的方法
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//執(zhí)行Controller中的方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
modelFactory.initModel(webRequest, mavContainer, invocableMethod)中會執(zhí)行@ModelAttribute注解的方法(org.springframework.web.method.annotation.ModelFactory中可查看):
/**
* Populate the model in the following order:
* <ol>
* <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}.
* <li>Invoke {@code @ModelAttribute} methods
* <li>Find {@code @ModelAttribute} method arguments also listed as
* {@code @SessionAttributes} and ensure they're present in the model raising
* an exception if necessary.
* </ol>
* @param request the current request
* @param container a container with the model to be initialized
* @param handlerMethod the method for which the model is initialized
* @throws Exception may arise from {@code @ModelAttribute} methods
*/
public void initModel(NativeWebRequest request, ModelAndViewContainer container,
HandlerMethod handlerMethod) throws Exception {
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
container.mergeAttributes(sessionAttributes);
//執(zhí)行@ModelAttribute注解的方法
invokeModelAttributeMethods(request, container);
////方法執(zhí)行結(jié)果的值放到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 void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)中會判斷方法上是否被@ModelAttribute注解斗埂,如果是則會執(zhí)行這個方法,并將返回值放到container中:
/**
* Invoke model attribute methods to populate the model.
* Attributes are added only if not already present in the model.
*/
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
throws Exception {
while (!this.modelMethods.isEmpty()) {
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
//判斷方法是否被@ModelAttribute注解
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;
}
//執(zhí)行被@ModelAttribute注解的方法
Object returnValue = modelMethod.invokeForRequest(request, container);
if (!modelMethod.isVoid()){
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
if (!ann.binding()) {
container.setBindingDisabled(returnValueName);
}
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}
}
我們進(jìn)入org.springframework.web.method.support.InvocableHandlerMethod 的invokeForRequest方法凫海,在給定request請求的上下文中解析其參數(shù)值后調(diào)用該方法呛凶,參數(shù)值通常通過 HandlerMethodArgumentResolver來解析。
/**
* Invoke the method after resolving its argument values in the context of the given request.
* <p>Argument values are commonly resolved through {@link HandlerMethodArgumentResolver}s.
* The {@code providedArgs} parameter however may supply argument values to be used directly,
* i.e. without argument resolution. Examples of provided argument values include a
* {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
* Provided argument values are checked before argument resolvers.
* @param request the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type, not resolved
* @return the raw value returned by the invoked method
* @exception Exception raised if no suitable argument resolver can be found,
* or if the method raised an exception
*/
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//看下面的方法
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"' with arguments " + Arrays.toString(args));
}
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"] returned [" + returnValue + "]");
}
return returnValue;
}
/**
* Get the method argument values for the current request.
*/
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
//又回歸到解析參數(shù)的老路上了行贪,就不多解析了
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
throw new IllegalStateException("Could not resolve method parameter at index " +
parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() +
": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
}
}
return args;
}
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite
/**
* Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
* @throws IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
*/
@Override
public Object resolveArgument(MethodParameter parameter, @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() + "]");
}
//又回到老版本的resolveArgument路上了
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
/**
* Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
*/
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
parameter.getGenericParameterType() + "]");
}
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
回到org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,可以看到:
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
@Nullable
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
@Nullable
private HandlerMethodArgumentResolverComposite argumentResolvers;
@Nullable
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
@Nullable
private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;
又回到老版本的resolveArgument這里了,就不往下解釋了
關(guān)于@ModelAttribute的例子請看這篇博客文章的漾稀,自己就不整例子了http://blog.csdn.net/hejingyuan6/article/details/49995987**
總之,通過源碼可以看出,當(dāng)@ModelAttribute注解方法時,這個方法在每次訪問Controller時都會被執(zhí)行,其執(zhí)行到的原理就是在每次執(zhí)行Controller時都會判斷一次,并執(zhí)行@ModelAttribute的方法,將執(zhí)行后的結(jié)果值放到container中,其實我們只需要知道這么多就成了,背后的機制無論新老版本都是解析綁定這4個字瓮顽。