SpringMVC 深度解析@RequestMapping(一)

来源:互联网 发布:java instrument 监控 编辑:程序博客网 时间:2024/05/10 17:04

    SpringMVC作为一个MVC框架,有控制层,当我们在浏览器发出了一个请求,SpringMVC是怎么处理请求,而且通过请求找到对应的类的方法?我们今天带着这么问题来解析SpringMVC源代码处理过程。

  我们在实现SpringMVC控制层时,标示了请求路径,并标示请求地址对应的哪个方法,源代码如下:

[java] view plain copy
print?
  1. @Controller  
  2. @RequestMapping(value="/test")  
  3. public class TestController2 {  
  4.     @Autowired  
  5.     private TestService testService;  
  6.       
  7.     @RequestMapping(value="/index")  
  8.     public ModelAndView getIndex(Model model){  
  9.        ModelAndView mv = new ModelAndView();    
  10.           return mv;        
  11.     }  
  12. }  

    注解@RequestMapping是处理方法的映射。我们在类上面注解和方法上注解这样会更加的清晰,我们在类上标示更能清晰的知道这个路径是请求这个类,并在方法上注解比较清楚的是请求哪个方法。例如:http://127.0.0.1:8080/test/index.jhtml。如图所示:

     

  我们先介绍两个比较重要的组件HandlerMapping和HandlerAdapter是@Contoller@RequestMapping注解的处理器, HandlerMapping是处理请求映射的处理器;HandlerAdapter适配器处理器(动态调用方法和处理参数)。我们在XML配置文件中进行配置这两种处理器。代码如下:

  

[java] view plain copy
print?
  1. <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>  
  2. <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">  

 一:我们通过解析SpringMVC处理请求深度解析,并介绍HandlerMapping映射处理器

  我们讲到这个这个XML配置,找到@RequestMapping@Controller并封装成RequestMappingInfo,为后面我们解析处理请求会比较清晰,我在这在补充一下,我们在初始化Bean时我们在上一篇有介绍过,对@RequestMapping注解处理这部分我们没介绍,所以我在这里在补充一下,RequestMappingHandlerMapping间接实现了InitializingBean接口,如图所示:


   RequestMappingHandlerMapping间接实现了InitializingBean接口重写了afterPropertiesSet方法,初始化RequestMappingHandlerMapping时,会调用afterPropertiesSet方法,跟 <bean class="" init-method=""/>属性init-method处理一样。afterPropertiesSet调用了RequestMappingHandlerMappinginitHandlerMethods实现的。处理@RequestMapping的,我们这边来分析一下它是怎么实现的。源代码:

