SpringMVC源码 HandlerMapping和HandlerAdapter(4)

本文深入探讨了Spring MVC中HandlerAdapter的工作原理,包括SimpleControllerHandlerAdapter与AnnotationMethodHandlerAdapter的具体实现,展示了如何通过这些组件支持不同类型的控制器。
摘要由CSDN通过智能技术生成

下面是当没有注册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的概念,该接口有两个重要方法(方法所做的事情就不解释了,注释已经很清楚了):

Java代码   收藏代码
  1. /** 
  2.      * Given a handler instance, return whether or not this HandlerAdapter can 
  3.      * support it. Typical HandlerAdapters will base the decision on the handler 
  4.      * type. HandlerAdapters will usually only support one handler type each. 
  5.      * <p>A typical implementation: 
  6.      * <p><code> 
  7.      * return (handler instanceof MyHandler); 
  8.      * </code> 
  9.      * @param handler handler object to check 
  10.      * @return whether or not this object can use the given handler 
  11.      */   
  12. boolean supports(Object handler);   
 
Java代码   收藏代码
  1. /** 
  2.      * Use the given handler to handle this request. 
  3.      * The workflow that is required may vary widely. 
  4.      * @param request current HTTP request 
  5.      * @param response current HTTP response 
  6.      * @param handler handler to use. This object must have previously been passed 
  7.      * to the <code>supports</code> method of this interface, which must have 
  8.      * returned <code>true</code>. 
  9.      * @throws Exception in case of errors 
  10.      * @return ModelAndView object with the name of the view and the required 
  11.      * model data, or <code>null</code> if the request has been handled directly 
  12.      */  
  13.     ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;  

 先来看SimpleControllerHandlerAdapter对这两个方法的实现,一句话的事情,因为太简单了:

Java代码   收藏代码
  1. public boolean supports(Object handler) {  
  2.         return (handler instanceof Controller);  
  3.     }  
  4.   
  5. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  6.             throws Exception {  
  7.   
  8.         ((Controller) handler).handleRequest(request, response);  
  9.         return null;  
  10.     }  

  Controller接口只是定义了handleRequest方法,所以这里的handle处理,就交给了我们实现了Controller的接口的处理类了。到这里,思路就理的很清楚了,但是我忽然有问题,这里适配支持了Controller的实现类,处理调用的是handleRequest,那method对request的处理是怎么回事呢,我们知道在detectHandler时,所有的@Request的value都被对准了一个类。先把我自己的疑惑带着吧,后面我看怎么破解我自己的疑惑。

  下面就来看看AnnotationMethodHandlerAdapter类吧,这个类可比SimpleControllerHandlerAdapter复杂多了,先来看看supports方法:

Java代码   收藏代码
  1. public boolean supports(Object handler) {  
  2.         return getMethodResolver(handler).hasHandlerMethods();  
  3.     }  

 接着去看getMethodResolver方法吧:

Java代码   收藏代码
  1. /** 
  2.  * Build a HandlerMethodResolver for the given handler type. 
  3.  */  
  4. private ServletHandlerMethodResolver getMethodResolver(Object handler) {  
  5.     Class handlerClass = ClassUtils.getUserClass(handler);  
  6.     ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);  
  7.     if (resolver == null) {  
  8.         resolver = new ServletHandlerMethodResolver(handlerClass);  
  9.         this.methodResolverCache.put(handlerClass, resolver);  
  10.     }  
  11.     return resolver;  
  12. }  
 

 首先我查看了ClassUtils的源码,为了查找getUserClass的涵义:

Java代码   收藏代码
  1. /** 
  2.      * Return the user-defined class for the given instance: usually simply 
  3.      * the class of the given instance, but the original class in case of a 
  4.      * CGLIB-generated subclass. 
  5.      * @param instance the instance to check 
  6.      * @return the user-defined class 
  7.      */   
  8.         public static Class<?> getUserClass(Object instance) {  
  9.         Assert.notNull(instance, "Instance must not be null");  
  10.         return getUserClass(instance.getClass());  
  11.     }  
 
