spring mvc工作机制

1. 配置

springMVC的核心是DispatcherServlet,它实现了HttpServlet类,在web.xml中引入配置:

 
    contextConfigLocation
    classpath*:springMVC.xml
  

  
    dispatcherServlet
    org.springframework.web.servlet.DispatcherServlet

  
  
    dispatcherServlet
    /
  

Servlet容器tomcat在初始化DispatcherServlet时,会创建一个新的applicationContext(默认是XmlWebApplicationContext)去加载参数 contextConfigLocation指定的spring xml配置文件。

在基于注解的springMVC配置中,需要在spring配置文件中引入如下配置:

   
   

注解默认会在spring上下文中引入以下这些bean:

  • RequestMappingHandlerMapping

  • RequestMappingHandlerAdapter
    两者配合处理基于注解的(@Controller@RequestMapping等等)spring mvc使用

    RequestMappingHandlerMapping会读取spring上下文中所有使用 @Controller或者@RequestMapping注解的bean并解析他的方法作为handler

  • BeanNameUrlHandlerMapping
    处理Struct风格的url,即将url作为bean name获取到bean,这种时候一个url请求对应一个bean(即你的controller类),这个时候你的controller类可以实现AbstractController抽象类

    BeanNameUrlHandlerMapping会读取spring上下文中所有名字以'/'开始的bean作为handler

  • SimpleControllerHandlerAdapter
    配合BeanNameUrlHandlerMapping一起使用。

2. SpringMVC工作流程

如下图:

spring mvc工作机制_第1张图片
springMVC流程.png
  1. DispatcherServlet作为前端控制器,请求会先经过它,进入到其doDispatch方法中,DispatcherServlet可以有多个HandlerMapping和HandlerAdapter,DispatcherServlet根据按序排列它们。

  2. DispatcherServlet会遍历所有HandlerMapping,并但返回第一个能够处理当前request的HandlerMapping对像,并调用其getHandler方法返回HandlerExecutionChain

  3. HandlerExecutionChain由一个handler和若干HandlerInterceptor组成,其中handler其实就是用户的业务逻辑实现,由创建这个HandlerExecutionChain的HandlerMapping设置,比如RequestMappingHandlerMapping的handler其实就是Contorller的某个映射当前request url的method的封装。

  4. DispatcherServlet调用所有HandlerInterceptor的preHandle处理request

  5. HandlerAdaptor是HandlerExecutionChain的handler的适配器,对于有多个HandlerAdaptor的情况,返回第一个能够适配handler的适配器。

    不同的适配器完成的功能不一样,一般都是在激活handler进入用户业务逻辑前做一些预处理,然后进入用户业务逻辑,最终返回ModelAnView对象。RequestMappingHandlerAdapter这种比较复杂的会处理@PathVariable,@ModelAttribute这个注解完成参数handler的参数的设置,然后调用handler的方法进入用户业务逻辑,

  6. 接下来调用HandlerExecutionChain中所有interceptor的postHandle

  7. 将ModelAndView交给ViewResolver处理

3. HandlerMapping

接口HandlerMapping只有一个接口方法:

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

getHandler里一般是判断能不能处理这个request,不能返回null,否则返回HandlerExecutionChain,类成员如下:

class HandlerExecutionChain{
    // handler一般是用户业务逻辑的封装
    private final Object handler;
    // 拦截器
    private HandlerInterceptor[] interceptors;
     ...
    // 上面第2节中步骤 4,它依次调用interceptors中每一个interceptor的preHandle
    // 只要有一个interceptor的preHandle返回false就不再走下去,直接返回false,本次对request处理结束,不再继续第2节中其他后续流程
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) 
    // 上面第2节中步骤 6
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
}

-------拦截器HandlerInterceptor----------
public interface HandlerInterceptor {
    // 返回false就不会调用后续拦截器,意味着本次request处理结束
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;
    // preHandler返回false,或者出现异常,或者正常返回时调用
    void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;

3.1 HandlerMapping的一些实现类

1. RequestMappingHandlerMapping
它是用来处理使用了@Controller或者@RequestMapping注解的用户controller层面的业务逻辑类的。
像下面这样:

@Controller
@RequestMapping("/api/echo")
public class EchoTime {
    @RequestMapping(value = "/currentDate", method = RequestMethod.GET, produces = "application/json")
    public Date currentDate(){
        Date d = new Date();
        System.out.println("in currentDate");
        return d;
    }
}

RequestMappingHandlerMapping获取当前applicationContext中所有使用了@Controller或者@RequestMapping注解的bean,然后解析这些bean中使用了@RequestMapping注解的方法,将这些方法封装成HandlerMethod,然后建立url到这个HandlerMethod的映射。