[java] view plain copy
print?
  1. protected void initHandlerMethods() {  
  2.     String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?  
  3.             BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :  
  4.             getApplicationContext().getBeanNamesForType(Object.class));  
  5.     for (String beanName : beanNames) {  
  6.         if (isHandler(getApplicationContext().getType(beanName))){  
  7.             <span style="color:#cc0000;">detectHandlerMethods(beanName);</span>  
  8.         }  
  9.     }  
  10.     handlerMethodsInitialized(getHandlerMethods());  
  11. }  
  12.   
  13. @Override  
  14. protected boolean isHandler(Class<?> beanType) {  
  15.     return ((AnnotationUtils.findAnnotation(beanType, <span style="color:#cc0000;">Controller.class</span>) != null) ||  
  16.             (AnnotationUtils.findAnnotation(beanType, <span style="color:#cc0000;">RequestMapping.class</span>) != null));  
  17. }          

 说明:

   (1isHandler这个方法是判断是否被@Controller@RequestMapping标记

   (2)如果有被@Controller@RequestMapping标记,然后生成RequestMappingInfo实例注册到缓存中,供我们在请求时通过URL能匹配找到。

我们来看怎么生成RequestMappingInfo实例注册到缓存,由detectHandlerMethods这个方法实现的。源代码如下:

[java] view plain copy
print?
  1. protected void detectHandlerMethods(final Object handler) {  
  2.     Class<?> handlerType =  
  3.             (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());  
  4.   
  5.     final Map<Method, T> mappings = new IdentityHashMap<Method, T>();  
  6.     final Class<?> userType = ClassUtils.getUserClass(handlerType);  
  7.     Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {  
  8.         public boolean matches(Method method) {  
  9.             T mapping = <span style="color:#cc0000;">getMappingForMethod(method, userType);</span>  
  10.             if (mapping != null) {  
  11.                 mappings.put(method, mapping);  
  12.                 return true;  
  13.             }  
  14.             else {  
  15.                 return false;  
  16.             }  
  17.         }  
  18.     });  
  19.   
  20.     for (Method method : methods) {  
  21.                        //注册到缓存中  
  22.         <span style="color:#cc0000;">registerHandlerMethod(handler, method, mappings.get(method))</span>;  
  23.     }  
  24. }  
  25.   
  26.      
  27.   @Override  
  28. protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {  
  29.     RequestMappingInfo info = null;  
  30.               //查找该类下注解的@RequestMapping的所有方法  
  31.     RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);  
  32.     if (methodAnnotation != null) {  
  33.         RequestCondition<?> methodCondition = getCustomMethodCondition(method);  
  34.                       //创建RequestMappingInfo  
  35.         info = createRequestMappingInfo(methodAnnotation, methodCondition);  
  36.                       //对类进行查找有没有标示@RequestMapping注解  
  37.         RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);  
  38.         if (typeAnnotation != null) {  
  39.             RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);  
  40.                                //成成RequestMappingInfo。类别和方法级别的RequestMapping注解进行组合  
  41.             info = <span style="color:#ff0000;">createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);</span>  
  42.         }  
  43.     }  
  44.     return info;  
  45. }  
  46.   //设置RequestMappingInfo的属性然后创建<span style="font-family: 宋体;">RequestMappingInfo</span>  
  47.  protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {  
  48.     String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());  
  49.     return new RequestMappingInfo(  
  50.             new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),  
  51.                     this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),  
  52.             new RequestMethodsRequestCondition(annotation.method()),  
  53.             new ParamsRequestCondition(annotation.params()),  
  54.             new HeadersRequestCondition(annotation.headers()),  
  55.             new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),  
  56.             new ProducesRequestCondition(annotation.produces(), annotation.headers(), getContentNegotiationManager()),  
  57.             customCondition);  
  58. }  

  

  当我们在浏览器发送了http://127.0.0.1:8080/test/index.jhtml这样的请求,SpringMVC注册在web.xml中的前端转发器DispatcherServlet接收,在这个之前,我们对initHandlerMappingsinitHandlerAdapters初始化,这个在上一篇有介绍过。接下来我们开始分析DispatcherServlet处理请求。

   DispatcherServlet是间接的继承了HttpSevlet,由父类FrameworkServlet实现了doPost和doGet方法,然后在调用子类,DispatcherServletdoDispatch方法处理请求,实现了设计原则接口隔离原则。请求的包含了一些头部的信息等,如图所示:

  



 doDispatch方法的源代码如下:

  
