您的位置:首页 > 编程语言 > Java开发

深入剖析Spring Web源码(九) - 处理器映射,处理器适配器以及处理器的实现 - 基于注解控制器流程的实现

2010-09-28 17:37 1086 查看
4.2.1.2 基于注解控制器流程的实现

上一节,我们详细的分析了基于简单控制器流程的实现,事实上,许多的简单控制器的实现已经不被推荐使用。自从 2.5发布以后, Spring开始鼓励使用基于注解控制器的流程。基于注解的控制器流程具有实现方法简单,程序代码清晰易读等特点。

基于注解控制器的流程和基于简单控制器的流程的实现非常相似,派遣器 Servlet在处理一个 HTTP请求的时候,它通过缺省注解处理器映射 (DefaultAnnotationHandlerMapping)把 HTTP请求映射到响应的注解控制器 (@Contoller),然后,把控制流传给注解方法处理器适配器 (AnnotationMethodHandlerAdapter)。注解方法处理器适配器并不是简单的传递控制流给注解控制器,而是以一定规则查找注解控制器里面的处理器方法,并且通过反射的方式映射 HTTP 请求信息到方法参数,然后使用反射调用方法,得到方法的返回结果后,再根据一定的规则把返回结果映射到模型和视图对象,进而返回给作为总控制器的派遣器 Servlet。

事实上,缺省注解处理器映射的实现重用了简单控制器流程的处理器映射的实现体系结构。回顾上一小结中分析的 Bean名 URL处理器映射继承自抽象探测 URL处理器映射,实现了其抽象方法 determineUrlsForHandler(),在这个方法实现中,把所有以左划线 (/)开头的 Bean名字注册作为一个简单的控制器。缺省注解处理器映射的实现同样也实现了抽象方法 determineUrlsForHandler(),如果某个 Bean中使用了请求映射 (@RequestMappings)注解,则注册这个 Bean作为一个注解控制器。如下类图所示,



图表 4 ‑20

如上图所示,缺省注解处理器映射实现了方法 determineUrlsForHandler(),查找 Bean类型级别的请求映射注解和方法级别的请求映射注解,如果两个级别的请求映射注解都存在,结合两个级别的请求映射注解,否则使用方法级别的请求映射注解,构造出一个 URL Pattern集合,并且返回这个 URL Pattern集合,抽象探测 URL处理器映射就会使用这个集合的每一个元素作为关键字注册当前的 Bean作为一个注解控制器。如下程序注释,

@Override
protected String[] determineUrlsForHandler(String beanName) {
// 取得Web应用程序环境
ApplicationContext context = getApplicationContext();

// 取得当前Bean的类型
Class<?> handlerType = context.getType(beanName);

// 找到类型级别的请求映射注解(@RequestMapping)
RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);

if (mapping != null) {
// 如果类型级别声明了请求映射注解(@RequestMapping)

// 缓存处理器类型和类型级别请求映射注解(@RequestMapping)
this.cachedMappings.put(handlerType, mapping);

// 开始查找所有应用在当前Bean的URL Pattern,这些URL Pattern是根据类型级别和方法级别的请求映射注解(@RequestMapping)计算得到的
Set<String> urls = new LinkedHashSet<String>();

// 取得类型级别的URL Pattern
String[] typeLevelPatterns = mapping.value();
if (typeLevelPatterns.length > 0) {
// 如果类型级别的请求映射注解(@RequestMapping)指定了URL Pattern,结合类型级别的URL Pattern和方法级别的URL Pattern
String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType);

// 对于每个类型级别的URL Pattern结合每一个方法级别的URL Pattern
for (String typeLevelPattern : typeLevelPatterns) {
// 如果没有使用斜线/开头,则添加一个斜线/开头,因为匹配时候的URI是以/开头的
if (!typeLevelPattern.startsWith("/")) {
typeLevelPattern = "/" + typeLevelPattern;
}

for (String methodLevelPattern : methodLevelPatterns) {
// 结合任意一个类型级别的URL Pattern和任意一个方法级别的URL Pattern
String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);

// 添加结合的URL Pattern到结果集合
addUrlsForPath(urls, combinedPattern);
}

// 添加类型级别的URL Pattern到结果集合
addUrlsForPath(urls, typeLevelPattern);
}

// 返回URL Pattern结果集合
return StringUtils.toStringArray(urls);
}
else {
// 如果类型级别的请求映射注解(@RequestMapping)没有配置URL Pattern,则直接返回所有方法级别的请求映射注解(@RequestMapping)中的URL Pattern
return determineUrlsForHandlerMethods(handlerType);
}
}
else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
// 如果没有声明类型级别的请求映射注解(@RequestMapping),但是声明了类型级别的控制器注解(@Controller),则直接返回所有方法级别的请求映射注解(@RequestMapping)中的URL Pattern
return determineUrlsForHandlerMethods(handlerType);
}
else {
// 如果既没有方法级别的请求映射注解(@RequestMapping)也没有类型级别的控制器注解(@Controller),则这个Bean不是注解控制器,返回null
return null;
}
}
protected String[] determineUrlsForHandlerMethods(Class<?> handlerType) {
// 声明成final是为了您名类的访问
final Set<String> urls = new LinkedHashSet<String>();

// 如果是代理类则使用代理类实现的接口,否则用类本身来探测处理器方法
Class<?>[] handlerTypes =
Proxy.isProxyClass(handlerType) ? handlerType.getInterfaces() : new Class<?>[]{handlerType};

// 对于每个类型,遍历类型中的每个处理器方法
for (Class<?> currentHandlerType : handlerTypes) {
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
// 取得当前方法级别声明的请求映射注解(@RequestMapping)
RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (mapping != null) {
// 如果方法级别的请求映射注解(@RequestMapping)存在

// 遍历每一个请求映射注解(@RequestMapping)中注册的URL Pattern
String[] mappedPaths = mapping.value();
for (String mappedPath : mappedPaths) {
// 添加到返回URL Pattern集合中
addUrlsForPath(urls, mappedPath);
}
}
}
});
}

// 返回结果集合
return StringUtils.toStringArray(urls);
}
protected void addUrlsForPath(Set<String> urls, String path) {
// 直接添加到结果集合中
urls.add(path);

// 如果使用缺省后缀,则添加另外两个URL到URL Pattern集合中,用于匹配扩展名后缀和斜线/后缀
if (this.useDefaultSuffixPattern && path.indexOf('.') == -1 && !path.endsWith("/")) {
urls.add(path + ".*");
urls.add(path + "/");
}
}


在缺省注解处理器映射中除了实现了提取注解处理器中配置的 URL Pattern 外,还改写了一个校验处理器的方法 validateHandler() ,这个方法的实现根据类型级别的请求映射的配置,校验当前的请求是否能够应用到这个处理器上。这个校验方法是在派遣器 Servlet 将一个请求映射到响应的处理器的时候调用的。具体逻辑如下,

如果处理器类型级别请求映射定义了 HTTP 方法,则当前 HTTP 请求方法必须是请求映射定义的这些 HTTP 方法之一。

如果处理器类型级别请求映射定义了 HTTP 参数,则当前 HTTP 请求参数必须包含请求映射定义的所有的 HTTP 参数。

如果处理器类型级别请求映射定义了 HTTP 头,则 HTTP 请求头必须包含请求映射定义的所有的 HTTP 头。

否则,这个处理器不能处理当前的 HTTP 请求,抛出异常,终止处理。可见,类型级别的请求映射是优先校验的,方法级别的请求映射是后来校验的,所以,我们得出一下结论。

方法级别请求映射定义的 HTTP 方法必须是类型级别请求映射定义的 HTTP 方法的子集,否则没有意义。

方法级别请求映射定义的 HTTP 参数可以多余类型级别请求映射定义的 HTTP 参数。

方法级别请求映射定义的 HTTP 头可以多余类型级别请求映射定义的 HTTP 头。

如下程序注释,

