Spring2.5源码解读 之 基于annotation的Controller实现原理分析(1)

来源:互联网 发布:怎么在淘宝上找代理 编辑:程序博客网 时间:2024/05/17 01:06
网上已有许多关于Spring源码解读的文章,但对于SpringMVC中基于annotation的Controller这一块,目前还没发现有相关源码解读的文章,这几天,一直在研究SpringMVC,小有所获。这里,对Spring中基于annotation的Controller的实现原理作下简单分析,作为以后学习的参考资料,如果有人也对此感兴趣,也欢迎一起研究,交流心得。 

快速开始SpringMVC 

1、导入核心JAR,有两种导入方式 
   * 导入全部JAR:spring.jar 
   * 导入最小JAR:spring-core、spring-beans、spring-context、spring-web、spring-webmvc 
   第三方依赖JAR:commons-logging.jar 
2、配置核心servlet: 

Java代码  收藏代码
  1. <servlet>  
  2.     <servlet-name>SpringServlet</servlet-name>  
  3.     <servlet-class>  
  4.         org.springframework.web.servlet.DispatcherServlet  
  5.     </servlet-class>  
  6.     <load-on-startup>1</load-on-startup>  
  7. </servlet>  
  8. <servlet-mapping>  
  9.     <servlet-name>SpringServlet</servlet-name>  
  10.     <url-pattern>*.html</url-pattern>  
  11. </servlet-mapping>  


* 3、配置包扫描列表 
  在[servlet-name]-servlet中配置: 

Java代码  收藏代码
  1. <context:component-scan base-package="com.spring.test" />  


将所有基于annotation的handler放在test包下即可。 

  相当简洁的配置,体现出了的Sping的强大、灵活,不过估计不会有人这样用Spring,呵呵 

源码分析之旅:
 

SpringMVC的核心是DispatcherServlet,网上已经有对该类的简单分析,见后面参考资料。对于handler扫描、初始化映射关系等,以后有时间再详细解读,这里只是稍微提一下: 
DispatcherServlet的初始化: 
Java代码  收藏代码
  1. protected void initStrategies(ApplicationContext context) {  
  2.     //方法入参为ApplicationContext,可证明在DispatcherServlet初始化之前,IoC容器已经开始工作了  
  3.     initMultipartResolver(context);  
  4.     initLocaleResolver(context);  
  5.     initThemeResolver(context);  
  6.     initHandlerMappings(context);  
  7.     initHandlerAdapters(context);  
  8.     initHandlerExceptionResolvers(context);  
  9.     initRequestToViewNameTranslator(context);  
  10.     initViewResolvers(context);  
  11. }  


该初始方法主要完成两件工作: 
* 1、将容器中配置(在applicationContext.xml中定义)的HandlerMapping、LocaleResolver等等初始化 
* 2、如果容器中未配置,则使用默认策略,该默认策略定义在DispatcherServlet.properties文件中 

这其中有几个比较重要的组件(也称管道)需要初始化,包括HandlerMapping、HandlerAdapter、ViewResolver。 
HandlerMapping 

我们从DispatcherServlet的doService方法入手: 

Java代码  收藏代码
  1. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  2.     ...  
  3.     //将WebApplicationContext放在了request中  
  4.     request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());  
  5.     request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);  
  6.     request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);  
  7.     request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());  
  8.   
  9.     try {  
  10.         doDispatch(request, response);  
  11.     }  
  12.     ...  
  13. }  


可以看出,对于请求的处理实际上是由doDispatch()完成的,这里只对与annotation相关的部分进行分析: 