[java] view plain copy
print?
  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  2.            HttpServletRequest processedRequest = request;  
  3.                               //判断是否是文件流请求  
  4.             processedRequest = checkMultipart(request);  
  5.                                //获取了映射处理器,里面是通过请求的URL获取对应的类并获取实例化的Bean,包装成HandlerMethod  
  6.             mappedHandler = <span style="color:#990000;">getHandler(processedRequest, false);</span>  
  7.             if (mappedHandler == null || mappedHandler.getHandler() == null) {  
  8.                 noHandlerFound(processedRequest, response);  
  9.                 return;  
  10.             }  
  11.                               //获取HandlerAdapter  
  12.             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
  13.             String method = request.getMethod();  
  14.             boolean isGet = "GET".equals(method);  
  15.             if (isGet || "HEAD".equals(method)) {  
  16.                 long lastModified = ha.getLastModified(request, mappedHandler.getHandler());  
  17.                 if (logger.isDebugEnabled()) {  
  18.                     String requestUri = urlPathHelper.getRequestUri(request);  
  19.                     logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);  
  20.                 }  
  21.                 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {  
  22.                     return;  
  23.                 }  
  24.             }  
  25.             try {  
  26.                mv = <span style="color:#cc0000;">ha.handle(processedRequest, response, mappedHandler.getHandler());</span>  
  27.             }  
  28.             applyDefaultViewName(request, mv);  
  29.             mappedHandler.applyPostHandle(processedRequest, response, mv);        
  30.             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  
  31.     }  
  32. }  
  33.   
  34.   
  35. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
  36.     for (HandlerMapping hm : this.handlerMappings) {  
  37.         HandlerExecutionChain handler = <span style="color:#990000;">hm.getHandler(request);</span>  
  38.         if (handler != null) {  
  39.             return handler;  
  40.         }  
  41.     }  
  42.     return null;  
  43. }  