//这个方法是在派遣器Servlet查找到一个处理器对象时调用的
@Override
protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {
// 从缓存中得到声明在处理器类级别的请求映射注解(@ReuqestMapping)
RequestMapping mapping = this.cachedMappings.get(handler.getClass());
if (mapping == null) {
// 如果缓存中没有,则直接取得,通常情况下,初始化的时候加入的
mapping = AnnotationUtils.findAnnotation(handler.getClass(), RequestMapping.class);
}
if (mapping != null) {
// 校验方法级别的映射信息
validateMapping(mapping, request);
}
}
protected void validateMapping(RequestMapping mapping, HttpServletRequest request) throws Exception {
// 取得类型级别声明的HTTP方法
RequestMethod[] mappedMethods = mapping.method();
if (!ServletAnnotationMappingUtils.checkRequestMethod(mappedMethods, request)) {
// 如果当前请求的HTTP方法不是类型级别声明的HTTP方法,而且类型级别声明了一个或者一个以上的HTTP方法
String[] supportedMethods = new String[mappedMethods.length];

// 取得类型级别声明的HTTP方法,这些方法是支持的HTTP方法
for (int i = 0; i < mappedMethods.length; i++) {
supportedMethods[i] = mappedMethods[i].name();
}

// 抛出异常,提示哪些HTTP方法是支持的
throw new HttpRequestMethodNotSupportedException(request.getMethod(), supportedMethods);
// 取得类型级别声明的HTTP参数
String[] mappedParams = mapping.params();

if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) {
// 如果当前请求的HTTP参数不包含类型级别声明的HTTP参数,而且类型级别声明了一个或者一个以上的HTTP参数

// 抛出异常,提示那些参数应该包含在HTTP请求中
throw new UnsatisfiedServletRequestParameterException(mappedParams, request.getParameterMap());
}
// 取得类型级别声明的HTTP头
String[] mappedHeaders = mapping.headers();
if (!ServletAnnotationMappingUtils.checkHeaders(mappedHeaders, request)) {
// 如果当前请求的HTTP头不包含类型级别声明的HTTP头,而且类型级别声明了一个或者一个以上的HTTP头

// 抛出异常,提示哪些头信息应该包含在在HTTP请求中
throw new ServletRequestBindingException("Header conditions /"" +
StringUtils.arrayToDelimitedString(mappedHeaders, ", ") +
"/" not met for actual request");
}

//可见,如果类型级别声明了支持的HTTP方法,那么,方法中声明的方法应该是类型级别声明HTTP方法的子集,参数和头信息的声明则可以是递增的
}


通过以上我们的分析,我们理解在基于注解控制器流程的实现中,缺省注解处理器映射是通过处理器中声明的请求映射注解注册注解控制器以及查找注解控制器的。作为总控制器的派遣器 Servlet 通过 HTTP 请求得到一个注解控制器,将注解控制器等传给注解方法处理器适配器进行处理器方法的调用,对处理器方法的调用是通过反射实现的,在调用之前,需要通过反射从请求参数,请求头等探测所有需要的参数,再调用后返回方法结果后,再通过一定规则映射结果到模型和视图对象。这个流程是在注解方法处理器适配器类和相关的支持类实现的,如下类图所示,



图表 4 ‑21

如上图所示,注解方法处理器适配器类实现了处理器适配器接口。在 handle() 方法的实现中,使用两个辅助类 Servlet 处理器方法解析器 (ServletHandlerMethodResolver) 和 Servlet 处理器方法调用器 (ServletHandlerMethodInvoker) 利用反射的原理调用注解处理器中的处理器方法。在处理器方法调用之前,通过参数注解从 HTTP 请求中提取参数值,在处理器方法调用之后通过注解映射方法的返回值到模型和视图对象,最后返回给派遣器 Servlet 进行视图解析和视图显示。如下流程图所示,



图表 4 ‑22

根据上面流程图显示的顺序,我们将深入的对代码进行剖析,派遣器 Servlet 从缺省注解处理器映射得到了注解控制器后,将控制器传递给注解方法处理器适配器的 handle() 方法, handle() 方法在进行通用的 HTTP 请求方法检查和设置 HTTP 响应缓存信息后,根据需要对处理器方法进行同步或者非同步的调用,如下代码注释,

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 如果在处理器类型级别声明了Session属性(@SessionAttributes), 为这个处理器使用特殊的缓存时间,这个特殊的缓存时间通过属性cacheSecondsForSessionAttributeHandlers配置
if (AnnotationUtils.findAnnotation(handler.getClass(), SessionAttributes.class) != null) {
// 如果使用Session属性管理,缺省情况下,指导用户不使用缓存
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
}
如果处理器级别没有声明Session属性(@SessionAttributes),则使用缺省的缓存配置
else {
// 缺省的缓存配置是通过属性cacheSeconds配置的
checkAndPrepare(request, response, true);
}
// 如果配置了Session内同步
if (this.synchronizeOnSession) {
// 取得Session对象,如果没有并不创建新的Session
HttpSession session = request.getSession(false);

// 如果Session存在
if (session != null) {
// 取得Session互斥锁对象,缺省是Session对象自己
Object mutex = WebUtils.getSessionMutex(session);

// 同步调用处理器方法
synchronized (mutex) {
return invokeHandlerMethod(request, response, handler);
}
}
}
// 如果没有配置Session内同步,或者还没有创建Session对象,则直接调用处理器方法,不需要Session内同步
return invokeHandlerMethod(request, response, handler);
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 通过传入的注解控制器构造一个方法解析器,一个类型的处理器对应一个Servlet处理器方法解析器
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);

// 根据URL Pattern匹配,解析得到注解控制器中能够处理当前请求的处理器方法
Method handlerMethod = methodResolver.resolveHandlerMethod(request);

// 构造方法调用器,一个类型的处理器对应一个Servlet处理器方法调用器,处理器方法的调用逻辑就是在这个类中实现的
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);

// 构造Web请求对象,它是一个代理对象,包含请求和响应引用和信息
ServletWebRequest webRequest = new ServletWebRequest(request, response);

// 构造空的模型对象,用于方法调用过程中存储必要的数据,状态,结果等等
ExtendedModelMap implicitModel = new BindingAwareModelMap();
// 通过反射调用处理器方法,在这个方法实现中,通过反射和声明在参数上的注解探测得到参数值,通过反射调用处理器方法得到返回值,更多的模型数据通过隐式模型返回
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);

// 通过方法结果的类型和其上声明的注解,把结果和模型数据映射成为模型和视图对象
ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);

// 导出Session属性从模型到Session中,如果是绑定对象,同时导出绑定结果
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);

return mav;
}


如上代码注释,对于如何解析处理器方法,如何解析参数,如何调用处理器方法以及如果映射返回值等的实现都是封装在处理器方法解析器和处理器方法调用器的实现中的,我们稍后会深入剖析这些逻辑的实现。

注解方法处理器适配器也对处理器适配器接口的另外两个方法进行实现,如下代码注释,

public boolean supports(Object handler) {
// 对于一个Bean对象,只要其中有一个方法声明了请求映射注解(@RequestMapping),则这个Bean对象是注解控制器
return getMethodResolver(handler).hasHandlerMethods();
}
public long getLastModified(HttpServletRequest request, Object handler) {
// 注解方法控制器适配器不支持最后修改操作,这可能是因为注解控制器基本应用到Form的处理过程,不需要支持最后修改操作,最后修改操作更多应用到请求资源上

// 【问题】下面的两个实现可能会更好,
//         1. 可以通过反射判断注解控制器是不是已经实现了LastModified接口
//         2. 也可以扩展一个方法注解,声明为这个注解的方法则用来返回实现返回最后修改的时间

return -1;
}


如上程序注释可见, supports() 和 getLastModified() 的实现是非常简单的,这里不再详细分析。通过上面的流程图和代码注释,我们也已经大体的了解了通过反射调用处理器方法的总体步骤,现在我们开始深入剖析注解方法处理器适配器是如何实现方法解析,方法调用以及模型结果数据映射的。

如何解析处理器方法呢?
正如注解控制器的名字所示,它是基于注解信息的控制器,这个控制器是一个普通的 Bean, 不需要实现任何接口或者继承抽象类。一个注解控制器可能包含一个或者更多的处理器方法,这些处理器方法是用请求映射注解 (@RequestMapping) 标志的,请求映射注解包含着用于匹配 HTTP 请求的 URI Pattern, 请求方法,请求参数,请求头的信息。这些在请求映射中声明的信息会用于匹配 HTTP 请求,如果匹配成功,则会使用匹配的处理器方法处理请求。我们首先分析请求映射注解 (@RequestMapping) 都包含哪些属性信息。

public @interface RequestMapping {
// 用于匹配查找路径的URI Pattern,HTTP请求的查找路径必须匹配URI Pattern之一
String[] value() default {};
// 处理器方法所支持的HTTP方法,这些方法包括 GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE,HTTP请求方法必须是其中之一
RequestMethod[] method() default {};
// HTTP请求必须包含所有的这些参数
String[] params() default {};
// HTTP请求必须包含所有的这些头
String[] headers() default {};
}


如上代码所示,声明在一个处理器方法或者处理器类型级别的请求映射注解可以包含 URI Pattern, 请求 方法,请求 参数, 请求头等信息。在匹配的时候, URI Pattern 是最重要的匹配信息,如果没有指定 URI Pattern ,则使用其余信息匹配。下面我们分析注解方法处理器适配器是如何使用这些信息匹配一个 HTTP 请求到一个处理器方法的。