Java代码   收藏代码
  1. /** 
  2.      * Return the user-defined class for the given class: usually simply the given 
  3.      * class, but the original class in case of a CGLIB-generated subclass. 
  4.      * @param clazz the class to check 
  5.      * @return the user-defined class 
  6.      */  
  7.     public static Class<?> getUserClass(Class<?> clazz) {  
  8.         return (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR) ?  
  9.                 clazz.getSuperclass() : clazz);  
  10.     }  

  这里得到的结果无非是handler的父类或者自身,再回到getMethodResolver方法,下面是从缓存中取出handlerClass对应的ServletHandlerMethodResolver对象,如果缓存中没有,那么就重新构造。从看了后面的代码,我头大了,真心的,你们是不知道后面有多麻烦,先来说明ServletHandlerMethodResolver它的意思,可以理解为解析能作为serlvet使用的方法,这个内部类有点儿大,慢慢来看,先来看它的构造函数。

Java代码   收藏代码
  1. private ServletHandlerMethodResolver(Class<?> handlerType) {  
  2.             init(handlerType);  
  3.         }  

 这里的init是它的父类HandlerMethodResovler的初始化方法:

Java代码   收藏代码
  1. /** 
  2.      * Initialize a new HandlerMethodResolver for the specified handler type. 
  3.      * @param handlerType the handler class to introspect 
  4.      */  
  5.     public void init(Class<?> handlerType) {  
  6.         Class<?>[] handlerTypes =  
  7.                 Proxy.isProxyClass(handlerType) ? handlerType.getInterfaces() : new Class<?>[] {handlerType};  
  8.         for (final Class<?> currentHandlerType : handlerTypes) {  
  9.             ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {  
  10.                 public void doWith(Method method) {  
  11.                     Method specificMethod = ClassUtils.getMostSpecificMethod(method, currentHandlerType);  
  12.                     if (isHandlerMethod(method)) {  
  13.                         handlerMethods.add(specificMethod);  
  14.                     }  
  15.                     else if (method.isAnnotationPresent(InitBinder.class)) {  
  16.                         initBinderMethods.add(specificMethod);  
  17.                     }  
  18.                     else if (method.isAnnotationPresent(ModelAttribute.class)) {  
  19.                         modelAttributeMethods.add(specificMethod);  
  20.                     }  
  21.                 }  
  22.             }, ReflectionUtils.NON_BRIDGED_METHODS);  
  23.         }  
  24.         this.typeLevelMapping = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);  
  25.         SessionAttributes sessionAttributes = handlerType.getAnnotation(SessionAttributes.class);  
  26.         this.sessionAttributesFound = (sessionAttributes != null);  
  27.         if (this.sessionAttributesFound) {  
  28.             this.sessionAttributeNames.addAll(Arrays.asList(sessionAttributes.value()));  
  29.             this.sessionAttributeTypes.addAll(Arrays.asList(sessionAttributes.types()));  
  30.         }  
  31.     }  

 这里又出现了RelectionUtils的doWithMethods方法,我在学习札记(五)里,已经详细介绍了这个方法,在回调处理函数中,我得一层层的来剥if判断,首先是isHandlerMethod

Java代码   收藏代码
  1. protected boolean isHandlerMethod(Method method) {  
  2.     return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null;  
  3. }  

 这个很好理解了,就是查找该方法的注解,看看有没有@RequestMapping如果有,那证明它就是请求处理方法。将它加入handlerMethods集合中,下面的判断是Method类的方法,isAnnotationPresent是其父类AccessibleObject的方法,内部实现则是getAnnotation方法,这一方法在AccessibleObject中是没有具体实现的,但并非抽象,方法内部很有意思。

Java代码   收藏代码
  1. public boolean isAnnotationPresent(  
  2.         Class<? extends Annotation> annotationClass) {  
  3.         return getAnnotation(annotationClass) != null;  
  4.     }  
 
Java代码   收藏代码
  1. public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {  
  2.         throw new AssertionError("All subclasses should override this method");  
  3.     }  

 Method类中:

Java代码   收藏代码
  1. public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {  
  2.         if (annotationClass == null)  
  3.             throw new NullPointerException();  
  4.   
  5.         return (T) declaredAnnotations().get(annotationClass);  
  6.     }  
 
Java代码   收藏代码
  1. private transient Map<Class, Annotation> declaredAnnotations;  
  2.   
  3.     private synchronized  Map<Class, Annotation> declaredAnnotations() {  
  4.         if (declaredAnnotations == null) {  
  5.             declaredAnnotations = AnnotationParser.parseAnnotations(  
  6.                 annotations, sun.misc.SharedSecrets.getJavaLangAccess().  
  7.                 getConstantPool(getDeclaringClass()),  
  8.                 getDeclaringClass());  
  9.         }  
  10.         return declaredAnnotations;  
  11.     }  

 注意刚才的doWithMethods方法,它是有3个参数的,最有一个参数是一个过滤器:

