zoukankan      html  css  js  c++  java
  • SpringMVC 请求全过程漫谈

    
    

    SpringMVC 请求全过程漫谈

    SpringMVC 跟其他的mvc框架一样,如 struts,webwork, 本质上都是 将一个 http 请求(request)进行各种处理, 然后返回response, 也就是一些数据给调用方。 调用方可以是 浏览器, 也可以是其他程序。 request 有各种各样,千差万别, response 也是各种各样,千差万别。甚至是可能没有 response。 如果考虑到 异步调用/ websocket , 那就更加复杂了。

    request 有各种各样的属性, 常见的有协议,请求路径 即 uri, method, 各种请求头,请求的参数,请求body, 编码格式,cookie, content-type, accept, language, 等等

    response 也是各种各样的属性, 响应头, 编码格式, 响应body, 响应cookie, content-type 等等


    通常, MVC框架们是这样做的, 不管怎么样, 首先, 根据 uri 返回一个处理器吧,然后你处理吧,处理前后设置各种拦截器吧,过滤器吧, 然后返回一个json 或者页面吧。 在SpringMVC中, 这个过程非常复杂,因为它做了很多事情。当然正是这种复杂,给我们的开发带来了很大的便利。但是也是带来了很多的困惑。这需要很大的学习成本。

    下面我们正式开始简单的分析。

    由于使用截图不方便配文字,故我这里通篇使用纯代码。 另外因为从 notepad++ 复制过来后没有了 tab 等空格 被编辑器过滤掉, 故我这里使用 贴代码的方式叙述:

    
     
    SpringMVC的入口是 
    org.springframework.web.servlet.DispatcherServlet 。这个地球人都知道, 自然不必多说。
    
    它有个默认的策略配置 defaultStrategies,为一个Properties类型对象 ,其实是读取了 org.springframework.web.servlet.DispatcherServlet.properties 文件: 
    
        static {
            try {
                ClassPathResource resource = new ClassPathResource("DispatcherServlet.properties", DispatcherServlet.class);
                defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
            } catch (IOException var1) {
                throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + var1.getMessage());
            }
        }
        
    # Default implementation classes for DispatcherServlet's strategy interfaces.
    # Used as fallback when no matching beans are found in the DispatcherServlet context.
    # Not meant to be customized by application developers.
    
    org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
    
    org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
    
    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
        org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
    
    org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,
        org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,
        org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
    
    org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,
        org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,
        org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
    
    org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
    
    org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
    
    org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
    
    
    我们简化一下,然后关键看这里:
    HandlerMapping = BeanNameUrlHandlerMapping, DefaultAnnotationHandlerMapping 
    HandlerAdapter = HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter, AnnotationMethodHandlerAdapter
    HandlerExceptionResolver = AnnotationMethodHandlerExceptionResolver,ResponseStatusExceptionResolver,DefaultHandlerExceptionResolver
    RequestToViewNameTranslator = DefaultRequestToViewNameTranslator
    ViewResolver = InternalResourceViewResolver
    
    
    它继承于 HttpServlet, 其中入口方法是 doService ,然后很快处理权交给了doDispatch方法。(我们略去其他的各种复杂的杂七杂八的繁琐的细节,暂时不需要关心到那些地方。)
    
    doDispatch 自然是非常非常关键的, 我们仔细分析分析: 
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            HandlerExecutionChain mappedHandler = null;
            boolean multipartRequestParsed = false;
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
            try {
                try {
                    ModelAndView mv = null;
                    Object dispatchException = null;
    
                    try {
                        processedRequest = this.checkMultipart(request); // STEP 1 检查如果是 文件上传之类的,那么 先用 multipartRequestParsed 标识下
                        multipartRequestParsed = processedRequest != request;
                        mappedHandler = this.getHandler(processedRequest); // STEP 2 这里相当关键
                        if (mappedHandler == null || mappedHandler.getHandler() == null) {
                            this.noHandlerFound(processedRequest, response); // 如果找不到 handler,那么就不处理吧。
                            当然, 这里仅仅是说springmvc 不进行处理, 把处理权返还给了servlet容器, servlet容器还是会继续处理的。
                            这里的 handler 是 HandlerExecutionChain。  顾名思义, 它本意上是 一条 handler 执行链。 
                            
                            return;
                        }
    
                        HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());// STEP 3
                        String method = request.getMethod();
                        boolean isGet = "GET".equals(method);
                        if (isGet || "HEAD".equals(method)) {
                            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                            if (this.logger.isDebugEnabled()) {
                                this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                            }
    
                            if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                                return;  // 对应 get 请求, 如果资源还没有过期,那么直接返回吧。 这个时候通常 会用到缓存技术 
                            }
                        }
    
                        if (!mappedHandler.applyPreHandle(processedRequest, response)) { // STEP 4
                            return; // 如果预处理失败,那么就返回吧。  这里的 applyPreHandle 其实就可以当作是一个 过滤器。 
                        }
    
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //STEP 5
                        if (asyncManager.isConcurrentHandlingStarted()) {
                            return; // 如果目前正在进行异步处理, 那么先返回吧。
                        }
    
                        this.applyDefaultViewName(processedRequest, mv);//STEP 6 根据request 和 mv,如果没有view的话,确定一个 viewName。
                        // 这个通常是由于 mv 并没有直接包含一个 view, 而仅仅是一个 viewName, 或者为空。 那么我们需要一个默认值
                        
                        mappedHandler.applyPostHandle(processedRequest, response, mv); // STEP 7
                    } catch (Exception var20) {
                        dispatchException = var20;
                    } catch (Throwable var21) {
                        dispatchException = new NestedServletException("Handler dispatch failed", var21);
                    }
    
                    this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);//STEP 8 主要是处理异常
                    
                    // processDispatchResult 成功处理完, 也有触发 triggerAfterCompletion 
                } catch (Exception var22) {
                    // 这里的意思是说, 如果 前面的 processDispatchResult 出了异常, 才会触发triggerAfterCompletion。 why?
                    this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
                } catch (Throwable var23) { // 同上
                    this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
                }
    
            } finally { //STEP 9
                if (asyncManager.isConcurrentHandlingStarted()) {
                    if (mappedHandler != null) {
                        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);// 异步请求相关处理
                    }
                } else if (multipartRequestParsed) {
                    this.cleanupMultipart(processedRequest); // 清理上传的临时文件之类的。
                }
    
            }
        }
    
    STEP 1 检查如果是 文件上传之类的,那么 先用 multipartRequestParsed 标识下
    
    STEP 2 获取处理器: mappedHandler = this.getHandler(processedRequest); 
    mappedHandler 是一个 HandlerExecutionChain,HandlerExecutionChain 大致有这么些内容。 
    public class HandlerExecutionChain {
        private final Object handler; //  实际的真正的处理器
        private HandlerInterceptor[] interceptors; // 拦截器
        private List<HandlerInterceptor> interceptorList; // 同上,本意是差不多的,但是为了内存优化,使用了一些奇怪的技巧。 一个是数组,一个是list。
        private int interceptorIndex; // 预处理之后的位置。比如假设原本有5个拦截器 1 2 3 4 5 ,applyPreHandle到第三个拦截器,那么triggerAfterCompletion 就是3 2 1。 
    
        boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {...}
        void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {...}
        void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {...}
        
        applyPreHandle 方法是 处理http请求前 进行拦截
        applyPostHandle 方法是 处理http请求成功后 进行 后置处理。 注意,这里就不能再说拦截了, 因为到这里,请求已经基本处理成功了。 当然, 其本质是一样的
        triggerAfterCompletion 方法是 处理http请求后,不管失败,还是失败, 都进行触发
    }   
    
    整体来看, HandlerExecutionChain 不像是一个 handler 执行链, 至少是不像 tomat 的 pipeline 那种的 valve 的执行链, 而更像是 HandlerWithInterceptor。 当然, 这种命名, 还是以专家为准。
    
    
    handler 是什么不要紧, 只要他能够处理 request。它是一个Object类型, 所以它是相当灵活的。 实际上, 我们的handler 往往需要 handlerAdapter来协助处理,
    handler 表明上是Object 当然, 实际上是不可能的,  Object 无法进行处理。  如果返回的是一个 Object, 那说明当前类可能 是一个适配器接口
    
    
    handler 如果是如何获取的? 是通过 handlerMappings,handlerMappings 是容器启动的时候注册的, 具体是如何注册的? 后面再讲。 handlerMapping 又是什么鬼? 它是专门用来获取HandlerExecutionChain的
    
    public interface HandlerMapping {
        String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
        String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
        String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
        String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
        String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
        String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
    
        HandlerExecutionChain getHandler(HttpServletRequest var1) throws Exception;
    }
    
    HandlerMapping 的层级关系大致是这样的:
    HandlerMapping 
        MatchableHandlerMapping (org.springframework.web.servlet.handler)
            RequestMappingHandlerMapping (org.springframework.web.servlet.mvc.method.annotation)
                AbstractEndpointHandlerMapping (org.springframework.boot.actuate.endpoint.mvc)
                    CloudFoundryEndpointHandlerMapping (org.springframework.boot.actuate.cloudfoundry)
                    EndpointHandlerMapping (org.springframework.boot.actuate.endpoint.mvc)
                StaticRequestMappingHandlerMapping in StandaloneMockMvcBuilder (org.springframework.test.web.servlet.setup)
            AbstractUrlHandlerMapping (org.springframework.web.servlet.handler)
                WelcomePageHandlerMapping in WebMvcAutoConfiguration (org.springframework.boot.autoconfigure.web)
                AbstractDetectingUrlHandlerMapping (org.springframework.web.servlet.handler)
                    DefaultAnnotationHandlerMapping (org.springframework.web.servlet.mvc.annotation)
                    BeanNameUrlHandlerMapping (org.springframework.web.servlet.handler)
                    AbstractControllerUrlHandlerMapping (org.springframework.web.servlet.mvc.support)
                        ControllerClassNameHandlerMapping (org.springframework.web.servlet.mvc.support)
                        ControllerBeanNameHandlerMapping (org.springframework.web.servlet.mvc.support)
                SimpleUrlHandlerMapping (org.springframework.web.servlet.handler)
        CompositeHandlerMapping in EndpointWebMvcChildContextConfiguration (org.springframework.boot.actuate.autoconfigure)
        AbstractHandlerMapping (org.springframework.web.servlet.handler)
            EmptyHandlerMapping in WebMvcConfigurationSupport (org.springframework.web.servlet.config.annotation)
            AbstractUrlHandlerMapping (org.springframework.web.servlet.handler)
                WelcomePageHandlerMapping in WebMvcAutoConfiguration (org.springframework.boot.autoconfigure.web)
                AbstractDetectingUrlHandlerMapping (org.springframework.web.servlet.handler)
                    DefaultAnnotationHandlerMapping (org.springframework.web.servlet.mvc.annotation)
                    BeanNameUrlHandlerMapping (org.springframework.web.servlet.handler)
                    AbstractControllerUrlHandlerMapping (org.springframework.web.servlet.mvc.support)
                        ControllerClassNameHandlerMapping (org.springframework.web.servlet.mvc.support)
                        ControllerBeanNameHandlerMapping (org.springframework.web.servlet.mvc.support)
                SimpleUrlHandlerMapping (org.springframework.web.servlet.handler)
            AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
                RequestMappingInfoHandlerMapping (org.springframework.web.servlet.mvc.method)
                    RequestMappingHandlerMapping (org.springframework.web.servlet.mvc.method.annotation)
                        AbstractEndpointHandlerMapping (org.springframework.boot.actuate.endpoint.mvc)
                            CloudFoundryEndpointHandlerMapping (org.springframework.boot.actuate.cloudfoundry)
                            EndpointHandlerMapping (org.springframework.boot.actuate.endpoint.mvc)
                        StaticRequestMappingHandlerMapping in StandaloneMockMvcBuilder (org.springframework.test.web.servlet.setup)
    
    很复杂, 但是我只分析几个常用的关键的。
    RequestMappingHandlerMapping ,它的继承链很深, 
    
    它的关键方法是通过 AbstractHandlerMapping 实现的 ,
        public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            Object handler = this.getHandlerInternal(request);
            if (handler == null) {
                handler = this.getDefaultHandler();
            }
    
            if (handler == null) {
                return null;
            } else {
                if (handler instanceof String) {
                    String handlerName = (String)handler;
                    handler = this.getApplicationContext().getBean(handlerName);
                }
    
                HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
                if (CorsUtils.isCorsRequest(request)) {
                    CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
                    CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
                    CorsConfiguration config = globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig;
                    executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
                }
    
                return executionChain;
            }
        }
        
    看到 getHandlerInternal 没? 正是这类返回了 hanler ,但是,它是抽象的,具体由 AbstractHandlerMethodMapping 实现,AbstractHandlerMethodMapping , 顾名思义, 它对于 http uri 的映射都是返回一个 HandlerMethod 对象, 以进行处理: 
    
        protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
            String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Looking up handler method for path " + lookupPath);
            }
    
            this.mappingRegistry.acquireReadLock();
    
            HandlerMethod var4;
            try {
                HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
                if (this.logger.isDebugEnabled()) {
                    if (handlerMethod != null) {
                        this.logger.debug("Returning handler method [" + handlerMethod + "]");
                    } else {
                        this.logger.debug("Did not find handler method for [" + lookupPath + "]");
                    }
                }
    
                var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
            } finally {
                this.mappingRegistry.releaseReadLock();
            }
    
            return var4;
        }
    可见, 对于RequestMappingHandlerMapping, 它的handler 就是一个 HandlerMethod, 也就是一个封装了 @RequestMapping 注解过的 @Controller 中的方法 。  这个我们已经很熟了, 不多说。
    
    WelcomePageHandlerMapping 它处理 /** 格式的请求 返回一个ParameterizableViewController 对象, 这个对象给请求进行了转发:controller.setViewName("forward:index.html");,当然, 也可能不做任何处理。 
    
    对于BeanNameUrlHandlerMapping 它继承于 AbstractUrlHandlerMapping , 直接根据 url 来映射的, 但是它是把 url 映射到了一个 bean:
    
        protected String[] determineUrlsForHandler(String beanName) {
            List<String> urls = new ArrayList();
            if (beanName.startsWith("/")) {
                urls.add(beanName);
            }
    
            String[] aliases = this.getApplicationContext().getAliases(beanName); // 这里是关键
            String[] var4 = aliases;
            int var5 = aliases.length;
    
            for(int var6 = 0; var6 < var5; ++var6) {
                String alias = var4[var6];
                if (alias.startsWith("/")) {
                    urls.add(alias);
                }
            }
    
            return StringUtils.toStringArray(urls);
        }
        
        至于 这个bean 怎么处理 请求。 spring mvc 没有具体限制,但是它需要返回一个  view。
        
    
    SimpleUrlHandlerMapping 它继承于 AbstractUrlHandlerMapping ,它提供了一个 map, 专门来存放 url - handle 映射 :
    
        private final Map<String, Object> urlMap = new LinkedHashMap(); // 这个映射, 通常是spring xml 文件配置的。
        
        
          handler 是Object,那怎么行!????  那么它具体到底是什么呢?
          
          
    STEP 3 获取一个处理器适配器
     HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
    它是一个接口, 它的定义很简单:
    public interface HandlerAdapter {
        boolean supports(Object var1);
    
        ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
    
        long getLastModified(HttpServletRequest var1, Object var2);
    }
    
    这里是关键. handlerAdapters 是初始化 spring MVC的时候就设置好了的, 默认有这么些:
    HandlerAdapter = HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter, AnnotationMethodHandlerAdapter
    HttpRequestHandlerAdapter 的handle 逻辑大致是, 
        public boolean supports(Object handler) {
            return handler instanceof HttpRequestHandler; // 如果Object handler是一个HttpRequestHandler, 那么我来处理。
            HttpRequestHandler 又是什么??  哦, spring mvc 提供的一个通用的接口而已, 没什么特别的。 通常是用来处理静态资源。
            因为他没什么特别的, 最简单。 比如ResourceHttpRequestHandler , 根据 request获取一个 Resource, 然后直接把Resource 写到前台。
    
        }
        
    
        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            ((HttpRequestHandler)handler).handleRequest(request, response);// 强转为HttpRequestHandler 进行处理
            return null;
        }
        
        HttpRequestHandlerAdapter 是第一个默认的 adapter,优先级还是比较高的吧。 那么如果我们使用第三方的,其优先级呢? 
    
    SimpleControllerHandlerAdapter 的逻辑是:
    
        public boolean supports(Object handler) {
            return handler instanceof Controller;// 注意 这里的Controller 不是@Controller注解, 而是org.springframework.web.servlet.mvc.Controller !!!
        }
    
        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            return ((Controller)handler).handleRequest(request, response); 
        }
    想法是好的, 但是通常我们不会去实现一个 org.springframework.web.servlet.mvc.Controller ... 
    
    
    AnnotationMethodHandlerAdapter 并不是我们对 @RequestMapping 注解的 适配器, RequestMappingHandlerAdapter 才是。RequestMappingHandlerAdapter 呢, 其实是mvc:annotation-driven  这个xml 元素注入进来的。 它是比较重量级的。 因为 我们的 @RequestMapping  配置可以很复杂, 所以,可想而知, 它很重,这里暂不多讲。
    
        public boolean supports(Object handler) {
            return this.getMethodResolver(handler).hasHandlerMethods(); // 判断方法上面是否有 @RequestMapping  等
        }
        
    AnnotationMethodHandlerAdapter 是这样的:
        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            Class<?> clazz = ClassUtils.getUserClass(handler);
            Boolean annotatedWithSessionAttributes = (Boolean)this.sessionAnnotatedClassesCache.get(clazz);
            if (annotatedWithSessionAttributes == null) {
                annotatedWithSessionAttributes = AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null;
                this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
            }
    
            if (annotatedWithSessionAttributes) {
                this.checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
            } else {
                this.checkAndPrepare(request, response, true);
            }
    
            if (this.synchronizeOnSession) {
                HttpSession session = request.getSession(false);
                if (session != null) {
                    Object mutex = WebUtils.getSessionMutex(session);
                    synchronized(mutex) {
                        return this.invokeHandlerMethod(request, response, handler); // 这里的 handler 就是那个方法!!!!
                    }
                }
            }
    
            return this.invokeHandlerMethod(request, response, handler); // 我怎么处理?? 主要就是调用 那个被注解了的方法 
        }
        
        
    而RequestMappingHandlerAdapter 的关键方法: 
        protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
            if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                this.checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
            } else {
                this.checkAndPrepare(request, response, true);
            }
    
            if (this.synchronizeOnSession) {
                HttpSession session = request.getSession(false);
                if (session != null) {
                    Object mutex = WebUtils.getSessionMutex(session);
                    synchronized(mutex) {
                        return this.invokeHandleMethod(request, response, handlerMethod);
                    }
                }
            }
    
            return this.invokeHandleMethod(request, response, handlerMethod);
        }
        
    
    HandlerAdapter 的hierarchy 如下:
    HandlerAdapter
        HttpRequestHandlerAdapter (org.springframework.web.servlet.mvc)
        SimpleServletHandlerAdapter (org.springframework.web.servlet.handler)
        CompositeHandlerAdapter in EndpointWebMvcChildContextConfiguration (org.springframework.boot.actuate.autoconfigure)
        AnnotationMethodHandlerAdapter (org.springframework.web.servlet.mvc.annotation)
        AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
            RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
        SimpleControllerHandlerAdapter (org.springframework.web.servlet.mvc)
    
    除了默认配置的 三个 , SimpleServletHandlerAdapter 也是比较有趣的, 它把 servlet 当作handler, 相当于是回到了纯 j2ee 时代。
    
    了解了上面的内容后, getHandlerAdapter 方法便不难理解了吧: 
        protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
            Iterator var2 = this.handlerAdapters.iterator();
    
            HandlerAdapter ha;
            do {
                if (!var2.hasNext()) {
                    throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
                }
    
                ha = (HandlerAdapter)var2.next();
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Testing handler adapter [" + ha + "]");
                }
            } while(!ha.supports(handler));
    
            return ha;
        }
    需要注意的是, 我们的hanlder 是通过handlerMappings 获取的, 而handlerMappings 是通过手动注册给spring的, 或者通过扫描方式, 比如@ComponentScan 之类的。
    
    
    那么, 对于 SimpleControllerHandlerAdapter,其HttpRequestHandler 是怎么注册给而handlerMappings 的呢? SimpleUrlHandlerMapping 
    SimpleUrlHandlerMapping 有个urlMap 这个就是url-handler 映射,它是在注册 SimpleUrlHandlerMapping的时候配置的。
    Map<String, Object> urlMap = new LinkedHashMap(); 如果我们使用 mvc:urlmapping 这里的Object 通常就是 HttpRequestHandler , 
    
    SimpleControllerHandlerAdapter 需要一个 Controller,这个Controller 又是何时注册给是怎么注册给而handlerMappings 的呢?
    我们可以使用 SimpleUrlHandlerMapping:
    1 先注册一个 Servlet ,比如:
    @Component
    public class LkController implements Controller { // 这里也可以是继承 AbstractController
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            System.out.println("LkxxController.handleRequest");
    
            return new ModelAndView("xxx");
        }
    }
    2 使用SimpleUrlHandlerMapping将 url 和 Controller 做个映射 
        <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="mappings">
                <value>
                    /aaaa = lkController  // 这里必须是beanName 
                </value>
            </property>
        </bean>
    
    
    还有SimpleServletHandlerAdapter, 它的使用如下:
    1 先注册一个 Servlet ,比如:
    @Component(value = "lKServlet")
    public class LKServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           ...
        }
        ...
    }
    
    
    2 使用SimpleUrlHandlerMapping将 url 和 Servlet做个映射 
        <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="mappings">
                <value>
                    /aaaa = lkController  // 这里必须是beanName 
                    /bbbb = lK2Servlet    // 这里必须是beanName 
                </value>
            </property>
        </bean>
    3 注册一个 SimpleServletHandlerAdapter, 无需id:
       <bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter">
       </bean>
    
    我们已经知道,默认有:
    HandlerMapping = BeanNameUrlHandlerMapping, DefaultAnnotationHandlerMapping 
    
    对于 BeanNameUrlHandlerMapping 来说, 当轮到它来处理handler 的时候, 它会根据 url 去查找同名 bean。 注意,bean 是什么类型不重要, 只要找到了, 那么 BeanNameUrlHandlerMapping 的使命就完成了 !    返回bean 怎么处理 http请求? 那个需要各种 HandlerAdapter 进行适配。
    
    
    我们知道有 DispatchServlet.properties,它会注册一些默认的配置。 但是, 如果我们使用 springmvc, 我们的 <mvc:annotation-driven validator="validator"></mvc:annotation-driven> 标签就会给我们注册一系列的 配置,DispatchServlet.properties反而用不上了。。 
    
    HandlerMapping 有很多个, 他们是有顺序的, 它是根据 order字段而定的。 当springmvc 找到了一个可以处理  http请求url 的HandlerMapping , 后面的HandlerMapping 就不会再有效。 
    
    
    对于HandlerAdapter 也是一样, 它们也有一定的顺序, 但是HandlerAdapter 接口并没有继承 Ordered接口, 故他们的顺序其实就是 注册的时间先后顺序, 除了 AbstractHandlerMethodAdapter, 它实现了Ordered接口,因为可以根据order 属性做一些特殊处理 。 据我观察, HandlerAdapter 的顺序其实不是很重要, 因为HandlerAdapter 关键是support 方法,是根据类型判断的。 
    
    
    STEP 4 拦截器预处理,被拦截了,那么就不再继续后续处理
    这里通常可以用来做一些权限方面的认证授权工作
    
    STEP 5 使用处理器适配器进行处理,返回一个mv:
     mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
     
    STEP 6 使用viewNameTranslator给设置一个默认的 view
    this.applyDefaultViewName(processedRequest, mv);  HandlerAdapter 处理完后,可能就ok了, 但是仍然有可能没有然后返回一个 ModelAndView ———— 毕竟什么情况都可能发生。  那么我们就使用viewNameTranslator给设置一个默认的 view吧, 默认是 前缀+uri最后一个/后面d部分+后缀:  
    
        public String getViewName(HttpServletRequest request) {
            String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
            return this.prefix + this.transformPath(lookupPath) + this.suffix;
        }
    
    STEP 7 使用各种 拦截器进行后处理
    mappedHandler.applyPostHandle(processedRequest, response, mv); 这里用到了 HandlerExecutionChain 的applyPostHandle方法, 也就是使用各种 拦截器进行后处理:
    
        void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
            HandlerInterceptor[] interceptors = this.getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for(int i = interceptors.length - 1; i >= 0; --i) {
                    HandlerInterceptor interceptor = interceptors[i];
                    interceptor.postHandle(request, response, this.handler, mv);
                }
            }
    
        }
    
    STEP 8 处理拦截器适配器处理后的结果或异常,如存在view ,那么渲染他。
    this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
    
    STEP 1/2/3/4/5/6/7 过程中是可能出现异常的, 这个不要紧, 我们把他捕获下来。到这里,其实已经差不多了,只剩最后一步: view 已经有了, 那么就渲染view 吧。 同时,如果有异常, 那么先使用 handlerExceptionResolvers 处理它。
    
        private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
            boolean errorView = false;
            if (exception != null) {
                if (exception instanceof ModelAndViewDefiningException) {
                    this.logger.debug("ModelAndViewDefiningException encountered", exception);
                    mv = ((ModelAndViewDefiningException)exception).getModelAndView();
                } else {
                    Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                    mv = this.processHandlerException(request, response, handler, exception); //  异常处理
                    errorView = mv != null;
                }
            }
    
            if (mv != null && !mv.wasCleared()) {
                this.render(mv, request, response); // 到了这一步, 不管mv 是个什么样的mv, 只要它不为空, 那就渲染他。
                if (errorView) {
                    WebUtils.clearErrorRequestAttributes(request);
                }
            } else if (this.logger.isDebugEnabled()) {
                this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
            }
    
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
                }
    
            }
        }
        
    processHandlerException 处理结果仍然是一个 ModelAndView:   
        protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            ModelAndView exMv = null;
            Iterator var6 = this.handlerExceptionResolvers.iterator();
    
            while(var6.hasNext()) {
                HandlerExceptionResolver handlerExceptionResolver = (HandlerExceptionResolver)var6.next();
                exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
                if (exMv != null) {
                    break;
                }
            }
    
            if (exMv != null) {
                if (exMv.isEmpty()) {
                    request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
                    return null;
                } else {
                    if (!exMv.hasView()) {
                        exMv.setViewName(this.getDefaultViewName(request));
                    }
    
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
                    }
    
                    WebUtils.exposeErrorRequestAttributes(request, ex, this.getServletName());
                    return exMv;
                }
            } else {
                throw ex;
            }
        }
    
    
    最后是渲染,也就是 render。 注意, 对于 @ResponseBody 注解的@RequestMapping 方法,如果方法正常返回, 没有异常,那么 这里的 render 方法是不会执行的, 因为, 它不需要渲染什么, 而是直接就显示给前台。 当然这个其实是 由 RequestResponseBodyMethodProcessor类型的HandlerMethodReturnValueHandler对象 完成的 : 
    
        protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
            Locale locale = this.localeResolver.resolveLocale(request);
            response.setLocale(locale);
            View view;
            if (mv.isReference()) {
                view = this.resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
                if (view == null) {
                    throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");
                }
            } else {
                view = mv.getView();
                if (view == null) {
                    throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + this.getServletName() + "'");
                }
            }
    
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'");
            }
    
            try {
                view.render(mv.getModelInternal(), request, response);
            } catch (Exception var7) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'", var7);
                }
    
                throw var7;
            }
        }
    
        protected String getDefaultViewName(HttpServletRequest request) throws Exception {
            return this.viewNameTranslator.getViewName(request); // 使用 viewNameTranslator
        }
    而: 
        protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
            Iterator var5 = this.viewResolvers.iterator(); // 用到了 viewResolvers
    
            View view;
            do {
                if (!var5.hasNext()) {
                    return null;
                }
    
                ViewResolver viewResolver = (ViewResolver)var5.next();
                view = viewResolver.resolveViewName(viewName, locale);
            } while(view == null);
    
            return view;
        }
    
    public class ModelAndView {
        private Object view;
        private ModelMap model;
        private boolean cleared = false;
        
        public boolean isReference() {
            return this.view instanceof String;
        }
        ... 
    }
    
    ModelAndView 提供了为数众多的构造器。 当view 字段是一个string 的时候,那么这个ModelAndView 就是一个引用对象。 这个时候我们需要resolveViewName 对他进一步解析, 这里就用到了 viewResolvers 。 否则view字段就应该是一个View对象。(这里只有两种情况)
    ViewResolver 接口很简单。 
    public interface ViewResolver {
        View resolveViewName(String var1, Locale var2) throws Exception;
    }
    
    View 是几乎最后一步了, 执行到这里, 前端基本上已经获得了所需结果。 
    
    STEP 9 清理request, 比如清理临时文件
  • 相关阅读:
    我要变牛逼
    java web
    导师选择
    2.1进程
    来到博客园写东西的第一天
    简单的页面布局
    html5
    第一个servlet程序
    java2D
    字节流 文件字节流 缓冲字节流
  • 原文地址:https://www.cnblogs.com/FlyAway2013/p/7960672.html
Copyright © 2011-2022 走看看