Java代码  收藏代码
  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  2.   
  3.     ...       
  4.     try {  
  5.         ModelAndView mv = null;  
  6.         try {  
  7.             //查找匹配的handler  
  8.             mappedHandler = getHandler(processedRequest, false);  
  9.             if (mappedHandler == null || mappedHandler.getHandler() == null) {  
  10.                 //如果没有,写入404错误  
  11.                 noHandlerFound(processedRequest, response);  
  12.                 return;  
  13.             }  
  14.   
  15.             //调用handler的方法  
  16.             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
  17.             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
  18.     ...  
  19.   
  20. }  


再来看查找handler的过程: 

Java代码  收藏代码
  1. protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {  
  2.     ...  
  3.     //初始时handlerMappings中有两个:  
  4.     //1、BeanNameUrlHandlerMapping:根据bean的名字查找匹配的handler,意味我们可以在容器中将bean名以url定义,如"/order/*"  
  5.     //2、DefaultAnnotationHandlerMapping:根据annotation定义查找  
  6.     //每个handlerMapping中都维持有一个url-handler的HashMap,该列表在生成在初始化时完成  
  7.     Iterator it = this.handlerMappings.iterator();  
  8.     while (it.hasNext()) {  
  9.         HandlerMapping hm = (HandlerMapping) it.next();  
  10.         ...  
  11.         handler = hm.getHandler(request);//实际的匹配过程交由handlerMapping完成  
  12.         ...  
  13.     }  
  14.     return null;  
  15. }  


实际查找handler的过程由DefaultAnnotationHandlerMapping类完成。从它的继承层次可以看出,匹配的主要工作都由其父类完成了。在父类中定义了算法的骨架,具体的处理交由子类完成,这是Templet设计模式的典型应用。[[BR]] 

先看父类AbstractHandlerMapping中定义的算法骨架: 

Java代码  收藏代码
  1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
  2.     Object handler = getHandlerInternal(request);//交由子类实现  
  3.     if (handler == null) {  
  4.         handler = getDefaultHandler();  
  5.     }  
  6.     if (handler == null) {  
  7.         return null;  
  8.     }  
  9.   
  10.     if (handler instanceof String) {    //如果handler是String,即完整类名,在容器中定义  
  11.         String handlerName = (String) handler;  
  12.         handler = getApplicationContext().getBean(handlerName);//从IoC中直接获取  
  13.     }  
  14.     return getHandlerExecutionChain(handler, request);  
  15. }  


AbstractHandlerMapping的子类AbstractUrlHandlerMapping中getHandlerInternal的定义: 
Java代码  收藏代码
  1. protected Object getHandlerInternal(HttpServletRequest request) throws Exception {  
  2.   
  3.     Object handler = lookupHandler(lookupPath, request);  
  4.     if (handler == null) {  
  5.         ...  
  6.     }  
  7.     return handler;  
  8. }  
  9.   
  10. protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {  
  11.     // 直接匹配:  
  12.     Object handler = this.handlerMap.get(urlPath);  
  13.     if (handler != null) {  
  14.         validateHandler(handler, request);//@RequestMapping的其它属性匹配交由子类完成,如method和param的匹配  
  15.         return buildPathExposingHandler(handler, urlPath);  
  16.     }  
  17.     // 正则表达式匹配:  
  18.     String bestPathMatch = null;  
  19.     for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {  
  20.         String registeredPath = (String) it.next();  
  21.         if (getPathMatcher().match(registeredPath, urlPath) &&  
  22.                 (bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {  
  23.             bestPathMatch = registeredPath;//可以看出,匹配原则是按照url更长则更匹配  
  24.         }  
  25.     }  
  26.     if (bestPathMatch != null) {  
  27.         handler = this.handlerMap.get(bestPathMatch);  
  28.         validateHandler(handler, request);//@RequestMapping的其它属性匹配交由子类完成,如method和param的匹配  
  29.         String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPathMatch, urlPath);  
  30.         return buildPathExposingHandler(handler, pathWithinMapping);  
  31.     }  
  32.     // No handler found...  
  33.     return null;  
  34. }  


子类DefaultAnnotationHandlerMapping中@RequestMapping的匹配过程: 

Java代码  收藏代码
  1. protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {  
  2.     RequestMapping mapping = this.cachedMappings.get(handler.getClass());  
  3.     if (mapping == null) {  
  4.         mapping = AnnotationUtils.findAnnotation(handler.getClass(), RequestMapping.class);  
  5.     }  
  6.     if (mapping != null) {  
  7.         validateMapping(mapping, request);//具体的匹配在validateMapping中完成  
  8.     }  
  9. }  
  10.   
  11. protected void validateMapping(RequestMapping mapping, HttpServletRequest request) throws Exception {  
  12.     RequestMethod[] mappedMethods = mapping.method();  
  13.   
  14.     //请求方法是否匹配?  
  15.     if (!ServletAnnotationMappingUtils.checkRequestMethod(mappedMethods, request)) {  
  16.         String[] supportedMethods = new String[mappedMethods.length];  
  17.         for (int i = 0; i < mappedMethods.length; i++) {  
  18.             supportedMethods[i] = mappedMethods[i].name();  
  19.         }  
  20.         throw new HttpRequestMethodNotSupportedException(request.getMethod(), supportedMethods);//直接就抛异常了?似乎不妥,为什么不尝试下一个比较匹配的那个URL呢?也有可能是父类的算法定义有问题  
  21.     }  
  22.   
  23.     //请求参数是否匹配?  
  24.     String[] mappedParams = mapping.params();  
  25.     if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) {  
  26.         throw new ServletException("Parameter conditions {" +  
  27.                 StringUtils.arrayToDelimitedString(mappedParams, ", ") +  
  28.                 "} not met for request parameters: " + request.getParameterMap());  
  29.     }  
  30. }  