Java代码   收藏代码
  1. /** 
  2.      * Pre-built MethodFilter that matches all non-bridge methods. 
  3.      */  
  4.     public static MethodFilter NON_BRIDGED_METHODS = new MethodFilter() {  
  5.   
  6.         public boolean matches(Method method) {  
  7.             return !method.isBridge();  
  8.         }  
  9.     };  

 什么是bridge方法,参见http://freish.iteye.com/blog/1158008 ,

再回到AnnotationMethodHandlerAdapter类的support方法中,我们可以看到hasHandlerMethods方法,该方法是HandlerMethodResolver类中方法:

Java代码   收藏代码
  1. public final boolean hasHandlerMethods() {  
  2.         return !this.handlerMethods.isEmpty();  
  3.     }  

 只要这个handler中有@RequestMapping注解的方法,那么这个handler必然不为空。

下面我们再进入AnnotationMethodHandlerAdapter类的handle方法,看的好累 不过总算收获不小:

Java代码   收藏代码
  1. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  2.             throws Exception {  
  3.   
  4.         if (AnnotationUtils.findAnnotation(handler.getClass(), SessionAttributes.class) != null) {  
  5.             // Always prevent caching in case of session attribute management.  
  6.             checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);  
  7.             // Prepare cached set of session attributes names.  
  8.         }  
  9.         else {  
  10.             // Uses configured default cacheSeconds setting.  
  11.             checkAndPrepare(request, response, true);  
  12.         }  
  13.   
  14.         // Execute invokeHandlerMethod in synchronized block if required.  
  15.         if (this.synchronizeOnSession) {  
  16.             HttpSession session = request.getSession(false);  
  17.             if (session != null) {  
  18.                 Object mutex = WebUtils.getSessionMutex(session);  
  19.                 synchronized (mutex) {  
  20.                     return invokeHandlerMethod(request, response, handler);  
  21.                 }  
  22.             }  
  23.         }  
  24.   
  25.         return invokeHandlerMethod(request, response, handler);  
  26.     }  

 首先判断该handler是否有@SessionAttributes的注解,如果有调用WebContentGenerator的checkAndPrepare。重点不在这里,重点在于invokeHandlerMethod方法:

Java代码   收藏代码
  1. protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)  
  2.             throws Exception {  
  3.   
  4.         ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);  
  5.         Method handlerMethod = methodResolver.resolveHandlerMethod(request);  
  6.         ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);  
  7.         ServletWebRequest webRequest = new ServletWebRequest(request, response);  
  8.         ExtendedModelMap implicitModel = new BindingAwareModelMap();  
  9.   
  10.         Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);  
  11.         ModelAndView mav =  
  12.                 methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);  
  13.         methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);  
  14.         return mav;  
  15.     }  

 getMethodResolver方法,我们已经看过了,因为已经创建了一次ServletHandlerMethodResolver对象为该handler,所以这次执行直接从缓存Map中读取了,下面就是为request找到适合的Method,使用resolveHandlerMethod方法,这个代码太长了,我贴上来你们别嫌烦。