首先,注解方法处理器适配器为每一个处理器类型创建一个处理器方法解析器,处理器方法解析器通过反射分析处理器类型,并且取得所有声明了请求映射注解的处理器方法,初始化绑定方法,模型属性方法。如下代码所示,

private ServletHandlerMethodResolver(Class<?> handlerType) {
// 使用父类的初始化方法进行初始化,一个处理器类型对应一个Servlet处理器方法解析器
init(handlerType);
}
public void init(Class<?> handlerType) {
// 如果处理器类型是代理类,则获得创建代理类指定的接口,否则使用处理器类型本身,这样可以忽略代理类本身的方法invoke()
Class<?>[] handlerTypes =
Proxy.isProxyClass(handlerType) ? handlerType.getInterfaces() : new Class<?>[] {handlerType};

// 遍历获得的每一个类型
for (final Class<?> currentHandlerType : handlerTypes) {
// 遍历每个类型的每个方法
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
// 如果这是接口的方法,取得实现接口的代理类中的对象的方法,这样主要是忽略代理类本身的方法invoke()
Method specificMethod = ClassUtils.getMostSpecificMethod(method, currentHandlerType);

// 如果是处理器方法,存储处理器方法
if (isHandlerMethod(method)) {
handlerMethods.add(specificMethod);
}
// 如果是初始化绑定方法,存储初始化绑定方法
else if (method.isAnnotationPresent(InitBinder.class)) {
initBinderMethods.add(specificMethod);
}
// 如果是模型属性方法,存储模型属性方法
else if (method.isAnnotationPresent(ModelAttribute.class)) {
modelAttributeMethods.add(specificMethod);
}
}
// 跳过桥梁方法,桥梁方法是编译器产生,用来解决模板的方法的重载问题的
}, ReflectionUtils.NON_BRIDGED_METHODS);
}

// 取得类型级别的请求映射注解
this.typeLevelMapping = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);

// 取得类型级别的Session属性注解
SessionAttributes sessionAttributes = handlerType.getAnnotation(SessionAttributes.class);

this.sessionAttributesFound = (sessionAttributes != null);

// 如果存在类型级别的Session属性注解
if (this.sessionAttributesFound) {
// 保存类型级别的Session属性注解声明的属性名字或者类型
this.sessionAttributeNames.addAll(Arrays.asList(sessionAttributes.value()));
this.sessionAttributeTypes.addAll(Arrays.asList(sessionAttributes.types()));
}
}
protected boolean isHandlerMethod(Method method) {
// 如果方法上存在请求映射注解,则这个方法是处理器方法
return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null;
}


我们看到,对于一个处理器类型初始化一个处理器方法解析器,处理器方法解析器在解析处理器方法时使用了一个复杂的逻辑决定这些方法中的哪些方法可以处理当前 HTTP 请求。如果多个处理器方法可以处理当前的请求,那么选择最佳匹配的处理器方法。以下详细分析这个工作流程。

在初始化阶段,我们已经存储了所有声明了请求映射注解的处理器方法。现在我们遍历所有的处理器方法,判断是否此方法支持当前的 HTTP 请求。

如果请求映射注解的处理器方法包含 URI Pattern 的信息,那么对于每一个 URI Pattern ,则查看是否存在类型级别的 URI Pattern ,如果存在,则结合类型级别的 URI Pattern 。否则,如果最佳匹配的 URI Pattern 存在,则结合最佳匹配的 URI Pattern 。否则单独使用处理器方法级别的 URI Pattern 匹配当前的查找路径。如果 URI Pattern 匹配成功,查看是否 HTTP 请求匹配声明的请求方法,请求参数和请求头。如果这些信息都匹配成功,则添加当前 URI Pattern 到匹配路径集合中,并通过路径匹配对比器对匹配路径集合进行排序,同事标识当前处理器方法为匹配。

如果请求映射注解的处理器方法不包含 URI Pattern 的信息,则只需要查看是否匹配声明的请求方法,请求参数和请求头。如果这些信息匹配,则认为当前处理器方法为匹配。一种特殊情况是,如果请求映射注解中没有声明 HTTP 方法和参数,那么首先使用处理器方法名解析器解析处理器方法名,如果解析的方法名和当前处理器方法相同,则认为当前处理器方法为匹配。缺省的方法名解析器是通过去掉 URI 最后一部分的文件名扩展名得到的。

如果某一个处理器方法匹配,存储这个处理器方法到匹配的处理器方法集合中。如果处理器集合中已经存在一个处理器方法,而且已存处理器方法和现在处理器方法不是同一个处理器方法,那么我们需要解析冲突。在这种情况下,如果没有路径信息,我们需要使用方法名解析器解析最佳处理器方法。处理规则如下,

1. 如果已存处理器方法和当前处理器方法名相同,则使用后解析的方法。
2. 否则,如果解析的方法名和已存处理器方法同名,继续使用已存处理器方法。如果解析的方法名和当前处理器方法同名,则使用当前处理器方法。
3. 如果解析的方法名既不等于当前处理器方法也不等于已存的处理器方法,则抛出异常,终止处理。

根据上面的逻辑分析,最终如果有一个或者多个处理器方法匹配当前 HTTP 请求,则通过请求映射信息对比器找到最佳匹配的处理器方法。如下代码所示,

public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
// 取得查找路径,查找路径是URI去掉应用程序环境部分和Servlet环境部分的剩余部分
String lookupPath = urlPathHelper.getLookupPathForRequest(request);

// 取得URL对比器,如果有多个URI Pattern匹配,用于对比找到最佳匹配
Comparator<String> pathComparator = pathMatcher.getPatternComparator(lookupPath);

// 用于存储请求映射信息到处理器方法的映射
Map<RequestMappingInfo, Method> targetHandlerMethods = new LinkedHashMap<RequestMappingInfo, Method>();
Set<String> allowedMethods = new LinkedHashSet<String>(7);
String resolvedMethodName = null;

// 遍历每一个处理器方法
for (Method handlerMethod : getHandlerMethods()) {
RequestMappingInfo mappingInfo = new RequestMappingInfo();

// 取得处理器方法的请求映射注解
RequestMapping mapping = AnnotationUtils.findAnnotation(handlerMethod, RequestMapping.class);

// 取得声明在请求映射注解上的路径信息,也就是URI Pattern
mappingInfo.paths = mapping.value();

// 如果处理器类型级别没有声明处理器映射或者方法级别处理器映射包含的HTTP方法信息不同于类型级别处理器映射包含的HTTP方法信息,则使用方法级别的声明信息
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
mappingInfo.methods = mapping.method();
}

// 如果处理器类型级别没有声明处理器映射或者方法级别处理器映射包含的HTTP参数信息不同于类型级别处理器映射包含的HTTP参数信息,则使用方法级别的声明信息
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
mappingInfo.params = mapping.params();
}

// 如果处理器类型级别没有声明处理器映射或者方法级别处理器映射包含的HTTP头信息不同于类型级别处理器映射包含的HTTP头信息,则使用方法级别的声明信息
if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {
mappingInfo.headers = mapping.headers();
}
boolean match = false;

// 如果URI Pattern存在,则先匹配URI
if (mappingInfo.paths.length > 0) {
List<String> matchedPaths = new ArrayList<String>(mappingInfo.paths.length);
for (String methodLevelPattern : mappingInfo.paths) {
// 取得匹配的URI Pattern
String matchedPattern = getMatchedPattern(methodLevelPattern, lookupPath, request);

// 如果取得的URI pattern不为空,则说明这个URI Pattern匹配成功
if (matchedPattern != null) {
// 匹配HTTP方法,参数和头等信息
if (mappingInfo.matches(request)) {
match = true;
// 记录匹配的URI Pattern
matchedPaths.add(matchedPattern);
}
else {
// 否则如果匹配失败,记录支持的HTTP方法,用来构造提示用户信息
for (RequestMethod requestMethod : mappingInfo.methods) {
allowedMethods.add(requestMethod.toString());
}
break;
}
}
}
// 排序匹配的URI Pattern集合
Collections.sort(matchedPaths, pathComparator);

// 设置匹配的URI Pattern信息到请求映射信息中
mappingInfo.matchedPaths = matchedPaths;
}
// 如果URI Pattern不存在,则只需要匹配HTTP方法,参数和请求头
else {
// 则只需要匹配HTTP方法,参数和请求头
match = mappingInfo.matches(request);

// 如果没有声明请求方法和请求参数(可能仅仅请求头匹配),则使用方法名解析器解析处理器方法,如果方法名解析器解析的方法和当前方法不同名,则匹配失败
if (match && mappingInfo.methods.length == 0 && mappingInfo.params.length == 0 &&
resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) {
match = false;
}
else {
// 否则如果匹配失败,否则记录支持的HTTP方法,用来构造提示用户信息
for (RequestMethod requestMethod : mappingInfo.methods) {
allowedMethods.add(requestMethod.toString());
}
}
}