请求方法及参数的匹配过程由ServletAnnotationMappingUtils类的静态方法完成,逻辑比较简单: 

Java代码  收藏代码
  1. public static boolean checkRequestMethod(RequestMethod[] methods, HttpServletRequest request) {  
  2.     if (!ObjectUtils.isEmpty(methods)) {  
  3.         boolean match = false;  
  4.         for (RequestMethod method : methods) {  
  5.             if (method.name().equals(request.getMethod())) {  
  6.                 match = true;  
  7.             }  
  8.         }  
  9.         if (!match) {  
  10.             return false;  
  11.         }  
  12.     }  
  13.     return true;  
  14. }  
  15.   
  16. public static boolean checkParameters(String[] params, HttpServletRequest request) {  
  17.     if (!ObjectUtils.isEmpty(params)) {  
  18.         for (String param : params) {  
  19.             int separator = param.indexOf('=');  
  20.             if (separator == -1) {  
  21.                 if (param.startsWith("!")) {  
  22.                     if (WebUtils.hasSubmitParameter(request, param.substring(1))) {  
  23.                         return false;  
  24.                     }  
  25.                 }  
  26.                 else if (!WebUtils.hasSubmitParameter(request, param)) {  
  27.                     return false;  
  28.                 }  
  29.             }  
  30.             else {  
  31.                 String key = param.substring(0, separator);  
  32.                 String value = param.substring(separator + 1);  
  33.                 if (!value.equals(request.getParameter(key))) {  
  34.                     return false;  
  35.                 }  
  36.             }  
  37.         }  
  38.     }  
  39.     return true;  
  40. }  



至此,handler的匹配过程结束,bean的实例作为匹配的handler返回。可以看出,匹配过程并未深入到方法一级,如果类级别和方法级别都定义了url,在这一层次会忽略方法级别的。其实,spring也不推荐在类级别和方法级别同时定义url。[[BR]] 
再回到DispatcherServlet中,找到匹配handler后,下一步就要去调用handler,调用的方式有许多,spring抽像出了一个接口HandlerAdapter,接口的定义: 

Java代码  收藏代码
  1. public interface HandlerAdapter {  
  2.   
  3.     boolean supports(Object handler); //是否支持此种类型的handler  
  4.   
  5.     ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; //具体的处理过程  
  6.   
  7.     long getLastModified(HttpServletRequest request, Object handler);  
  8. }  


DispatcherServlet中寻找合适的HandlerAdapter的过程: 

Java代码  收藏代码
  1. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {  
  2.   
  3.     /* 
  4.     spring默认提供四个HandlerAdapter: 
  5.         HttpRequestHandlerAdapter:处理HttpRequestHandler接口的实例 
  6.         SimpleControllerHandlerAdapter:处理Controller接口的实例 
  7.         ThrowawayControllerHandlerAdapter:已经过时 
  8.         AnnotationMethodHandlerAdapter:处理annotation定义的实例 
  9.     */  
  10.     Iterator it = this.handlerAdapters.iterator();  
  11.     while (it.hasNext()) {  
  12.         HandlerAdapter ha = (HandlerAdapter) it.next();  
  13.         if (logger.isDebugEnabled()) {  
  14.             logger.debug("Testing handler adapter [" + ha + "]");  
  15.         }  
  16.         if (ha.supports(handler)) {  
  17.             return ha;  
  18.         }  
  19.     }  
  20.     ...  
  21. }  


