SpringMVCソース解析
記事の目次 SpringMVCソース解析 一、大まかな流れ 、doDisplatchソース表示版: 、詳細 、get Handler()詳細 、get Handler Adapter()詳細 、SpringMVCの九大コンポーネント 、onRefresh()初期化詳細 、handle()の詳細 (1)invokeHandlerMethod方法 (2)reolveHandlerAgments決定パラメータ (3)reolveCommargment解析一般パラメータ (4)reolveStandardAgment解析標準パラメータ (5)カスタムパラメータ決定値 (6)パラメータ決定総括(難点) SpringMVCソース解析
一、大体の流れ
1)、すべての要求をDisplatch Servletに来て処理する。
2)、doDisplatch()を呼び出して処理する。
1、getHandler():現在の要求に基づいて、HandlerMappingにこの要求のマッピング情報を見つけて、この要求を処理できる対象プロセッサ類を見つけます。
2、getHandlerAdfapter():このプロセッサ方法を実行できるアダプターを現在のプロセッサ類から取得します。
3、先ほどのアダプターを使って目標方法を実行します。
4、目標方法を実行したらModelAndViewオブジェクトに戻ります。
5、ModelAndViewの情報に基づいて具体的なページに転送し、ModelMapのデータを要求ドメインから取り出すことができます。
二、doDisplatchソース表示版:
三、細かいところ
1、get Handler()詳細
どのように現在の要求に基づいてどの種類のエネルギーを見つけて処理しますか?
-HandlerExecution Charinタイプのオブジェクトを返します。handleer(目標処理方法情報)、interceptorList(ブロッキング情報)などが含まれます。
get Handler():
handleMappings:プロセッサマッピングは、各プロセッサがどのような要求を処理できますか?ビーンNameUrlhandlerMappingは、プロファイル を介して構成されている。 Default AnnotationhandlerMappingは注釈を通して、注釈で表示されたRequest Mappingマッピング関係は全部この種類のhandle Mapの中にあります。
なぜハンドルMappingsはマッピングを保存しましたか?
IOC起動時はすべてのControllerをスキャンし、Request MappingをDefault AnnotationHandlerMappingに保存します。
どのように現在の要求に基づいてどの種類のエネルギーを見つけて処理しますか?
HandlerAdapterはプロセッサ方法を呼び出し、プロセッサ方法にパラメータ解析、戻り値処理などの適応作業を提供し、ユーザーを業務ロジックの実現に専念させる。
get Handler Adapter():
すべてのhanderAdaptersを巡回して、どのadaterがsupportこのhandlerを見ることができますか?(handle Adaptersはデフォルトで3つのhanderAdapterがあります。) HttpRequest HandlerAdapterは、HttpRequest Handlerインターフェースを実現し、Http requestsを処理するために使用され、簡単なServlet と類似しています。 SimpleController HandlerAdapterがControllerインターフェースを実現するプロセッサ、 あなたのカスタムコントローラがもし実装されているのがControllerインターフェースであれば、SimpleController Handler Adapterアダプタを使用してカスタムControllerを実行する必要があります。 AnnotationMethodhandler Adapterが注釈方法を解析できるアダプター(annotationn-drivenが配置されていないのはこれです。)
Displatch Srvletにはいくつかの引用タイプの属性があります。SpringMVCの九大コンポーネントです。
SpringMVCは仕事をする時、肝心な位置はすべてこれらの部品から完成したのです。共通点:九大の部品は全部「インターフェース」で、インターフェースは拡張性を提供しました。
SpringMVCの九大コンポーネントの作業原理(オーディエンスレベル) Displatch Servletは、SpringIOC容器refresh()におけるサブクラスの実現のための一歩にちょうど対応する方法を実現しました。 したがって、このonRefresh()方法は、IOC容器を初期化する際に実行され、9つのコンポーネントを初期化するために使用される。
いくつかのコンポーネントはコンテナの中でタイプを使って探しています。一部のコンポーネントはIDを使って探しています。
容器の中にこの部品を探しに行きます。見つけられなかったら、デフォルトの設定を使います。
例えば、HadleMappingは ダミーモデル に取り込まれてもよい。
ターゲット方法の実行にロックします。handle()の詳細(反射はパラメータなどをどうやって決定しますか?)( に追加する。( ( を実行する。
ModelAttributeの方法と目標方法を実行する時、方法のパラメータを決めます。デザインは巧みです。 を解析する。 に伝えます。 ModelAttributeメソッドにvalue識別子がないと表示されていると、返ってきた値の種類をatrNameとして解析し、戻り値を暗黙のモデル
このクラスの重要なロジックの一部
ModelAttribute表示の方法は、事前に実行され、実行後の戻り値を暗黙のモデルに追加する(方法実行の詳細)
この方法はは、まず、元のAPIとの関連があるかどうかを見て、resoveStandardAgment方法で を処理する。 ModelまたはMapタイプのパラメータが、暗黙的なモデルに入ってきたかどうかを見て、他のタイプのパラメータ、例えばSession Stuts、HttpEntity、resoveHandlerAgments方法で を処理します。は、単純なタイプかどうか(Integer、String...)を見ています。もし、パラムName=「」であれば、 です。もし全部ではないなら、atrName=""(例えばPOJO、Book、Personタイプ)は、reolveHandlerAgments方法で です。
カスタムパラメータを解析します。もし暗黙のモデルがatrNameという値を持つなら、それです。 そうでなければ、この変数はSession Attributeに表示されている属性ではないかと思います。値がなければ、投げ異常 です。いずれもそうでない場合は、反射により空のオブジェクト を作成する。
(6)パラメータ決定のまとめ(難点)
まとめ:注解があるかどうかを見てください。ある話は解決注 です。注釈がない場合: 元のAPIがあるかどうかを見てください。 ModelやMapなどのタイプかどうか見てください。 は全部違います。簡単なタイプかどうか、paramName=" "はatrNameに値を割り当て、atrName(パラメータが表示されています@ModelAttribute(“”)は指定されています。ラベルがないのは“”です。カスタムタイプのパラメータを確定します。
一、大体の流れ
1)、すべての要求をDisplatch Servletに来て処理する。
2)、doDisplatch()を呼び出して処理する。
1、getHandler():現在の要求に基づいて、HandlerMappingにこの要求のマッピング情報を見つけて、この要求を処理できる対象プロセッサ類を見つけます。
2、getHandlerAdfapter():このプロセッサ方法を実行できるアダプターを現在のプロセッサ類から取得します。
3、先ほどのアダプターを使って目標方法を実行します。
4、目標方法を実行したらModelAndViewオブジェクトに戻ります。
5、ModelAndViewの情報に基づいて具体的なページに転送し、ModelMapのデータを要求ドメインから取り出すことができます。
二、doDisplatchソース表示版:
doDispatch():
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// , ,
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//(1) ( , )
// , request processedRequest
processedRequest = checkMultipart(request);
// processedRequest request , multipartRequestParsed(Boolean)
multipartRequestParsed = processedRequest != request;
//(2)
// Determine handler for the current request.
// handler(Controller) , ,
// * :*
// 1. ( ), default-servlet-handler, mappedHandler null
// 2. mappedHandler HandlerExecutionChain , handler( )、interceptorList( )
mappedHandler = getHandler(processedRequest);
//(3) , 404,
if (mappedHandler == null || mappedHandler.getHandler() == null) {
// , ,
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// (4) ( )
// * :*
// annotation-driven, Handler AnnotationMethodHandlerAdpater
// Handler RequestMappingHandlerAdpater
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// (Get、Post...), ,lastModified ( , )
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// (handlerAdpater) handler(Controller)
// (5) , ModelAndView
// , ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
// , ,
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
// , ,
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
// , ,
// (6) ModelAndView,
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
----------------------------------------------------------三、細かいところ
1、get Handler()詳細
どのように現在の要求に基づいてどの種類のエネルギーを見つけて処理しますか?
-HandlerExecution Charinタイプのオブジェクトを返します。handleer(目標処理方法情報)、interceptorList(ブロッキング情報)などが含まれます。
get Handler():
handleMappings:プロセッサマッピングは、各プロセッサがどのような要求を処理できますか?
なぜハンドルMappingsはマッピングを保存しましたか?
IOC起動時はすべてのControllerをスキャンし、Request MappingをDefault AnnotationHandlerMappingに保存します。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
2、get Handler Adapter()詳細どのように現在の要求に基づいてどの種類のエネルギーを見つけて処理しますか?
HandlerAdapterはプロセッサ方法を呼び出し、プロセッサ方法にパラメータ解析、戻り値処理などの適応作業を提供し、ユーザーを業務ロジックの実現に専念させる。
get Handler Adapter():
すべてのhanderAdaptersを巡回して、どのadaterがsupportこのhandlerを見ることができますか?(handle Adaptersはデフォルトで3つのhanderAdapterがあります。)
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator var2 = this.handlerAdapters.iterator();
HandlerAdapter ha;
do {
if (!var2.hasNext()) {
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
ha = (HandlerAdapter)var2.next();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Testing handler adapter [" + ha + "]");
}
} while(!ha.supports(handler));
return ha;
}
3、SprigMVCの九大セットDisplatch Srvletにはいくつかの引用タイプの属性があります。SpringMVCの九大コンポーネントです。
SpringMVCは仕事をする時、肝心な位置はすべてこれらの部品から完成したのです。共通点:九大の部品は全部「インターフェース」で、インターフェースは拡張性を提供しました。
SpringMVCの九大コンポーネントの作業原理(オーディエンスレベル)
/** MultipartResolver used by this servlet */
/** */
private MultipartResolver multipartResolver;//
/** LocaleResolver used by this servlet */
/** , */
private LocaleResolver localeResolver;
/** ThemeResolver used by this servlet */
/** , */
private ThemeResolver themeResolver;
/** List of HandlerMappings used by this servlet */
/** Handler */
private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet */
/** Handler */
private List<HandlerAdapter> handlerAdapters;
/** List of HandlerExceptionResolvers used by this servlet */
/** SpringMVC */
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** RequestToViewNameTranslator used by this servlet */
/** , */
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMapManager used by this servlet */
/** SpringMVC */
private FlashMapManager flashMapManager;
/** List of ViewResolvers used by this servlet */
/** */
private List<ViewResolver> viewResolvers;
4、onRefsh()初期化の詳細@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
コンポーネントの初期化:いくつかのコンポーネントはコンテナの中でタイプを使って探しています。一部のコンポーネントはIDを使って探しています。
容器の中にこの部品を探しに行きます。見つけられなかったら、デフォルトの設定を使います。
/**
* Initialize the strategy objects that this servlet uses.
* May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
例えば、初期化HanderMappings:DispatcherServlet.properties
の九つのコンポーネントのデフォルトクラスをロードします。例えば、HadleMappingは
BeanNameUrlHandlerMapping
とDefaultAnnotationHandlerMapping
です。private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// web.xml DispatcherServlet , detectAllHandlerMappings
// IOC HandlerMapping
if (this.detectAllHandlerMappings) {
// 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<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
OrderComparator.sort(this.handlerMappings);
}
}
else {
// , ID “handlerMapping”
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.
// , handlerMapping
// !!! DispatcherServlet.properties !!!
//
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
5、ハンドルの詳細implicitModel
におけるデータは、Requestドメインでターゲット方法の実行にロックします。handle()の詳細(反射はパラメータなどをどうやって決定しますか?)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
invokeHandlerMethod
)Session Atributesのオブジェクトに値があれば、暗黙のモデルimplicitModel
invokeHandlerMethod
)すべてのModelAttributeの表示を行う方法invokeHandlerMethod
)は、目標方法ModelAttributeの方法と目標方法を実行する時、方法のパラメータを決めます。デザインは巧みです。
resolveHandlerArguments
は、パラメータを決定し、先に注釈表示のパラメータかどうかを見て、解析を行い、そうでない場合は、resolveCommonArgument
を呼び出して、通常パラメータresolveCommonArgument
は、まず、resoveStandardAgment解析標準パラメータ(Requestなどの元のコンポーネントを充填する)を呼び出し、その後、コンポーネントがModelまたはMapタイプであるかどうかを見て、直接implicitModel
をresolveStandardArgument
解析標準パラメータ(ここにRequestなどの元の部品を充填する)implicitModel
に追加します。@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// handler (@Controller )
Class<?> clazz = ClassUtils.getUserClass(handler);
// SessionAttribute
Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
if (annotatedWithSessionAttributes == null) {
annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
}
if (annotatedWithSessionAttributes) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
// Prepare cached set of session attributes names.
}
else {
// Uses configured default cacheSeconds setting.
// , ,
checkAndPrepare(request, response, true);
}
// 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) {
return invokeHandlerMethod(request, response, handler);
}
}
}
// , invokeHandlerMethod
return invokeHandlerMethod(request, response, handler);
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
// request
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
//
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
// request、response
ServletWebRequest webRequest = new ServletWebRequest(request, response);
// implicitModel ( BindingAwareModelMap , Map ) implicit: 、
ExtendedModelMap implicitModel = new BindingAwareModelMap();
/**
* , , , (success)( , 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;
}
(1)invokeHandler Method方法このクラスの重要なロジックの一部
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
try {
boolean debug = logger.isDebugEnabled();
// SessionAtributes ,
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);
// *************** *****************
//
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);
}
}
// !!! ModelAttribute
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;
}
}
(2)reolveHandlerAgments決定パラメータModelAttribute表示の方法は、事前に実行され、実行後の戻り値を暗黙のモデルに追加する(方法実行の詳細)
resolveHandlerArguments
。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) {
// RequestParam
if (RequestParam.class.isInstance(paramAnn)) {
RequestParam requestParam = (RequestParam) paramAnn;
//
paramName = requestParam.value();
// required
required = requestParam.required();
defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
// , ++
annotationsFound++;
}
// RequestHeader
else if (RequestHeader.class.isInstance(paramAnn)) {
RequestHeader requestHeader = (RequestHeader) paramAnn;
headerName = requestHeader.value();
required = requestHeader.required();
defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
annotationsFound++;
}
// RequestBody
else if (RequestBody.class.isInstance(paramAnn)) {
requestBodyFound = true;
annotationsFound++;
}
// CookieValue
else if (CookieValue.class.isInstance(paramAnn)) {
CookieValue cookieValue = (CookieValue) paramAnn;
cookieName = cookieValue.value();
required = cookieValue.required();
defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
annotationsFound++;
}
// PathVariable
else if (PathVariable.class.isInstance(paramAnn)) {
PathVariable pathVar = (PathVariable) paramAnn;
pathVarName = pathVar.value();
annotationsFound++;
}
// ModelAttribute
else if (ModelAttribute.class.isInstance(paramAnn)) {
ModelAttribute attr = (ModelAttribute) paramAnn;
attrName = attr.value();
annotationsFound++;
}
// Value
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);
// ( ) ,
if (argValue != WebArgumentResolver.UNRESOLVED) {
args[i] = argValue;
}
//
else if (defaultValue != null) {
args[i] = resolveDefaultValue(defaultValue);
}
else {
//
Class<?> paramType = methodParam.getParameterType();
// ************ *************
// Model !! Map
// implicitModel
// *****************************
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;
}
// SessionStatus
else if (SessionStatus.class.isAssignableFrom(paramType)) {
args[i] = this.sessionStatus;
}
// HttpEntity
else if (HttpEntity.class.isAssignableFrom(paramType)) {
args[i] = resolveHttpEntityRequest(methodParam, webRequest);
}
// Errors
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;
}
(3)resoveCommon Agment解析一般パラメータprotected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
throws Exception {
// Invoke custom argument resolvers if present...
// ,
if (this.customArgumentResolvers != null) {
for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) {
Object value = argumentResolver.resolveArgument(methodParameter, webRequest);
if (value != WebArgumentResolver.UNRESOLVED) {
return value;
}
}
}
// Resolution of standard parameter types...
Class<?> paramType = methodParameter.getParameterType();
//
Object value = resolveStandardArgument(paramType, webRequest);
if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) {
throw new IllegalStateException("Standard argument type [" + paramType.getName() +
"] resolved to incompatible value of type [" + (value != null ? value.getClass() : null) +
"]. Consider declaring the argument type in a less specific fashion.");
}
return value;
}
(4)resoveStandardAgment解析基準パラメータこの方法は
AnnotationMethodHandlerMapping
にある。@Override
protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
// API
// ServletRequest
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;
}
// ServletResponse
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;
}
// HttpSession
else if (HttpSession.class.isAssignableFrom(parameterType)) {
return request.getSession();
}
// Principal
else if (Principal.class.isAssignableFrom(parameterType)) {
return request.getUserPrincipal();
}
// Locale
else if (Locale.class.equals(parameterType)) {
return RequestContextUtils.getLocale(request);
}
// InputStream
else if (InputStream.class.isAssignableFrom(parameterType)) {
return request.getInputStream();
}
// Reader
else if (Reader.class.isAssignableFrom(parameterType)) {
return request.getReader();
}
// OutputStream
else if (OutputStream.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getOutputStream();
}
// Writer
else if (Writer.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getWriter();
}
return super.resolveStandardArgument(parameterType, webRequest);
}
(5)カスタムパラメータ決定値BeanUtils.isSimpleProperty(paramType)
は、reolveHandlerAgments方法でカスタムパラメータを解析します。
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());
}
resolveModelAttribute
方法private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
// Bind request parameter onto object...
String name = attrName;
// attrName , (book)
if ("".equals(name)) {
name = Conventions.getVariableNameForParameter(methodParam);
}
Class<?> paramType = methodParam.getParameterType();
Object bindObject;
//
if (implicitModel.containsKey(name)) {
bindObject = implicitModel.get(name);
}
// sessionAttribute , ,
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;
}
SpringMVCはPOJO値の三段階を確定します。(6)パラメータ決定のまとめ(難点)
まとめ:
1. attrName , @ModelAttribute("")
2. `implicitModel` key
3. @SessionAtrribute , Session ,
4. @SessionAtrribute , , (WebDataBinder)