// 如果这个处理器方法和当前的HTTP请求匹配成功
if (match) {
// 存储请求映射信息到当前处理器方法的映射,如果有已存的处理器方法,则取得已存的处理器方法,这意味着对同一个请求映射信息配置了两个处理器方法
Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);

// 如果已存的处理器方法不同于当前处理器方法
if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {
// 如果请求映射注解中没有URI Pattern声明信息,则使用方法名解析器解析冲突
if (methodNameResolver != null && mappingInfo.paths.length == 0) {
// 如果已存处理器方法和当前处理器方法名不同
if (!oldMappedMethod.getName().equals(handlerMethod.getName())) {
if (resolvedMethodName == null) {
// 使用方法名解析器解析方法名
resolvedMethodName = methodNameResolver.getHandlerMethodName(request);
}

// 如果解析方法名不等于已存方法名,则抛弃已存方法
if (!resolvedMethodName.equals(oldMappedMethod.getName())) {
oldMappedMethod = null;
}
// 如果解析方法名不等于当前方法名
if (!resolvedMethodName.equals(handlerMethod.getName())) {
// 如果已存方法名没有被抛弃,也就是解析方法名等于已存方法名
if (oldMappedMethod != null) {
// 恢复并仍然使用已存方法名
targetHandlerMethods.put(mappingInfo, oldMappedMethod);
oldMappedMethod = null;
}
else {
// 解析方法名既不等于已存方法名也不等于当前方法名
targetHandlerMethods.remove(mappingInfo);
}
}
}
}

// 解析方法名既不等于已存方法名也不等于当前方法名
if (oldMappedMethod != null) {
throw new IllegalStateException(
"Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" +
oldMappedMethod + ", " + handlerMethod +
"}. If you intend to handle the same path in multiple methods, then factor " +
"them out into a dedicated handler class with that path mapped at the type level!");
}
}
}
}

// 如果有一个或者多个处理器方法匹配
if (!targetHandlerMethods.isEmpty()) {
// 取得所有匹配的请求映射信息
List<RequestMappingInfo> matches = new ArrayList<RequestMappingInfo>(targetHandlerMethods.keySet());

// 获得可以对请求映射信息排列的对比器
RequestMappingInfoComparator requestMappingInfoComparator =
new RequestMappingInfoComparator(pathComparator);

// 排序
Collections.sort(matches, requestMappingInfoComparator);

// 第一个为最佳匹配处理器方法
RequestMappingInfo bestMappingMatch = matches.get(0);

// 取得最佳匹配处理器方法的最佳匹配路径
String bestMatchedPath = bestMappingMatch.bestMatchedPath();

if (bestMatchedPath != null) {
// 如果存在最佳匹配路径,则提取模板变量
extractHandlerMethodUriTemplates(bestMatchedPath, lookupPath, request);
}

// 返回处理器方法
return targetHandlerMethods.get(bestMappingMatch);
}
// 如果没有处理器方法匹配
else {
// 当URI Pattern匹配或者没有声明URI Pattern的时候,如果HTTP方法,请求参数或者头信息不匹配,则提示HTTP方法不支持,并且提示那些HTTP方法支持
if (!allowedMethods.isEmpty()) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(),
StringUtils.toStringArray(allowedMethods));
}
// 否则提示用户没有请求处理器方法
else {
throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(),
request.getParameterMap());
}
}
}
private String getMatchedPattern(String methodLevelPattern, String lookupPath, HttpServletRequest request) {
// 如果类型级别声明了请求映射注解的URI Pattern
if (hasTypeLevelMapping() && (!ObjectUtils.isEmpty(getTypeLevelMapping().value()))) {
// 取得类型级别声明的请求映射注解的URI Pattern
String[] typeLevelPatterns = getTypeLevelMapping().value();
// 遍历每一个URI Pattern
for (String typeLevelPattern : typeLevelPatterns) {
// 补充URI前缀
if (!typeLevelPattern.startsWith("/")) {
typeLevelPattern = "/" + typeLevelPattern;
}

// 结合方法级别的URL Pattern
String combinedPattern = pathMatcher.combine(typeLevelPattern, methodLevelPattern);

// 如果查找路径匹配结合的URL Pattern
if (isPathMatchInternal(combinedPattern, lookupPath)) {
// 返回匹配结合的URL Pattern
return combinedPattern;
}
}
// 如果无法匹配,则返回空
return null;
}

// 取得最佳匹配URI Pattern
String bestMatchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
// 如果存在最佳匹配URL Pattern
if (StringUtils.hasText(bestMatchingPattern)) {
// 结合最佳匹配URL Pattern和方法级别的URI Pattern
String combinedPattern = pathMatcher.combine(bestMatchingPattern, methodLevelPattern);
// 如果结合URI Pattern匹配,则使用这个结合的Pattern
if (!combinedPattern.equals(bestMatchingPattern) &&
(isPathMatchInternal(combinedPattern, lookupPath))) {
return combinedPattern;
}
}
// 【问题】问什么我们需要使用最佳匹配Pattern进行结合

// 如果类型级别没有声明请求映射注解的URI Pattern,则使用方法级别的URI Pattern进行匹配
if (isPathMatchInternal(methodLevelPattern, lookupPath)) {
return methodLevelPattern;
}
return null;
}
private boolean isPathMatchInternal(String pattern, String lookupPath) {
// 如果查找路径和URI Pattern相等或者匹配
if (pattern.equals(lookupPath) || pathMatcher.match(pattern, lookupPath)) {
return true;
}

// 增加后缀进行匹配
boolean hasSuffix = pattern.indexOf('.') != -1;
if (!hasSuffix && pathMatcher.match(pattern + ".*", lookupPath)) {
return true;
}

// 增加前缀进行匹配
boolean endsWithSlash = pattern.endsWith("/");
if (!endsWithSlash && pathMatcher.match(pattern + "/", lookupPath)) {
return true;
}
return false;
}
private void extractHandlerMethodUriTemplates(String mappedPath,
String lookupPath,
HttpServletRequest request) {
Map<String, String> variables = null;
boolean hasSuffix = (mappedPath.indexOf('.') != -1);

// 如果路径不存在.字符(例如.do),则使用模糊后缀匹配,增加.*进行匹配,意味着可以以任何字符串结尾
if (!hasSuffix && pathMatcher.match(mappedPath + ".*", lookupPath)) {
String realPath = mappedPath + ".*";
if (pathMatcher.match(realPath, lookupPath)) {
variables = pathMatcher.extractUriTemplateVariables(realPath, lookupPath);
}
}

// 使用模糊前缀进行匹配,如果URI Pattern没有以/开始,则表示可以以任意路径开始
if (variables == null && !mappedPath.startsWith("/")) {
String realPath = "/**/" + mappedPath;
if (pathMatcher.match(realPath, lookupPath)) {
variables = pathMatcher.extractUriTemplateVariables(realPath, lookupPath);
}
else {
// 如果路径没有匹配成功则增加模糊后缀继续匹配
realPath = realPath + ".*";
if (pathMatcher.match(realPath, lookupPath)) {
variables = pathMatcher.extractUriTemplateVariables(realPath, lookupPath);
}
}
}

// 导出得到的模板变量到请求属性中
if (!CollectionUtils.isEmpty(variables)) {
Map<String, String> typeVariables =
(Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
if (typeVariables != null) {
variables.putAll(typeVariables);
}
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, variables);
}
}


如何解析处理器方法参数呢?

注解方法处理器适配器是通过请求映射注解解析处理器方法的。解析得到了处理器方法,在调用处理器方法之前我们必须首先解析所有的处理器方法参数。处理器方法参数也是通过各种注解标记的,不同的注解包含着信息指导注解方法处理器适配器从不同的数据源取得数据。下面我们详细分析,处理器方法参数所支持的所有注解。

最常用的应用在处理器方法参数上的注解是请求参数注解 (@RequestParam) 。 请求参数注解指导注解方法处理器适配器通过参数名字找到请求参数值,并且赋值给当前方法参数。如下代码注释,

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
// 请求参数名字
String value() default "";
// 请求参数是不是必须的
boolean required() default true;
// 如果提供了缺省值,则请求参数自动变成非必须的
String defaultValue() default ValueConstants.DEFAULT_NONE;
}


请求头注解 (@RequestHeader) 指导注解方法处理器适配器通过头名字找到请求头的值,并且赋值给当前方法参数。如下代码注释,

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader {
// 请求头名字
String value() default "";
// 请求头是不是必须的
boolean required() default true;
// 如果提供了缺省值,则请求头自动变成非必须的
String defaultValue() default ValueConstants.DEFAULT_NONE;
}