Java代码   收藏代码
  1. public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {  
  2.             String lookupPath = urlPathHelper.getLookupPathForRequest(request);  
  3.             Comparator<String> pathComparator = pathMatcher.getPatternComparator(lookupPath);  
  4.             Map<RequestMappingInfo, Method> targetHandlerMethods = new LinkedHashMap<RequestMappingInfo, Method>();  
  5.             Set<String> allowedMethods = new LinkedHashSet<String>(7);  
  6.             String resolvedMethodName = null;  
  7.             for (Method handlerMethod : getHandlerMethods()) {  
  8.                 RequestMappingInfo mappingInfo = new RequestMappingInfo();  
  9.                 RequestMapping mapping = AnnotationUtils.findAnnotation(handlerMethod, RequestMapping.class);  
  10.                 mappingInfo.paths = mapping.value();  
  11.                 if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {  
  12.                     mappingInfo.methods = mapping.method();  
  13.                 }  
  14.                 if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {  
  15.                     mappingInfo.params = mapping.params();  
  16.                 }  
  17.                 if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {  
  18.                     mappingInfo.headers = mapping.headers();  
  19.                 }  
  20.                 boolean match = false;  
  21.                 if (mappingInfo.paths.length > 0) {  
  22.                     List<String> matchedPaths = new ArrayList<String>(mappingInfo.paths.length);  
  23.                     for (String methodLevelPattern : mappingInfo.paths) {  
  24.                         String matchedPattern = getMatchedPattern(methodLevelPattern, lookupPath, request);  
  25.                         if (matchedPattern != null) {  
  26.                             if (mappingInfo.matches(request)) {  
  27.                                 match = true;  
  28.                                 matchedPaths.add(matchedPattern);  
  29.                             }  
  30.                             else {  
  31.                                 for (RequestMethod requestMethod : mappingInfo.methods) {  
  32.                                     allowedMethods.add(requestMethod.toString());  
  33.                                 }  
  34.                                 break;  
  35.                             }  
  36.                         }  
  37.                     }  
  38.                     Collections.sort(matchedPaths, pathComparator);  
  39.                     mappingInfo.matchedPaths = matchedPaths;  
  40.                 }  
  41.                 else {  
  42.                     // No paths specified: parameter match sufficient.  
  43.                     match = mappingInfo.matches(request);  
  44.                     if (match && mappingInfo.methods.length == 0 && mappingInfo.params.length == 0 &&  
  45.                             resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) {  
  46.                         match = false;  
  47.                     }  
  48.                     else {  
  49.                         for (RequestMethod requestMethod : mappingInfo.methods) {  
  50.                             allowedMethods.add(requestMethod.toString());  
  51.                         }  
  52.                     }  
  53.                 }  
  54.                 if (match) {  
  55.                     Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);  
  56.                     if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {  
  57.                         if (methodNameResolver != null && mappingInfo.paths.length == 0) {  
  58.                             if (!oldMappedMethod.getName().equals(handlerMethod.getName())) {  
  59.                                 if (resolvedMethodName == null) {  
  60.                                     resolvedMethodName = methodNameResolver.getHandlerMethodName(request);  
  61.                                 }  
  62.                                 if (!resolvedMethodName.equals(oldMappedMethod.getName())) {  
  63.                                     oldMappedMethod = null;  
  64.                                 }  
  65.                                 if (!resolvedMethodName.equals(handlerMethod.getName())) {  
  66.                                     if (oldMappedMethod != null) {  
  67.                                         targetHandlerMethods.put(mappingInfo, oldMappedMethod);  
  68.                                         oldMappedMethod = null;  
  69.                                     }  
  70.                                     else {  
  71.                                         targetHandlerMethods.remove(mappingInfo);  
  72.                                     }  
  73.                                 }  
  74.                             }  
  75.                         }  
  76.                         if (oldMappedMethod != null) {  
  77.                             throw new IllegalStateException(  
  78.                                     "Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" +  
  79.                                             oldMappedMethod + ", " + handlerMethod +  
  80.                                             "}. If you intend to handle the same path in multiple methods, then factor " +  
  81.                                             "them out into a dedicated handler class with that path mapped at the type level!");  
  82.                         }  
  83.                     }  
  84.                 }  
  85.             }  
  86.             if (!targetHandlerMethods.isEmpty()) {  
  87.                 List<RequestMappingInfo> matches = new ArrayList<RequestMappingInfo>(targetHandlerMethods.keySet());  
  88.                 RequestMappingInfoComparator requestMappingInfoComparator =  
  89.                         new RequestMappingInfoComparator(pathComparator);  
  90.                 Collections.sort(matches, requestMappingInfoComparator);  
  91.                 RequestMappingInfo bestMappingMatch = matches.get(0);  
  92.                 String bestMatchedPath = bestMappingMatch.bestMatchedPath();  
  93.                 if (bestMatchedPath != null) {  
  94.                     extractHandlerMethodUriTemplates(bestMatchedPath, lookupPath, request);  
  95.                 }  
  96.                 return targetHandlerMethods.get(bestMappingMatch);  
  97.             }  
  98.             else {  
  99.                 if (!allowedMethods.isEmpty()) {  
  100.                     throw new HttpRequestMethodNotSupportedException(request.getMethod(),  
  101.                             StringUtils.toStringArray(allowedMethods));  
  102.                 }  
  103.                 else {  
  104.                     throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(),  
  105.                             request.getParameterMap());  
  106.                 }  
  107.             }  
  108.         }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值