基于annotation的handler则是由AnnotationMethodHandlerAdapter进行处理,来看AnnotationMethodHandlerAdapter中相关处理代码: 

Java代码  收藏代码
  1. //是否支持此类型的handler?  
  2. public boolean supports(Object handler) {  
  3.     return getMethodResolver(handler).hasHandlerMethods();  
  4. }  
  5.   
  6. private ServletHandlerMethodResolver getMethodResolver(Object handler) {  
  7.     Class handlerClass = ClassUtils.getUserClass(handler);  
  8.     ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);  
  9.     if (resolver == null) {  
  10.         resolver = new ServletHandlerMethodResolver(handlerClass);//在父类的构造方法中完成handler的解析  
  11.         this.methodResolverCache.put(handlerClass, resolver);//缓存起来,方法调用时有用  
  12.     }  
  13.     return resolver;  
  14. }  
  15.   
  16. public final boolean hasHandlerMethods() {  
  17.     return !this.handlerMethods.isEmpty();//非常简单的判断,如果该类中的方法标记有@RequestMapping就返回true,也意味着它支持此种类型的handler  
  18. }  


好了,到这里,handler已确定,由谁去处理handler也已确定,剩下的工作就是如何调用了。来看具体的调用代码: 

Java代码  收藏代码
  1. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  2.         throws Exception {  
  3.   
  4.     ...  
  5.     if (this.synchronizeOnSession) {  
  6.         HttpSession session = request.getSession(false);  
  7.         if (session != null) {  
  8.             Object mutex = WebUtils.getSessionMutex(session);  
  9.             synchronized (mutex) {  
  10.                 return invokeHandlerMethod(request, response, handler);  
  11.             }  
  12.         }  
  13.     }  
  14.     return invokeHandlerMethod(request, response, handler);  
  15. }  
  16.   
  17. protected ModelAndView invokeHandlerMethod(  
  18.         HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
  19.   
  20.     try {  
  21.         ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);//缓存中已有  
  22.         Method handlerMethod = methodResolver.resolveHandlerMethod(request);//确定具体该调用哪个方法  
  23.         ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);  
  24.         ServletWebRequest webRequest = new ServletWebRequest(request, response);  
  25.         ExtendedModelMap implicitModel = new ExtendedModelMap();//方法入参ModelMap的原型  
  26.   
  27.         Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);//调用方法  
  28.         ModelAndView mav =  
  29.                 methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);//构建ModelAndView  
  30.         methodInvoker.updateModelAttributes(  
  31.                 handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);//处理ModelMap中的值  
  32.         return mav;  
  33.     }  
  34.     catch (NoSuchRequestHandlingMethodException ex) {  
  35.         return handleNoSuchRequestHandlingMethod(ex, request, response);  
  36.     }  
  37. }  


其中,确定具体调用哪个方法这个过程比较复杂,由ServletHandlerMethodResolver完成。 