请求体注解 (@RequestBody) 指导注解方法处理器适配器通过消息转换器将请求体转换成 Java 对象作为当前方法参数的值。但是请求注解并没有声明任何属性信息,它只是个标志,指导注解方法处理器适配器为当前参数解析请求体。如下代码注释,

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
}


Cookie 值注解 (@CookieValue) 指导注解方法处理器适配器通过 Cookie 名字找到 Cookie 的值,并且赋值给当前方法参数。如下代码注释,

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CookieValue {
// Cookie名字
String value() default "";
// Cookie值是不是必须的
boolean required() default true;
// 如果提供了缺省值,则Cookie值自动变成非必须的
String defaultValue() default ValueConstants.DEFAULT_NONE;
}


路径变量注解 (@PathVariable) 指导注解方法处理器适配器通过路径变量名字找到路径变量的值 ( 路径变量通常被称为模板变量 ) ,并且赋值给当前方法参数。如下代码注释,

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
// 路径变量的名字
String value() default "";
}


模型属性注解 (@ModelAttribute) 指导注解方法处理器适配器通过模型属性名字找到模型属性的值,并且赋值给当前方法参数。模型属性注解也可以用于声明在方法上,这种情况下把方法的返回值作为模型数据值放入隐式模型中。如下代码注释,

// 可以生命在参数上,作为参数的输入值,也可以生命在方法上,方法的返回值会被加入到隐式模型中
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModelAttribute {
// 模型属性的名字
String value() default "";
}


对于一个处理器方法参数,只能声明上面的注解中的一个,或者不声明注解。如果声明了上面注解中的一个,则根据注解解析处理器方法参数的值。如果一个处理器方法参数没有声明任何注解,则查看是否配置有客户化 Web 参数解析器,如果存在则使用客户化 Web 参数解析器进行解析。如果没有配置客户化 Web 参数解析器或者客户化 Web 参数解析器不能解析当前参数,则判断是不是标准的 WebRequest 类型,如果是则返回标准的 WebRequest 类型。如果上面的情况仍然不能解析参数值,则使用默认值。

如果通过上面的流程仍然没有解析出参数值,则做如下判断,

· 如果参数是模型类型或者 Map 类型,则使用当前隐式模型。
· 如果参数是 Session 状态类型,则使用当前的 Session 状态,它表明当前 Session 完成或者没有完成。
· 如果参数是 HTTP 实体,则解析 HTTP 请求体作为 HTTP 实体。
· 如果是错误对象,则必须前一个参数是绑定对象。否则,抛出异常,终止处理。下面将介绍绑定对象。
· 如果是简单数据类型,则初始化参数名为空。

最后,如果参数值仍然没有被解析,那么这个参数是一个需要绑定的 Bean 。如果这个方法参数声明了模型属性或者这个模型属性是 Session 属性,则从模型中或者 Session 中取得当前值,否则根据类型创建一个对象,再使用 HTTP 请求参数进行绑定操作。

此外,值注解 (@Value) 和校验注解 (@Valid) 是两个辅助的注解,并不直接绑定方法参数到任何 HTTP 请求信息。用来声明方法参数缺省值或者校验的。

值注解 (@Value) 用于给参数值指定缺省值,如果没有指定任何注解或者指定的注解所表达的数据为空,而且注解中没有指定缺省值,则使用这个指定的缺省值。如下代码注释,

@Retention(RetentionPolicy.RUNTIME)
// 可以应用到参数,字段和方法上
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Value {
// 指定缺省值
String value();
}


校验注解 (@Valid) 指导注解方法处理器适配器对当前的属性进行校验。这个注解没有任何属性,仅仅是一个标志。

@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface Valid {
}


整个流程实现在处理器方法解析器类中。如下代码所示,

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;
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++;
}
// Cookie值注解
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 ("Valid".equals(paramAnn.annotationType().getSimpleName())) {
validate = true;
}
}
// 不允许一个参数上有多个注解,但是@Value和@Valid除外
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 {
// 【问题】 如果参数声明类型过于细节化,则会出现类型转换异常,例如,你声明了一个客户化的模型类实现,这种情况下尽量生命更通用的类型,例如,Model, Map, SessionStatus等等

Class paramType = methodParam.getParameterType();
// 如果参数是Model或者Map的子类或者实现类,则使用隐式模型
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
args[i] = implicitModel;
}
// 如果参数是Session状态的子类或者实现类,则使用当前的Session状态对象
else if (SessionStatus.class.isAssignableFrom(paramType)) {
args[i] = this.sessionStatus;
}
// 如果参数是HTTP实体的子类或者实现类,则使用消息转换器将请求体转换作为HTTP实体对象,它类似于对请求体注解(@RequestBody)解析
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);
}
// 解析请求体值,这是通过HTTP消息转换器完成的
else if (requestBodyFound) {
args[i] = resolveRequestBody(methodParam, webRequest, handler);
}
// 解析Cookie值
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) {
// 如果声明了模型属性注解,或者此属性是Session属性,则可以从模型或者Session中取得,否则创建一个新的属性对象,然后进行绑定
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, !assignBindingResult);
}

// 取得绑定的参数值
args[i] = binder.getTarget();

// 将绑定结果赋值给下一个参数值,对于一个绑定参数,下一个参数一定是绑定结果或者错误对象
if (assignBindingResult) {
args[i + 1] = binder.getBindingResult();
i++;
}

// 将绑定结果放入到隐式模型中
implicitModel.putAll(binder.getBindingResult().getModel());
}
}
return args;
}
protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
throws Exception {
// 首先使用客户化的Web参数解析器进行解析
if (this.customArgumentResolvers != null) {
for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) {
Object value = argumentResolver.resolveArgument(methodParameter, webRequest);
if (value != WebArgumentResolver.UNRESOLVED) {
// 存在一个客户化的Web参数解析器解析到参数值,则返回此解析的参数值
return value;
}
}
}

// 取得参数类型
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;
}
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest) throws Exception {
// 如果期待Web请求类的子类,则返回NativeWebRequest参数
if (WebRequest.class.isAssignableFrom(parameterType)) {
return webRequest;
}
return WebArgumentResolver.UNRESOLVED;
}


我们理解,对于不同的参数注解,注解方法处理器适配器从不同的数据源提取数据,例如,请求参数,请求头或者请求体等,下面我们具体分析对于不同的参数注解,注解方法处理器适配器是如何解析方法参数值的。

下面是通过 HTTP 方法参数解析处理器方法参数值的 (@RequestParam) 。如下代码注释,