说明:

  (1Spring3.1开始的版本,建议使用RequestMappingHandlerMappingRequestMappingHandlerAdapter,所以我们在XML配置了这个Bean组件。 List<HandlerMapping> handlerMappings里面存放的是映射处理器,spring内置了很多映射处理器,例如SimpleUrlHandlerMappingBeanNameUrlHandlerMapping,如图所示:

   

  (2HandlerExecutionChain包含了处理该请求的处理器,还包含一系列可以拦截请求的拦截器。

   RequestMappingHandlerMapping也是继承了AbstractHandlerMappinggetHandler具体实现是由AbstractHandlerMapping来实现的,源代码如下:

    
[java] view plain copy
print?
  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.         // Bean name or resolved handler?  
  10.         if (handler instanceof String) {  
  11.             String handlerName = (String) handler;  
  12.             handler = getApplicationContext().getBean(handlerName);  
  13.         }  
  14.         return <span style="color:#990000;">getHandlerExecutionChain(handler, request);</span>  
  15.     }  

说明:

     (1getHandlerInternal方法是处理映射的,获取request获取了请求路径,然后找到对应的RequestMappingInfo获取了Controller类,并找到了对应的方法。

     (2HandlerExecutionChain带了一系列的interceptors

     

第一:getHandlerInternal方法是通过URL找到对应的处理映射的,并找到对应的Bean实例,我们通过源代码分析是怎么处理的?

  getHandlerInternal方法源代码如下:

[java] view plain copy
print?
  1. protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {  
  2.                //通过UrlPathHelper获取request获取了请求路径  例如:test/index.jhtml  
  3.         String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);  
  4.         if (logger.isDebugEnabled()) {  
  5.             logger.debug("Looking up handler method for path " + lookupPath);  
  6.         }  
  7.                 //  
  8.         HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);  
  9.         if (logger.isDebugEnabled()) {  
  10.             if (handlerMethod != null) {  
  11.                 logger.debug("Returning handler method [" + handlerMethod + "]");  
  12.             }  
  13.             else {  
  14.                 logger.debug("Did not find handler method for [" + lookupPath + "]");  
  15.             }  
  16.         }  
  17.                //返回对handlerMethod 进行设置已经初始化Bean并设置属性的handlerMethod   
  18.         return (handlerMethod != null ? handlerMethod.<span style="color:#990000;">createWithResolvedBean</span>() : null);  
  19.     }  
  20.      //对bean进行初始化  
  21.    public HandlerMethod createWithResolvedBean() {  
  22.         Object handler = this.bean;  
  23.         if (this.bean instanceof String) {  
  24.             String beanName = (String) this.bean;  
  25.                          //获取对应的Bean  
  26.             handler = this.beanFactory.getBean(beanName);  
  27.         }  
  28.         return new HandlerMethod(this, handler);  
  29.     }  
  30.  //设置bean、还有beanFactory 、method、parameters 等属性  
  31. private HandlerMethod(HandlerMethod handlerMethod, Object handler) {  
  32.         Assert.notNull(handlerMethod, "HandlerMethod is required");  
  33.         Assert.notNull(handler, "Handler object is required");  
  34.         this.bean = handler;  
  35.         this.beanFactory = handlerMethod.beanFactory;  
  36.         this.method = handlerMethod.method;  
  37.         this.bridgedMethod = handlerMethod.bridgedMethod;  
  38.         this.parameters = handlerMethod.parameters;  
  39.     }  

 说明:

1UrlPathHelper是分析请求的URLLookupPathForRequest(request)这个方法中有alwaysUseFullPath默认是false使用相对路径。

2lookupHandlerMethod通过URL查看映射到哪个方法和类,MultiValueMap<String, T> urlMap 存放的keyurlvalueRequestMappingInfo信息(params等),通过lookupPath查找对应的RequestMappingInfo,然后通过RequestMappingInfoMap<T, HandlerMethod> handlerMethods查找对应的HandlerMethod,并返回。MultiValueMap<String, T> urlMap这个缓存中是我们在最开始时有介绍,处理@RequestMapping@Controll 并封装成RequestMappingInfo并放到缓存,如图所示:



如果查找对应的方法时,放到Match,里面有包含的HandlerMethod,如图所示:

  

   然后通过HandlerMethodcreateWithResolvedBean方法实现了通过beanName获取已经初始化的 Bean。然后重新初始化HandlerMethod对象,并设置bean、还有beanFactory methodparameters 等属性。


第二:HandlerExecutionChain 包含了一系列拦截器。会在调用Controller类对应方法之前、处理完方法还没返回视图、返回视图之后,这些动态加以拦截。

HandlerExecutionChain这个类属性很很多添加一系列的拦截器,源代码如下:

[java] view plain copy
print?
  1. public class HandlerExecutionChain {  
  2.     private HandlerInterceptor[] interceptors;  
  3.     private List<HandlerInterceptor> interceptorList;  
  4. }  

getHandler具体实现是由AbstractHandlerMapping中,在这个 方法中实现了加入了拦截器,我们在看一下我们怎么加入拦截器,源代码如下:

[java] view plain copy
print?
  1. protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {  
  2.         HandlerExecutionChain chain =  
  3.             (handler instanceof HandlerExecutionChain) ?  
  4.                 (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);  
  5.                 //添加拦截器  
  6.         <span style="color:#990000;">chain.addInterceptors(getAdaptedInterceptors());</span>  
  7.         String lookupPath = urlPathHelper.getLookupPathForRequest(request);  
  8.         for (MappedInterceptor mappedInterceptor : mappedInterceptors) {  
  9.             if (mappedInterceptor.matches(lookupPath, pathMatcher)) {  
  10.                 chain.addInterceptor(mappedInterceptor.getInterceptor());  
  11.             }  
  12.         }  
  13.   
  14.         return chain;  
  15.     }  
  16. }  