  • RequestMappingHandlerMapping # getHandler返回的就是包装了HandlerMethod对象HandlerExecutionChain
  • RequestMappingHandlerMapping返回的HandlerExecutionChain中interceptor处理用户在spring的配置文件中显示指定以外,应该默认会包含当前上下文中所有实现了MappedInterceptor的bean

2. BeanNameUrlHandlerMapping
它将url映射到bean。和在RequestMappingHandlerMapping中,一个url映射到一个contorller的方法不同。
同时BeanNameUrlHandlerMapping要求bean name必须是以'/'开始的。BeanNameUrlHandlerMapping默认会加载当前applicationContext中所有bean name以‘/’开始bean,然后以bean name作为url建立到bean的映射。 这种方式中一般要求用户的controller类实现Controller接口,或者AbstractController抽象类。如下所示:

// bean name要以‘/’开始,
@Controller("/test.do")
public class EchoController extends AbstractController {
    protected ModelAndView handleRequestInternal(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("date", "date");
        return modelAndView;
    }
}

BeanNameUrlHandlerMapping返回的HandlerExecutionChain的handler就是匹配url的bean的实例。

3. SimpleUrlHandlerMapping
这是一种很灵活的Handler Mapping,能够直接指定url到bean的映射, 可以在配置文件中指定:



        
            
                
                   
                    echoContorller
                
            
        

4 HandlerAdaptor

接口HandlerAdaptor如下:

// 参数handler即HandlerExecutionChain中的handler, supports判断当前adaptor是否适配handler
boolean supports(Object handler);

// 使用handler处理请求
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

4.1 HandlerAdaptor实现类

1. RequestMappingHandlerAdapter
它能适配RequestMappingHandlerMapping返回的handler,即MethodHandler的实例(它封装了处理当前请求url的bean以及更加具体method的信息)

RequestMappingHandlerAdapter会处理@PathVariable,@ModelAttribute等注解。

它会先完成使用了@ModelAttribute注解的方法的调用。然后调用url映射的具体方法的调用(调用前完成方法参数的绑定)。

2. SimpleControllerHandlerAdapter
它能够适配BeanNameUrlHandlerMappingSimpleUrlHandlerMapping返回的handler(也就是controller bean的实例)

5. HandlerInterceptor

在HandlerAdaptor # handle前后调用,也就是在用户业务逻辑代码前后提供预处理和后处理的能力。

自定义interceptor需要实现接口HandlerInterceptor,然后在spring的xml文件中配置:



            
                // 拦截这个地址
                
               //不拦截这个地址
                
            

上面这种写法,自定义拦截器myInterceptor会被包装成MappedInterceptor,看看MappedInterceptor的成员就明白了:

// url匹配这些模式的拦截
private final String[] includePatterns;

// url匹配这些模式的不拦截
private final String[] excludePatterns;

// 真正的拦截器
private final HandlerInterceptor interceptor;

所以如果你不使用的形式定义拦截器,也可以直接使用MappedInterceptor作为bean,如下:


                
                    
                        /test.do
                    
                
                
                    
                

上面不管哪种定义拦截器的方式,都会被RequestMappingHandlerMapping,BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping加载。

要想单独只被某个HandlerMapping加载,应该做如下配置:


       
           
                 
           
       


这样myInterceptor这个拦截器只会被这个BeanNameUrlHandlerMapping使用到。

注:如果按上面配置,同时xml文件里又存在,那么可能发现不生效,原因是因为会默认加载一个BeanNameUrlHandlerMapping,而且它的优先级是2(越小越高),而你在配置文件里的BeanNameUrlHandlerMapping默认优先级很低,前面说到DispatcherServlet会对HandlerMapping排序,所以它默认加载的总是会先使用。

一些参考

  1. spring mvc快速入门教程
  2. Spring MVC实践

你可能感兴趣的:(spring mvc工作机制)