private Object resolveRequestParam(String paramName, boolean required, String defaultValue,
MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
throws Exception {
Class<?> paramType = methodParam.getParameterType();

// 如果参数是映射类型,这种情况下请求参数注解没有必要指定任何参数名字,返回所有参数名值对映射
if (Map.class.isAssignableFrom(paramType)) {
// 解析参数映射,这个参数映射包含所有参数名值对
return resolveRequestParamMap((Class<? extends Map>) paramType, webRequest);
}

// 如果参数名是空,请求参数注解没有指定任何参数名,则使用参数名解析器解析的参数名,这是通过解析方法参数名实现的
if (paramName.length() == 0) {
paramName = getRequiredParameterName(methodParam);
}

Object paramValue = null;
// 如果是多部请求体,则解析文件参数
if (webRequest.getNativeRequest() instanceof MultipartRequest) {
paramValue = ((MultipartRequest) webRequest.getNativeRequest()).getFile(paramName);
}

if (paramValue == null) {
// 如果是普通请求,则取得参数值
String[] paramValues = webRequest.getParameterValues(paramName);
// 如果参数类型不是数组,但是返回了参数数组值,则只使用数组中的第一个元素
if (paramValues != null && !paramType.isArray()) {
paramValue = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
// 否则,使用数组赋值
else {
paramValue = paramValues;
}
}

if (paramValue == null) {
// 如果请求参数注解中制定了缺省值,或者通过值注解(@value)中制定了缺省值
if (StringUtils.hasText(defaultValue)) {
// 使用指定的缺省值
paramValue = resolveDefaultValue(defaultValue);
}
else if (required) {
// 如果请求参数注解中制定了是必须的参数,则抛出异常,终止处理
raiseMissingParameterException(paramName, paramType);
}

// 检查参数值的合法性
paramValue = checkValue(paramName, paramValue, paramType);
}

// 通过绑定器转换参数值到需要的类型
WebDataBinder binder = createBinder(webRequest, null, paramName);
initBinder(handlerForInitBinderCall, paramName, binder, webRequest);
return binder.convertIfNecessary(paramValue, paramType, methodParam);
}
private Map resolveRequestParamMap(Class<? extends Map> mapType, NativeWebRequest webRequest) {
Map<String, String[]> parameterMap = webRequest.getParameterMap();

// 如果是多值映射
if (MultiValueMap.class.isAssignableFrom(mapType)) {
MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(parameterMap.size());
// 遍历所有的HTTP请求参数
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
// 遍历每一个HTTP请求参数的值
for (String value : entry.getValue()) {
// 加多个值到同一个参数名中,所以成为多值映射
result.add(entry.getKey(), value);
}
}
return result;
}
// 如果是单值映射,对于HTTP数组参数,则仅仅使用第一个元素
else {
Map<String, String> result = new LinkedHashMap<String, String>(parameterMap.size());
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
if (entry.getValue().length > 0) {
result.put(entry.getKey(), entry.getValue()[0]);
}
}
return result;
}
}
private String getRequiredParameterName(MethodParameter methodParam) {
String name = methodParam.getParameterName();
// 如果不能解析处理器方法参数名,则抛出异常
if (name == null) {
throw new IllegalStateException(
"No parameter name specified for argument of type [" + methodParam.getParameterType().getName() +
"], and no parameter name information found in class file either.");
}
return name;
}
private Object checkValue(String name, Object value, Class paramType) {
// 把应用空值制定为布尔false, 如果对于私有类型解析了空值,则抛出异常,终止处理
if (value == null) {
if (boolean.class.equals(paramType)) {
return Boolean.FALSE;
}
else if (paramType.isPrimitive()) {
throw new IllegalStateException("Optional " + paramType + " parameter '" + name +
"' is not present but cannot be translated into a null value due to being declared as a " +
"primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
}
}
return value;
}


解析 HTTP 请求头作为处理器方法参数值的实现和解析 HTTP 请求参数作为处理器方法参数值的实现相似 (@RequestHeader) 。这里不再进行代码注释。

下面是通过 HTTP 请求体解析处理器方法参数值的 (@RequestBody) 。如下代码注释,

protected Object resolveRequestBody(MethodParameter methodParam, NativeWebRequest webRequest, Object handler)
throws Exception {
// 创建HTTP请求的代理对象,可以从这个对象取得请求体的流对象,取得方法参数类型,然后使用消息转换器进行解析
return readWithMessageConverters(methodParam, createHttpInputMessage(webRequest), methodParam.getParameterType());
}
private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType)
throws Exception {
// 确定HTTP请求的内容类型
MediaType contentType = inputMessage.getHeaders().getContentType();

// 如果没有指定HTTP请求的内容类型,抛出不支持的HTTP媒体类型异常
if (contentType == null) {
StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));
String paramName = methodParam.getParameterName();
if (paramName != null) {
builder.append(' ');
builder.append(paramName);
}
throw new HttpMediaTypeNotSupportedException(
"Cannot extract parameter (" + builder.toString() + "): no Content-Type found");
}
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
if (this.messageConverters != null) {
// 遍历所有的消息转换器
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
// 保存支持的媒体类型,如果解析请求体失败,则提示用户那些媒体类型是支持的
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());

// 判断是否有消息转换器可以解析HTTP请求体
if (messageConverter.canRead(paramType, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + paramType.getName() + "] as /"" + contentType
+"/" using [" + messageConverter + "]");
}
// 如果存在,则返回转换的对象引用
return messageConverter.read(paramType, inputMessage);
}
}
}

// 如果解析失败,则抛出异常
throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
}


事实上,有另外一个方法可以用来解析请求体作为处理器方法参数,那就是通过声明请求参数作为 HttpEntity 类型, HttpEntity 类型可以指定模板类型进行泛化。对比请求体注解, HttpEntity 参数不需要注解,注解方法处理器适配器是通过类型来判断是否需要解析方法体的。如下代码注释,

private HttpEntity resolveHttpEntityRequest(MethodParameter methodParam, NativeWebRequest webRequest)
throws Exception {
// 创建HTTP请求的代理对象
HttpInputMessage inputMessage = createHttpInputMessage(webRequest);

// 返回声明的参数类型,例如, 如果参数是HttpEntity<Resource>, 这个方法返回Reource.class, 如果参数是HttpEntity<Resource[].class>, 这个方法返回Resource[].class
Class<?> paramType = getHttpEntityType(methodParam);

// 使用方法转换器进行解析
Object body = readWithMessageConverters(methodParam, inputMessage, paramType);

// 解析请求体后,构造HTTP实体对象,因为真正需要返回的是HTTP实体对象,而不是参数类型对象
return new HttpEntity<Object>(body, inputMessage.getHeaders());
}
private Class<?> getHttpEntityType(MethodParameter methodParam) {
// 校验参数类型是HTTP实体类型或者子类
Assert.isAssignable(HttpEntity.class, methodParam.getParameterType());

// 取得模板参数化类型
ParameterizedType type = (ParameterizedType) methodParam.getGenericParameterType();

// 校验模板参数化类型只有一个参数类型
if (type.getActualTypeArguments().length == 1) {
Type typeArgument = type.getActualTypeArguments()[0];
// 如果参数类型是单值,则直接返回
if (typeArgument instanceof Class) {
return (Class<?>) typeArgument;
}
// 如果参数类型是数组,则通过构造一个空数组返回数组类型
else if (typeArgument instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) typeArgument).getGenericComponentType();
if (componentType instanceof Class) {
// Surely, there should be a nicer way to do this
Object array = Array.newInstance((Class<?>) componentType, 0);
return array.getClass();
}
}
}

// 如果模板参数化类型没有参数或者有多个参数,则抛出异常,终止处理
throw new IllegalArgumentException(
"HttpEntity parameter (" + methodParam.getParameterName() + ") is not parameterized");

}


我们知道,请求体注解 (@RequestBody) 和 HTTP 实体 (HttpEntity) 的实现都是通过消息转换器进行解析 HTTP 请求体作为处理器方法参数的。消息转换器的设计和处理器适配器十分相似,消息转换器有一个方法判断是否一个消息转换器可以转换某个类型的参数,当然,还有另外一个方法用于事实上转换请求体到一个指定类型的对象。后面我们将有一小节讨论 HTTP 消息转换器的实现体系结构。

下面是通过 Cookie 值解析处理器方法参数值的 (@CookieValue) 。如下代码注释,

private Object resolveCookieValue(String cookieName, boolean required, String defaultValue,
MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
throws Exception {
// 取得参数类型
Class<?> paramType = methodParam.getParameterType();

// 如果没有指定Cookie名,则默认使用处理器方法参数名
if (cookieName.length() == 0) {
cookieName = getRequiredParameterName(methodParam);
}

// 通过Cookie名解析Cookie值
Object cookieValue = resolveCookieValue(cookieName, paramType, webRequest);

// 如果解析失败
if (cookieValue == null) {
// 如果Cookie值注解中指定了缺省值,或者使用值注解(@Value)指定了缺省值,则使用缺省值
if (StringUtils.hasText(defaultValue)) {
cookieValue = resolveDefaultValue(defaultValue);
}
// 如果是必须的处理器方法值,则抛出异常,终止处理
else if (required) {
raiseMissingCookieException(cookieName, paramType);
}

// 校验解析的Cookie值
cookieValue = checkValue(cookieName, cookieValue, paramType);
}

// 使用数据绑定进行数据类型转换
WebDataBinder binder = createBinder(webRequest, null, cookieName);
initBinder(handlerForInitBinderCall, cookieName, binder, webRequest);
return binder.convertIfNecessary(cookieValue, paramType, methodParam);
}
//这是声明在处理器调用器中的占位符方法,子类必须改写其实现
protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
throws Exception {
throw new UnsupportedOperationException("@CookieValue not supported");
}
//子类改写父类的占位符,从请求中解析Cookie值
protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
throws Exception {
// 取得HTTP请求对象
HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest();

// 取Cookie值
Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);

// 如果处理器方法参数是Cookie类型,则直接返回
if (Cookie.class.isAssignableFrom(paramType)) {
return cookieValue;
}
// 否则返回字符串值
else if (cookieValue != null) {
return cookieValue.getValue();
}
else {
return null;
}
}


下面是通过模板变量解析处理器方法参数值的 (@PathVariant) 。如下代码注释,

private Object resolvePathVariable(String pathVarName, MethodParameter methodParam,
NativeWebRequest webRequest, Object handlerForInitBinderCall) throws Exception {
// 取得参数类型
Class<?> paramType = methodParam.getParameterType();

// 如果没有制定路径变量名,则使用参数名本身
if (pathVarName.length() == 0) {
pathVarName = getRequiredParameterName(methodParam);
}

// 解析路径变量值,这些值是在请求映射实现中保存的
String pathVarValue = resolvePathVariable(pathVarName, paramType, webRequest);

// 使用数据绑定转换到期望的类型对象
WebDataBinder binder = createBinder(webRequest, null, pathVarName);
initBinder(handlerForInitBinderCall, pathVarName, binder, webRequest);
return binder.convertIfNecessary(pathVarValue, paramType, methodParam);
}
//占位符方法
protected String resolvePathVariable(String pathVarName, Class paramType, NativeWebRequest webRequest)
throws Exception {
throw new UnsupportedOperationException("@PathVariable not supported");
}
//以上占位符方法的实现
protected String resolvePathVariable(String pathVarName, Class paramType, NativeWebRequest webRequest)
throws Exception {
// 取得HTTP请求
HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest();

// 取得路径变量(模板变量),这些变量是在请求映射的实现中保存的
Map<String, String> uriTemplateVariables =
(Map<String, String>) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);