Java代码  收藏代码
  1. public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {  
  2.     ...  
  3.     for (Method handlerMethod : getHandlerMethods()) {  
  4.         RequestMappingInfo mappingInfo = new RequestMappingInfo();  
  5.         RequestMapping mapping = AnnotationUtils.findAnnotation(handlerMethod, RequestMapping.class);  
  6.         mappingInfo.paths = mapping.value();  
  7.         //如果类级别没有定义@RequestMapping,则使用方法级别定义的  
  8.         if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {  
  9.             mappingInfo.methods = mapping.method();  
  10.         }  
  11.         if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {  
  12.             mappingInfo.params = mapping.params();  
  13.         }  
  14.         boolean match = false;  
  15.         if (mappingInfo.paths.length > 0) {//先检查url是否匹配  
  16.             for (String mappedPath : mappingInfo.paths) {  
  17.                 if (isPathMatch(mappedPath, lookupPath)) {  
  18.                     if (checkParameters(mappingInfo, request)) {  
  19.                         match = true;  
  20.                         targetPathMatches.put(mappingInfo, mappedPath);  
  21.                     }  
  22.                     else {  
  23.                         break;  
  24.                     }  
  25.                 }  
  26.             }  
  27.         }  
  28.         else {  
  29.             //如果没有定义url,则只需检查其它项是否匹配,如param、method  
  30.             match = checkParameters(mappingInfo, request);  
  31.             if (match && mappingInfo.methods.length == 0 && mappingInfo.params.length == 0 &&  
  32.                     resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) {  
  33.                 match = false;  
  34.             }  
  35.         }//如果已经有匹配了,还须检查是否有歧义的调用?  
  36.         if (match) {  
  37.             Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);  
  38.             if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {  
  39.                 if (methodNameResolver != null && mappingInfo.paths.length == 0) {  
  40.                     if (!oldMappedMethod.getName().equals(handlerMethod.getName())) {  
  41.                         if (resolvedMethodName == null) {  
  42.                             resolvedMethodName = methodNameResolver.getHandlerMethodName(request);  
  43.                         }  
  44.                         if (!resolvedMethodName.equals(oldMappedMethod.getName())) {  
  45.                             oldMappedMethod = null;  
  46.                         }  
  47.                         if (!resolvedMethodName.equals(handlerMethod.getName())) {  
  48.                             if (oldMappedMethod != null) {  
  49.                                 targetHandlerMethods.put(mappingInfo, oldMappedMethod);  
  50.                                 oldMappedMethod = null;  
  51.                             }  
  52.                             else {  
  53.                                 targetHandlerMethods.remove(mappingInfo);  
  54.                             }  
  55.                         }  
  56.                     }  
  57.                 }  
  58.                 if (oldMappedMethod != null) {  
  59.                     ...  
  60.                 }  
  61.             }  
  62.         }  
  63.     }//如果只有一个符合条件的方法  
  64.     if (targetHandlerMethods.size() == 1) {  
  65.         return targetHandlerMethods.values().iterator().next();  
  66.     }//如果有多个,还需确定最合适的方法  
  67.     else if (!targetHandlerMethods.isEmpty()) {  
  68.         RequestMappingInfo bestMappingMatch = null;  
  69.         String bestPathMatch = null;  
  70.         for (RequestMappingInfo mapping : targetHandlerMethods.keySet()) {  
  71.             String mappedPath = targetPathMatches.get(mapping);  
  72.             if (bestMappingMatch == null) {  
  73.                 bestMappingMatch = mapping;  
  74.                 bestPathMatch = mappedPath;  
  75.             }  
  76.             else {  
  77.                 if (isBetterPathMatch(mappedPath, bestPathMatch, lookupPath) ||  
  78.                         (!isBetterPathMatch(bestPathMatch, mappedPath, lookupPath) &&  
  79.                                 (isBetterMethodMatch(mapping, bestMappingMatch) ||  
  80.                                 (!isBetterMethodMatch(bestMappingMatch, mapping) &&  
  81.                                         isBetterParamMatch(mapping, bestMappingMatch))))) {  
  82.                     bestMappingMatch = mapping;  
  83.                     bestPathMatch = mappedPath;  
  84.                 }  
  85.             }  
  86.         }  
  87.         return targetHandlerMethods.get(bestMappingMatch);  
  88.     }  
  89.     else {  
  90.         throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(), request.getParameterMap());  
  91.     }  
  92. }  



完成方法调用,由父类HandlerMethodInvoker完成: 

Java代码  收藏代码
  1. public final Object invokeHandlerMethod(  
  2.         Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel)  
  3.         throws Exception {  
  4.     ...  
  5.     Object[] args = resolveHandlerArguments(handlerMethod, handler, webRequest, implicitModel);//参数解析  
  6.   
  7.     return doInvokeMethod(handlerMethod, handler, args);  
  8. }  


至此,handler的方法调用至此结束。我们的分析也告一段落。但对于spring的处理远还未结束,后面还有许多的收尾工作,至于是什么,以后再说,呵呵 
阅读全文
0 0