- 浏览: 84895 次
- 性别:
- 来自: 西安
最新评论
-
w67856101:
sssss
在Spring中使用jcaptcha实现图片验证 -
yjq8116:
不错。很有收获
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(4) -
rolenz:
mongodb越来越火,但是网上的资料不是很全,希望深入的写些 ...
MongoDB学习(二)索引的用法 -
sha0k:
rolenz 写道能不能说说replSet到底如何进行认证我才 ...
MongoDB学习(一)安装和基本使用 -
rolenz:
能不能说说replSet到底如何进行认证
MongoDB学习(一)安装和基本使用
每天敦促自己阅读spring源码 绝不松懈
下面是当没有注册HandlerAdapter时,spring提供的默认HandlerAdapter的实现类
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
上一节看完了DefaultAnnoationHandlerMapping类,HandlerMapping对request的处理就差不多了解完了,接下来再看HandlerAdapter的概念,该接口有两个重要方法(方法所做的事情就不解释了,注释已经很清楚了):
/** * Given a handler instance, return whether or not this HandlerAdapter can * support it. Typical HandlerAdapters will base the decision on the handler * type. HandlerAdapters will usually only support one handler type each. * <p>A typical implementation: * <p><code> * return (handler instanceof MyHandler); * </code> * @param handler handler object to check * @return whether or not this object can use the given handler */ boolean supports(Object handler);
/** * Use the given handler to handle this request. * The workflow that is required may vary widely. * @param request current HTTP request * @param response current HTTP response * @param handler handler to use. This object must have previously been passed * to the <code>supports</code> method of this interface, which must have * returned <code>true</code>. * @throws Exception in case of errors * @return ModelAndView object with the name of the view and the required * model data, or <code>null</code> if the request has been handled directly */ ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
先来看SimpleControllerHandlerAdapter对这两个方法的实现,一句话的事情,因为太简单了:
public boolean supports(Object handler) { return (handler instanceof Controller); } public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((Controller) handler).handleRequest(request, response); return null; }
Controller接口只是定义了handleRequest方法,所以这里的handle处理,就交给了我们实现了Controller的接口的处理类了。到这里,思路就理的很清楚了,但是我忽然有问题,这里适配支持了Controller的实现类,处理调用的是handleRequest,那method对request的处理是怎么回事呢,我们知道在detectHandler时,所有的@Request的value都被对准了一个类。先把我自己的疑惑带着吧,后面我看怎么破解我自己的疑惑。
下面就来看看AnnotationMethodHandlerAdapter类吧,这个类可比SimpleControllerHandlerAdapter复杂多了,先来看看supports方法:
public boolean supports(Object handler) { return getMethodResolver(handler).hasHandlerMethods(); }
接着去看getMethodResolver方法吧:
/** * Build a HandlerMethodResolver for the given handler type. */ private ServletHandlerMethodResolver getMethodResolver(Object handler) { Class handlerClass = ClassUtils.getUserClass(handler); ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass); if (resolver == null) { resolver = new ServletHandlerMethodResolver(handlerClass); this.methodResolverCache.put(handlerClass, resolver); } return resolver; }
首先我查看了ClassUtils的源码,为了查找getUserClass的涵义:
/** * Return the user-defined class for the given instance: usually simply * the class of the given instance, but the original class in case of a * CGLIB-generated subclass. * @param instance the instance to check * @return the user-defined class */ public static Class<?> getUserClass(Object instance) { Assert.notNull(instance, "Instance must not be null"); return getUserClass(instance.getClass()); }
/** * Return the user-defined class for the given class: usually simply the given * class, but the original class in case of a CGLIB-generated subclass. * @param clazz the class to check * @return the user-defined class */ public static Class<?> getUserClass(Class<?> clazz) { return (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR) ? clazz.getSuperclass() : clazz); }
这里得到的结果无非是handler的父类或者自身,再回到getMethodResolver方法,下面是从缓存中取出handlerClass对应的ServletHandlerMethodResolver对象,如果缓存中没有,那么就重新构造。从看了后面的代码,我头大了,真心的,你们是不知道后面有多麻烦,先来说明ServletHandlerMethodResolver它的意思,可以理解为解析能作为serlvet使用的方法,这个内部类有点儿大,慢慢来看,先来看它的构造函数。
private ServletHandlerMethodResolver(Class<?> handlerType) { init(handlerType); }
这里的init是它的父类HandlerMethodResovler的初始化方法:
/** * Initialize a new HandlerMethodResolver for the specified handler type. * @param handlerType the handler class to introspect */ public void init(Class<?> handlerType) { 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) { 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); SessionAttributes sessionAttributes = handlerType.getAnnotation(SessionAttributes.class); this.sessionAttributesFound = (sessionAttributes != null); if (this.sessionAttributesFound) { this.sessionAttributeNames.addAll(Arrays.asList(sessionAttributes.value())); this.sessionAttributeTypes.addAll(Arrays.asList(sessionAttributes.types())); } }
这里又出现了RelectionUtils的doWithMethods方法,我在学习札记(五)里,已经详细介绍了这个方法,在回调处理函数中,我得一层层的来剥if判断,首先是isHandlerMethod
protected boolean isHandlerMethod(Method method) { return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null; }
这个很好理解了,就是查找该方法的注解,看看有没有@RequestMapping如果有,那证明它就是请求处理方法。将它加入handlerMethods集合中,下面的判断是Method类的方法,isAnnotationPresent是其父类AccessibleObject的方法,内部实现则是getAnnotation方法,这一方法在AccessibleObject中是没有具体实现的,但并非抽象,方法内部很有意思。
public boolean isAnnotationPresent( Class<? extends Annotation> annotationClass) { return getAnnotation(annotationClass) != null; }
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { throw new AssertionError("All subclasses should override this method"); }
Method类中:
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { if (annotationClass == null) throw new NullPointerException(); return (T) declaredAnnotations().get(annotationClass); }
private transient Map<Class, Annotation> declaredAnnotations; private synchronized Map<Class, Annotation> declaredAnnotations() { if (declaredAnnotations == null) { declaredAnnotations = AnnotationParser.parseAnnotations( annotations, sun.misc.SharedSecrets.getJavaLangAccess(). getConstantPool(getDeclaringClass()), getDeclaringClass()); } return declaredAnnotations; }
注意刚才的doWithMethods方法,它是有3个参数的,最有一个参数是一个过滤器:
/** * Pre-built MethodFilter that matches all non-bridge methods. */ public static MethodFilter NON_BRIDGED_METHODS = new MethodFilter() { public boolean matches(Method method) { return !method.isBridge(); } };
什么是bridge方法,参见http://freish.iteye.com/blog/1158008 ,
再回到AnnotationMethodHandlerAdapter类的support方法中,我们可以看到hasHandlerMethods方法,该方法是HandlerMethodResolver类中方法:
public final boolean hasHandlerMethods() { return !this.handlerMethods.isEmpty(); }
只要这个handler中有@RequestMapping注解的方法,那么这个handler必然不为空。
下面我们再进入AnnotationMethodHandlerAdapter类的handle方法,看的好累 不过总算收获不小:
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (AnnotationUtils.findAnnotation(handler.getClass(), SessionAttributes.class) != null) { // 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); } } } return invokeHandlerMethod(request, response, handler); }
首先判断该handler是否有@SessionAttributes的注解,如果有调用WebContentGenerator的checkAndPrepare。重点不在这里,重点在于invokeHandlerMethod方法:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ServletHandlerMethodResolver methodResolver = getMethodResolver(handler); Method handlerMethod = methodResolver.resolveHandlerMethod(request); ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver); 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); methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest); return mav; }
getMethodResolver方法,我们已经看过了,因为已经创建了一次ServletHandlerMethodResolver对象为该handler,所以这次执行直接从缓存Map中读取了,下面就是为request找到适合的Method,使用resolveHandlerMethod方法,这个代码太长了,我贴上来你们别嫌烦。
public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException { String lookupPath = urlPathHelper.getLookupPathForRequest(request); 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); mappingInfo.paths = mapping.value(); if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) { mappingInfo.methods = mapping.method(); } if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) { mappingInfo.params = mapping.params(); } if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) { mappingInfo.headers = mapping.headers(); } boolean match = false; if (mappingInfo.paths.length > 0) { List<String> matchedPaths = new ArrayList<String>(mappingInfo.paths.length); for (String methodLevelPattern : mappingInfo.paths) { String matchedPattern = getMatchedPattern(methodLevelPattern, lookupPath, request); if (matchedPattern != null) { if (mappingInfo.matches(request)) { match = true; matchedPaths.add(matchedPattern); } else { for (RequestMethod requestMethod : mappingInfo.methods) { allowedMethods.add(requestMethod.toString()); } break; } } } Collections.sort(matchedPaths, pathComparator); mappingInfo.matchedPaths = matchedPaths; } else { // No paths specified: parameter match sufficient. match = mappingInfo.matches(request); if (match && mappingInfo.methods.length == 0 && mappingInfo.params.length == 0 && resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) { match = false; } else { for (RequestMethod requestMethod : mappingInfo.methods) { allowedMethods.add(requestMethod.toString()); } } } if (match) { Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod); if (oldMappedMethod != null && oldMappedMethod != handlerMethod) { 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 { if (!allowedMethods.isEmpty()) { throw new HttpRequestMethodNotSupportedException(request.getMethod(), StringUtils.toStringArray(allowedMethods)); } else { throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(), request.getParameterMap()); } } }
写不下去了,累死了,忙一天了,明天再弄吧,睡觉去了sorry~~~
发表评论
-
SpringMVC源码学习札记(六)DispatcherServlet类在3.2中的变化
2013-08-07 22:48 2257protected void doDispatch(Htt ... -
Spring MVC 源码学习札记(五)说说ReflectionUtils
2012-01-30 21:33 7814这里主要是为了解决我前面遇到的问题,因为在默认注解Defaul ... -
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(3)
2012-01-29 13:44 3883上一节主要阅读了BeanNameUrlHandlerMappi ... -
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(2)
2012-01-28 17:55 3906上一节我们看到 当***-serlvet.xml中未声明Ha ... -
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(1)
2012-01-28 13:35 7630在分析HandlerMapping和HandlerAdapte ... -
Spring MVC 源码学习札记(三)看看View怎么做到视图转发的
2012-01-21 00:16 3237上一节看完了ViewResolver解析视图名产生View的过 ... -
Spring MVC 源码学习札记(二)ViewResolver处理viewname
2012-01-19 17:53 6639第一节我们看到了ViewResolver对ModelAndVi ... -
Spring MVC 源码学习札记(一)DispatcherServlet阅读
2012-01-19 15:06 15008马上过年了~闲来无事,阅读Spring源码提升自己,做事要有个 ...
相关推荐
在本篇博客“SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门”中,我们将探讨Spring MVC框架的核心组件——HandlerMapping和HandlerAdapter。这两个组件是Spring MVC处理请求的关键,它们负责映射HTTP...
Spring MVC是Java EE开发中广泛使用的一个Web框架,它是Spring框架的一部分,主要负责Model-View-Controller(MVC)架构模式的...通过学习和实践,开发者可以更有效地利用Spring MVC框架来构建高性能的Web应用程序。
标题中的"开发Spring MVC应用程序补充—程序源码下载.rar_spring_spring mvc_spring mvc 源码_sp"表明这是一个关于Spring MVC框架的开发教程,其中包含了源代码供学习者参考。Spring MVC是Spring框架的一个核心组件...
通过深入学习这个 Spring MVC 项目源码,你可以掌握 MVC 设计模式的运用,了解 Spring 框架的组件协同工作方式,以及在实际项目中的最佳实践。这将有助于提升你的 Java Web 开发技能,更好地理解和使用 Spring MVC。
- 学习Spring MVC和MyBatis的整合,可以参考官方文档、教程和开源项目的源码。 - 通过调试源码,理解其实现原理,有助于提升实战技能。 总结,这个压缩包提供了一个完整的Spring MVC和MyBatis整合的示例,包含了...
而`spring-framework-2.5.6-with-docs.zip`可能包含了Spring 2.5.6的源码和文档,帮助开发者了解Spring MVC的内部实现和最佳实践。 总之,这个压缩包提供了开发基于Spring MVC和Hibernate的Java Web应用所需要的...
【Spring5MVC源码分析】 Spring MVC 是一个基于Java的、用于构建Web应用程序的、高度可插拔的MVC框架,它是Spring Framework的重要组成部分。Spring MVC的核心目标是简化前端控制器的开发,使得开发者可以专注于...
《精通Spring MVC 4:看透SpringMVC》是一本深度解析Spring MVC框架的专业书籍,旨在帮助读者全面理解和掌握Spring MVC 4的核心概念和技术。Spring MVC是Spring框架的一部分,专门用于构建Web应用程序,它提供了模型...
在 Spring MVC 中,核心类和接口是 DispatcherServlet、HandlerMapping、HandlerAdapter、Controller、ViewResolver、HandlerInterceptor、View 等。 DispatcherServlet 是 Spring MVC 的前置控制器,它负责处理...
### Spring MVC核心组件之HandlerMapping详解 #### 一、引言 在Java Web开发领域,Spring MVC框架因其灵活且强大的特性而备受青睐。...理解和掌握`HandlerMapping`的工作机制对于深入学习Spring MVC框架至关重要。
这个"Spring MVC 基础实例源码01"的资源很可能是为了帮助初学者理解Spring MVC的核心概念和基本用法。下面我们将详细探讨Spring MVC的一些关键知识点。 1. **MVC模式**:MVC(Model-View-Controller)是一种设计...
通过深入研究源码,我们可以更好地理解Spring MVC的运行机制,如何配置和使用其特性,以及如何优化和调试Spring MVC应用。对于开发人员来说,掌握Spring MVC的源码分析有助于提升Web应用开发的效率和质量。
- 创建Spring MVC的配置文件,例如`servlet-context.xml`,配置HandlerMapping、HandlerAdapter、ViewResolver等组件。 - 在配置文件中声明Controller Bean,通常使用注解@Controller。 3. **注解驱动开发**: -...
在这个学习记录总结中,我们将深入理解Spring MVC的核心概念、主要组件以及其工作流程。 1. Spring MVC 概述 Spring MVC 是Spring框架的一部分,它基于Spring IoC(Inversion of Control,控制反转)容器,简化了...
2. **配置 Spring**:在 `applicationContext.xml` 或者 Spring Boot 的配置文件中,配置 Spring MVC 的 DispatcherServlet、HandlerMapping 和 HandlerAdapter。同时,配置 Spring 的 DataSource,这是连接数据库的...
spring-webmvc-5.0.9.RELEASE-sources.jar则包含了源码,可以深入研究Spring Web MVC的实现细节,对于学习和调试都非常有帮助。 九、依赖管理 在实际项目中,Spring Web MVC往往与其他Spring模块如Core、AOP、Data...
这份“Spring.MVC学习指南.pdf”很可能是为了帮助开发者深入理解和掌握Spring MVC的核心概念、工作原理以及实践技巧。下面将详细阐述Spring MVC的相关知识点。 1. **核心组件**: - **DispatcherServlet**:Spring...
3. **创建Spring MVC配置**:创建一个XML文件(如`servlet-context.xml`),配置HandlerMapping、HandlerAdapter、视图解析器等。 4. **编写Controller**:创建一个名为`GreetingController`的Java类,使用`@...
8. **配置文件**:理解Spring MVC的配置文件,如`servlet-context.xml`,它定义了Spring MVC的组件,如视图解析器、HandlerMapping和HandlerAdapter等。 9. **Maven或者Gradle构建**:项目可能使用Maven或Gradle...