// 失败找到模板变量,所有模板变量都是必须的
if (uriTemplateVariables == null || !uriTemplateVariables.containsKey(pathVarName)) {
throw new IllegalStateException(
"Could not find @PathVariable [" + pathVarName + "] in @RequestMapping");
}

// 返回模板变量值
return uriTemplateVariables.get(pathVarName);
}


最后我们分析一下注解方法处理器适配器是如何绑定领域模型对象,初始化领域模型对象,和管理领域模型对象的。

当一个参数上没有声明任何注解,而且参数没有缺省值并且不是一个简单的属性,也就是说,它是一个领域对象模型对象,也就是一个 Bean 对象。那么则需要数据绑定对象把请求参数数据绑定到 Bean 的属性上。请看解析参数主流程的代码,这个片段的代码如下注释所示,

// 如果领域对象模型Bean存在在隐式模型中,则取得领域对象模型Bean,否则,根据领域对象模型类型,实例一个对象
WebDataBinder binder =
resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
// 如果有下一个参数,而且下一个参数是错误类型的子类,则同时复制绑定结果
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
// 如果将要绑定的领域对象模型Bean不为空,则绑定Web参数到对象模型中
if (binder.getTarget() != null) {
doBind(binder, webRequest, validate, !assignBindingResult);
}
// 取得绑定过后的结果做为参数
args[i] = binder.getTarget();
// 如果下一个参数是绑定结果对象,则把绑定结果赋值给下一个参数
if (assignBindingResult) {
args[i + 1] = binder.getBindingResult();
i++;
}
// 把绑定结果放入隐式模型中
implicitModel.putAll(binder.getBindingResult().getModel());


首先,它在隐式模型中查找领域对象模型 Bean ,因为这个领域对象模型可能是被标志有模型属性注解 (@ModelAttribute) 的方法返回的。如果隐式模型中不包含领域对象模型 Bean ,而且当前领域对象模型 Bean 是一个 Session 属性,则在 Session 中取得领域对象模型 Bean 。如果仍然不能解析领域对象模型 Bean ,则通过反射实例化一个全新的类型进行绑定。如下代码注释,

private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
// 如果此参数没有生命模型属性注解,则此参数没有属性名,使用类型自动生成的名字
String name = attrName;
if ("".equals(name)) {
name = Conventions.getVariableNameForParameter(methodParam);
}

// 取得参数类型
Class<?> paramType = methodParam.getParameterType();
Object bindObject;

// 如果隐式模型中存在此领域对象模型Bean,则使用它
if (implicitModel.containsKey(name)) {
bindObject = implicitModel.get(name);
}
// 如果是Session属性,则使用Session中的Bean
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");
}
}
// 如果隐式模型和Session中都没有此领域对象模型Bean,则实例化一个新的
else {
bindObject = BeanUtils.instantiateClass(paramType);
}

// 创建绑定对象
WebDataBinder binder = createBinder(webRequest, bindObject, name);

// 初始化绑定,首先使用客户化的绑定初始化器进行初始化,这个初始化器是所有处理器共享的,然后,再使用处理器生命初始化绑定方法(@InitBinder)进行初始化,这些初始化是处理器指定的
initBinder(handler, name, binder, webRequest);
return binder;
}
protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
throws Exception {
// 创建一个Web请求数据绑定,这个类库扩展了数据绑定类,实现了绑定HTTP请求参数到Bean对象
return new WebRequestDataBinder(target, objectName);
}
protected void initBinder(Object handler, String attrName, WebDataBinder binder, NativeWebRequest webRequest)
throws Exception {
// 首先使用配置的初始化绑定器进行初始化,绑定初始化器有一个缺省的实现ConfigurableWebBindingInitializer,这个实现支持许多配置选项
if (this.bindingInitializer != null) {
this.bindingInitializer.initBinder(binder, webRequest);
}

// 取得生命在处理器中的初始化绑定方法,也就是标记有初始化绑定器注解(@InitBinder)的方法
if (handler != null) {
Set<Method> initBinderMethods = this.methodResolver.getInitBinderMethods();

// 如果存在初始化绑定方法
if (!initBinderMethods.isEmpty()) {
boolean debug = logger.isDebugEnabled();
// 遍历调用每一个方法
for (Method initBinderMethod : initBinderMethods) {
// 找到桥梁方法
Method methodToInvoke = BridgeMethodResolver.findBridgedMethod(initBinderMethod);

// 初始化绑定器注解可以声明应用在哪些属性上
String[] targetNames = AnnotationUtils.findAnnotation(methodToInvoke, InitBinder.class).value();

// 如果初始化绑定注解声明中包含此属性名或者没有生命任何属性名
if (targetNames.length == 0 || Arrays.asList(targetNames).contains(attrName)) {
// 解析初始化绑定器的所需要的方法
Object[] initBinderArgs =
resolveInitBinderArguments(handler, methodToInvoke, binder, webRequest);
if (debug) {
logger.debug("Invoking init-binder method: " + methodToInvoke);
}

// 设置方法可以存取属性
ReflectionUtils.makeAccessible(methodToInvoke);

// 调用方法,并且禁止放回任何返回值
Object returnValue = methodToInvoke.invoke(handler, initBinderArgs);
if (returnValue != null) {
throw new IllegalStateException(
"InitBinder methods must not have a return value: " + methodToInvoke);
}
}
}
}
}


现在我们看到,一个绑定对象初始化完毕后,这包括应用在所有处理器上的绑定初始化器的初始化,和声明在处理器内部的绑定初始化方法,然后开始做真正的绑定操作。这个操作就是把 HTTP 请求参数设置到绑定的目标对象中。如下代码所示,

private void doBind(WebDataBinder binder, NativeWebRequest webRequest, boolean validate, boolean failOnErrors)
throws Exception {
// 代理到事实的绑定方法中
doBind(binder, webRequest);

// 如果设置了校验,则校验绑定,绑定结果会赋值给下一个Errors对象,并且会放入到隐式模型中给试图进行展示
if (validate) {
binder.validate();
}
if (failOnErrors && binder.getBindingResult().hasErrors()) {
throw new BindException(binder.getBindingResult());
}
}
protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
/ 既然创建的是Web请求数据绑定,则调用Web请求数据绑定的绑定方法
((WebRequestDataBinder) binder).bind(webRequest);
}


Web 数据绑定是数据绑定的子类,主要是分析 HTTP 请求参数包含的数据,这包括多部文件 HTTP 请求,然后,将收集的参数信息赋值给绑定的目标对象。如下代码注释,

public class WebRequestDataBinder extends WebDataBinder {
// 提供API方法绑定目标对象到HTTP请求对象
public void bind(WebRequest request) {
// 从HTTP请求中提取请求参数,这些包括写在URL中的参数和普通POST体的参数
MutablePropertyValues mpvs = new MutablePropertyValues(request.getParameterMap());

// 如果是多部文件HTTP请求,则也需要绑定多部文件HTTP请求体的文件参数,这些不包含URL中的参数
if (request instanceof NativeWebRequest) {
MultipartRequest multipartRequest = ((NativeWebRequest) request).getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
bindMultipartFiles(multipartRequest.getFileMap(), mpvs);
}
}

doBind(mpvs);
}
}
public class WebDataBinder extends DataBinder {
protected void doBind(MutablePropertyValues mpvs) {
// 检查是否存在缺省字段,缺省字段指定的值是在此字段没有值的时候应用的,这个缺省字段是以!开头的
checkFieldDefaults(mpvs);
// 检查是否存在标记字段,标记字段指对那些没有指定的请求参数,需要清空原来的已有的值
checkFieldMarkers(mpvs);
super.doBind(mpvs);
}

protected void bindMultipartFiles(Map<String, MultipartFile> multipartFiles, MutablePropertyValues mpvs) {
for (Map.Entry<String, MultipartFile> entry : multipartFiles.entrySet()) {
String key = entry.getKey();
MultipartFile value = entry.getValue();
// 可以配置是否绑定空的文件参数
if (isBindEmptyMultipartFiles() || !value.isEmpty()) {
mpvs.add(key, value);
}
}
}
}
public class DataBinder implements PropertyEditorRegistry, TypeConverter {

protected void doBind(MutablePropertyValues mpvs) {
// 可以配置哪些字段是允许的,哪些字段是必须的
checkAllowedFields(mpvs);
checkRequiredFields(mpvs);

// 事实上赋值属性
applyPropertyValues(mpvs);
}

protected void applyPropertyValues(MutablePropertyValues mpvs) {
try {
// getPropertyAccessor()返回的是BeanWrapper实现, BeanWrapper是用來存取Bean属性的封装类
getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
}
catch (PropertyBatchUpdateException ex) {
// 使用绑定错误处理器处理绑定错误结果
for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
}
}
}
}