说明:

       我们在XML里没配置自己的拦截器,所以这边都是为空的。

       HandlerInterceptor拦截器接口,里面有三个方法:

     (1preHandle方法:请求处理之前执行的这个方法,在Controller方法调用之前调用。例如:调用之前判断是否有登陆。

     (2postHandle方法: 请求进行处理之后,在Controller 方法调用之后执行,会在DispatcherServlet 调用ModelView视图之前调用。

     (3afterCompletion方法:是在DispatcherServlet 调用ModelView视图之后调用。


     既然HandlerInterceptor是接口,我们可以自己实现一个类实现这个接口,这样我们就自己定义自己的拦截器,然后加到SpringMVC拦截中?当然可以。

    我们自己定义了一个类实现了HandlerInterceptor 接口,例如:public class myInterceptor implements HandlerInterceptor 然后重写了这个3个方法。我们在XML配置这个类,把自己定义的拦截器加到SpringMVC拦截中。在配置文件加入了

[java] view plain copy
print?
  1. <span style="color:#990000;"><beans xmlns:mvc="http://www.springframework.org/schema/mvc"  
  2.      xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"></span>  
  3.  <mvc:interceptors>  
  4. <mvc:interceptor>  
  5.   <!--拦截哪个包下的类例如:-->  
  6.       <mvc:mapping path="/test/*"/>  
  7.       <bean class="test.myInterceptor "></bean>  
  8.     </mvc:interceptor>  
  9.   </mvc:interceptors>  

这样就把我们定义好的拦截器加到SpringMVC的拦截器中。


到这里,我们对HandlerMapping映射处理器介绍完了,


二:我们通过解析SpringMVC处理请求深度解析,并介绍HandlerAdapter适配器处理器(动态调用方法和处理参数)

  

HandlerAdapter处理HandlerMethod映射并返回了视图和数据的对象。getHandlerAdapter获取了我们在配置文件的如图所示:

 

父类AbstractHandlerMethodAdapter实现的,我们先看一下 继承关系,这种开封闭原则。如图所示:



我们来看一下这个handle(processedRequest, response, mappedHandler.getHandler());动态的调用方法和处理参数的具体实现。源代码如下:

[java] view plain copy
print?
  1. public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  2.             throws Exception {  
  3.         return handleInternal(request, response, (HandlerMethod) handler);  
  4.     }  
  5.   
  6.   @Override  
  7.     protected final ModelAndView handleInternal(HttpServletRequest request,  
  8.             HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {  
  9.   
  10.         if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {  
  11.             // Always prevent caching in case of session attribute management.  
  12.             checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);  
  13.         }  
  14.         else {  
  15.             // Uses configured default cacheSeconds setting.  
  16.             checkAndPrepare(request, response, true);  
  17.         }  
  18.   
  19.         // Execute invokeHandlerMethod in synchronized block if required.  
  20.         if (this.synchronizeOnSession) {  
  21.             HttpSession session = request.getSession(false);  
  22.             if (session != null) {  
  23.                 Object mutex = WebUtils.getSessionMutex(session);、  
  24.                 synchronized (mutex) {  
  25.                     return invokeHandleMethod(request, response, handlerMethod);  
  26.                 }  
  27.             }  
  28.         }  
  29.          //动态的调用方法和处理参数  
  30.         return invokeHandleMethod(request, response, handlerMethod);  
  31.     }  

说明:

  通过HandlerAdapter动态的调用方法和处理参数,调用方法。我们这边具体怎么动态调用方法和处理参数,并返回视图,等下一章在具体的介绍,这里涉及也比较多。


总结:

  (1) 当我们在浏览器发送了http://127.0.0.1:8080/test/index.jhtml这样的请求,SpringMVC注册在web.xml中的前端转发器DispatcherServlet接收时。

  (2)通过URL查看映射到哪个方法和类,MultiValueMap<String, T> urlMap 存放的keyurlvalueRequestMappingInfo信息(params等),RequestMappingInfo获取了Controller类,并找到了对应的方法。并包装返回了HandlerMethod。

  (3)通过BeanName,到工厂获取已经初始化的Bean,然后重新初始化HandlerMethod对象,并设置bean、还有beanFactory methodparameters 等属性。

   (4)对HandlerExecutionChain添加拦截器和handler然后返回HandlerExecutionChain

  (5)HandlerAdapter对HandlerExecutionChain进行动态的调用方法会返回ModelAndView。



浏览器请求已经获取到了,也找到了对应的类和方法,那怎么动态的请求方法和处理参数等,执行完方法并返回ModelAndView等?,带着这些问题我们继续前进。



阅读全文
0 0
原创粉丝点击