数据绑定,属性校验,属性编辑器的实现是一个复杂的话题,属于 Spring 框架的核心实现,我们不在这里进行分析和代码注释。

如何调用处理器方法呢?

在得到方法参数以前,注解方法处理器适配器首先处理模型属性方法,初始化隐式模型,如果存在 Session 属性,导入 Session 属性,然后使用解析得到的方法参数调用处理器方法。如下代码所示,

public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
// 取得真正的处理器方法,而不是桥梁方法,桥梁方法是编译器产生的方法
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
try {
boolean debug = logger.isDebugEnabled();

// 如果有Session属性,则提取Session属性到隐式模型属性中
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
if (attrValue != null) {
implicitModel.addAttribute(attrName, attrValue);
}
}

// 遍历所有的模型属性方法,将其返回的模型属性放入隐式模型中
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(attributeMethodToInvoke, ModelAttribute.class).value();
if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
continue;
}

// 调用模型属性方法,得到属性值
Object attrValue = doInvokeMethod(attributeMethodToInvoke, 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);
}
}

// 通过注解,缺省值等注解解析处理器参数
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
}

return doInvokeMethod(handlerMethodToInvoke, handler, args);
}
catch (IllegalStateException ex) {
// Throw exception with full handler method context...
throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
}
}
private Object doInvokeMethod(Method method, Object target, Object[] args) throws Exception {
// 使方法可见
ReflectionUtils.makeAccessible(method);
try {
// 调用方法
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {
ReflectionUtils.rethrowException(ex.getTargetException());
}
throw new IllegalStateException("Should never get here");
}


如果映射处理器方法返回值和隐式模型到模型和视图对象呢?

注解方法处理器适配器调用一个处理器方法之后,得到了处理器方法的返回值,它根据返回值的类型解析模型和视图对象,如果声明了消息响应体,它将使用消息转换器转换返回值到 HTTP 响应体。我们已经分析了消息转换器的实现体系结构,这里不再进行分析。如下代码所示,

public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue,
ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
// 如果声明了响应状态注解
ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
if (responseStatusAnn != null) {
// 从响应状态注解中解析设置的HTTP响应状态代码
HttpStatus responseStatus = responseStatusAnn.value();

// to be picked up by the RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);

// 设置响应状态代码
webRequest.getResponse().setStatus(responseStatus.value());
responseArgumentUsed = true;
}
// Invoke custom resolvers if present...
if (customModelAndViewResolvers != null) {
for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
ModelAndView mav = mavResolver
.resolveModelAndView(handlerMethod, handlerType, returnValue, implicitModel, webRequest);
if (mav != ModelAndViewResolver.UNRESOLVED) {
return mav;
}
}
}
// 如果参数类型是HTTP实体类型
if (returnValue instanceof HttpEntity) {
handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
return null;
}
//如果声明了请求体注解,则使用消息转换器解析返回值到相应体
else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
handleResponseBody(returnValue, webRequest);
return null;
}
//如果返回值已经是模型和视图类型,则合并隐式模型的值
else if (returnValue instanceof ModelAndView) {
ModelAndView mav = (ModelAndView) returnValue;
mav.getModelMap().mergeAttributes(implicitModel);
return mav;
}	// 如果返回值是模型,则合并它和隐式模型,并且构造一个模型和视图对象
else if (returnValue instanceof Model) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
}
// 如果返回值是视图,则合并它和隐式模型,并且构造一个模型和视图对象
else if (returnValue instanceof View) {
return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
}
// 如果是模型属性方法,则加返回值到模型中,并且合并隐式模型
else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
// 如果返回值是映射,则合并它和隐式模型,构造模型和视图对象
else if (returnValue instanceof Map) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);
}
// 如果返回值是字符串,它是视图的逻辑名,构造一个模型和视图对象
else if (returnValue instanceof String) {
return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
}
// 如果返回值为空,
else if (returnValue == null) {
// 如果设置了响应状态, 或者没有修改状态, 返回空值
if (this.responseArgumentUsed || webRequest.isNotModified()) {
return null;
}
else {
// Assuming view name translation...
return new ModelAndView().addAllObjects(implicitModel);
}
}
// 如果是一个Bean值,加入到模型中,并且合并隐式模型,返回模型和视图对象
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
// Assume a single model attribute...
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else {
throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
}
}
private void handleResponseBody(Object returnValue, ServletWebRequest webRequest)
throws Exception {
if (returnValue == null) {
return;
}
HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);

// 使用消息转换器写返回值
writeWithMessageConverters(returnValue, inputMessage, outputMessage);
}
private void handleHttpEntityResponse(HttpEntity<?> responseEntity, ServletWebRequest webRequest)
throws Exception {
if (responseEntity == null) {
return;
}
HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);

// 如果返回值是相应体,则包含相应状态,需要写入HTTP响应里
if (responseEntity instanceof ResponseEntity && outputMessage instanceof ServerHttpResponse) {
((ServerHttpResponse)outputMessage).setStatusCode(((ResponseEntity) responseEntity).getStatusCode());
}

// 把返回的HTTP实体的头信息写入HTTP相应里
HttpHeaders entityHeaders = responseEntity.getHeaders();
if (!entityHeaders.isEmpty()) {
outputMessage.getHeaders().putAll(entityHeaders);
}
Object body = responseEntity.getBody();
if (body != null) {
// 转换返回值并且写入HTTP相应体里
writeWithMessageConverters(body, inputMessage, outputMessage);
}
}
@SuppressWarnings("unchecked")
private void writeWithMessageConverters(Object returnValue,
HttpInputMessage inputMessage, HttpOutputMessage outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException {
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();

// 如果HTTP请求没有制定接受的媒体类型,默认接受所有媒体类型
if (acceptedMediaTypes.isEmpty()) {
acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
}
MediaType.sortByQualityValue(acceptedMediaTypes);
Class<?> returnValueType = returnValue.getClass();
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
if (getMessageConverters() != null) {
// 使用HTTP消息转换器转换返回值作为HTTP相应体
for (MediaType acceptedMediaType : acceptedMediaTypes) {
for (HttpMessageConverter messageConverter : getMessageConverters()) {
if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
messageConverter.write(returnValue, acceptedMediaType, outputMessage);
if (logger.isDebugEnabled()) {
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType == null) {
contentType = acceptedMediaType;
}
logger.debug("Written [" + returnValue + "] as /"" + contentType +
"/" using [" + messageConverter + "]");
}
this.responseArgumentUsed = true;
return;
}
}
}
for (HttpMessageConverter messageConverter : messageConverters) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
}
}
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
}


如何更新模型数据呢?

最后,如果存在 Session 属性,则导出 Session 属性从模型到 Session 中,如下代码所示,

public final void updateModelAttributes(Object handler, Map<String, Object> mavModel,
ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {
// 如果Session状态标识成结束,则清除Session属性,默认情况下Session始终是未完成的
if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
}
}
// Expose model attributes as session attributes, if required.
// Expose BindingResults for all attributes, making custom editors available.
Map<String, Object> model = (mavModel != null ? mavModel : implicitModel);
for (String attrName : new HashSet<String>(model.keySet())) {
Object attrValue = model.get(attrName);
boolean isSessionAttr =
this.methodResolver.isSessionAttribute(attrName, (attrValue != null ? attrValue.getClass() : null));
// 如果Session属性,而且Session状态是未完成的,则导出Session属性从模型到Session中
if (isSessionAttr && !this.sessionStatus.isComplete()) {
this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
}

// 同时导出绑定结构到Session中
if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) &&
(isSessionAttr || isBindingCandidate(attrValue))) {
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
if (mavModel != null && !model.containsKey(bindingResultKey)) {
WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
initBinder(handler, attrName, binder, webRequest);
mavModel.put(bindingResultKey, binder.getBindingResult());
}
}
}
}


基于简单控制器流程的实现和基于注解控制器流程的实现是两个用来处理 HTTP 请求的 Spring Web MVC 流程的实现。他们使用了不同的方式实现了相同的功能和流程,简单控制器流程是经典的实现,而基于注解控制器流程的实现是从版本 2.5 新引入的,其简单易用又不失于功能强大,使基于注解控制器流程的实现成为被推荐使用的流程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