• SpringMVC工作原理


    一、MVC程序

    我们使用全注解的方式来使用MVC,所以先写段演示程序。 

    /**
     * 系统初始化入口。【代替web.xml】
     */
    public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
        /**
         * Spring配置(设置spring容器启动的入口。配置ContextLoaderListener)
         */
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class<?>[]{AppConfig.class};
        }
    
        /**
         * SpringMVC配置文件。(配置DispatcherServlet)
         */
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class<?>[]{WebMvcConfig.class};
        }
    
        /**
         * 设置DispatcherServlet的拦截路径
         */
        @Override
        protected String[] getServletMappings() {
            // /:拦截所有请求,包括静态资源,但不包括jsp
            // /*:拦截所有请求,包括静态资源和jsp文件
            return new String[]{"/"};
        }
    }
    
    /**
     * Spring父容器扫描.【@Contoller注解由MVC子容器来扫描,MVC中的C就表示Controller】
     */
    @ComponentScan(value = "com.mvc", excludeFilters = {//排除扫描@Controller
            @Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
    })
    public class AppConfig {
    }
    
    /**
     * SpringMVC子容器
     */
    @EnableWebMvc
    //@ComponentScan("com.mvc")
    @ComponentScan(value = "com.mvc", includeFilters = {//扫描@Controller
            @Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
    }, useDefaultFilters = false)//禁用默认的过滤规则
    public class WebMvcConfig extends WebMvcConfigurerAdapter {
    
        /**
         * 继承默认的转换器。【导入jackson包后会自动添加json转换器,不需显式添加】
         */
        /*
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            System.out.println(converters);
            converters.add(new MappingJackson2HttpMessageConverter());
            System.out.println(converters);
        }*/
    
        /**
         * 拦截器
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyAuthInterceptor());
        }
    
        /**
         * 视图解析器
         */
        @Bean
        public ViewResolver viewResolver() {
            //jsp视图解析器
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix("/WEB-INF/view/");
            resolver.setSuffix(".jsp");
            resolver.setExposeContextBeansAsAttributes(true);
            return resolver;
        }
    
        /**
         * 配置静态资源的处理
         * 将请求交由Servlet处理,不经过DispatchServlet
         */
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    
    }
    
    /**
     * 返回视图
     */
    @Controller
    public class UserController {
    
        @Autowired
        private UserService userService;
    
        @GetMapping("/user")
        public String getUser() {
            userService.getUser();
            return "success";
        }
    
    }
    
    /**
     * 返回rest数据
     */
    @RestController
    public class UserRestController {
    
        @Autowired
        private UserService userService;
    
        @ResponseBody
        @GetMapping("/user-rest")
        public User getUser() {
            return userService.getUser();
        }
    
    }

    二、源码分析

    1.MVC工作原理

    下面是DispatcherServlet的工作原理图,图片来源于网络。

    DispatcherServlet的工作流程如下:

    1.请求到达后,调用HandlerMapping来查找对应的处理器Handler

    2.查找能调用上面Handler的HandlerAdapter。(并非直接调用Handler,而是使用的适配器模式)

    3.预处理操作。比如执行拦截器

    4.真正的处理请求的操作。(比如执行controller中的某方法来处理请求)

    5.后处理操作。重走拦截器栈。(跟strut2中的拦截器栈相似)

    6.进行视图渲染输出。如果前面步骤发生了异常,则这里要处理异常。

        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            HandlerExecutionChain mappedHandler = null;
            boolean multipartRequestParsed = false;
    
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;
    
                try {
              //0.检查是否为文件上传请求 processedRequest
    = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 1.查找能处理当前请求的处理器 Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // 2.查找能处理当前请求的处理器适配器 Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } }           // 3.预处理:比如调用拦截器 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 4.真正的处理请求(调用Controller的处理方法) Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv);
              // 5.处理后操作:比如再次重走拦截器栈 mappedHandler.applyPostHandle(processedRequest, response, mv); }
    catch (Exception ex) { dispatchException = ex; }
           // 6.处理最终结果:返回视图或处理异常 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }
    catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }

    下面来看看每个步骤的源码

    1.查找处理器-getHandler

    遍历处理器HandlerMapping,查找出能够处理该请求的处理器,显然可以是多个,这些处理器组成了一个处理器执行链。

        protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            //遍历SpringMVC内置的几种HandlerMapping
            for (HandlerMapping hm : this.handlerMappings) {
                if (logger.isTraceEnabled()) {
                    logger.trace(
                            "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
                }
                
                HandlerExecutionChain handler = hm.getHandler(request);
                //只要找到一种能处理我们的程序的HandlerMapping就返回。
                //
                if (handler != null) {
                    return handler;
                }
            }
            return null;
        }

    由于我们可以使用不同的方式来开发SpringMVC应用,每种方式SpringMVC都能找到对应的处理器来进行处理。比如我们使用@RequestMapping注解方式,则SpringMVC就利用RequestMappingHandler来处理。常见的几种HandlerMapping有RequestMappingHandlerMapping,BeanNameUrlHandlerMapping,SimpleUrlHandlerMaping。

    SpringMVC只会选用其中一种HandlerMapping来处理。因为我们编写的程序使用了@RequestMapping,实际上就是@RequestMapping方式,所以SpringMVC对应使用RequestMappingHandlerMapping来处理。

    SpringMVC是怎么判断RequestMappingHandlerMapping能处理我们的程序呢?实际上每种HandlerMapping对应一些默认的拦截器,也可能没有对应的拦截器。SpringMVC通过查找所有的拦截器,如果拦截器能够处理当前请求,则将这些拦截器串起来组成拦截器链。只要该拦截器链不为null,就表明能够处理我们的程序。

        @Override
        public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            Object handler = getHandlerInternal(request);
            if (handler == null) {
                handler = getDefaultHandler();
            }
            if (handler == null) {
                return null;
            }
            // Bean name or resolved handler?
            if (handler instanceof String) {
                String handlerName = (String) handler;
                handler = getApplicationContext().getBean(handlerName);
            }
            //获取能处理当前Controller的拦截器链
            HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
            if (CorsUtils.isCorsRequest(request)) {
                CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
                CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
                CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
                executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
            }
            return executionChain;
        }
    
        //AbstractHandlerMapping#getHandlerExecutionChain()
        protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
            HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                    (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    
            String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
            //查找所有能处理当前url的拦截器,组成拦截器链
            for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
                if (interceptor instanceof MappedInterceptor) {
                    MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                    if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                        chain.addInterceptor(mappedInterceptor.getInterceptor());
                    }
                }
                else {
                    chain.addInterceptor(interceptor);
                }
            }
            return chain;
        }

    下面是能处理我们UserController的拦截器链,除了我们自定义的MyAuthInterceptor外,还有两个默认拦截器。

    2.查找能调用Handler的HandlerAdapter

    因为系统中有不同类型的controller,每种controller的使用方式都不一样。必然会出现这样的代码。

    if(handler instanceof A类型){

      //处理A类型的controller

    }else (handler instanceof B类型){  

      //处理B类型的controller

    }

    显然这样的处理方式不利于扩展,因此使用适配器模式来调用不同的处理器。

        protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
            //遍历所有的HandlerMethodAdapter
            for (HandlerAdapter ha : this.handlerAdapters) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Testing handler adapter [" + ha + "]");
                }
                //只要该HandlerMethodAdapter支持处理handler就返回
                if (ha.supports(handler)) {
                    return ha;
                }
            }
            throw new ServletException("No adapter for handler [" + handler +
                    "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
        }
    
        //AbstractHandlerMethodAdapter.supports()
        @Override
        public final boolean supports(Object handler) {
            return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
        }
        
        //RequestMappingHandlerAdapter.supportsInternal()
        @Override
        protected boolean supportsInternal(HandlerMethod handlerMethod) {
            //RequestMappingHandlerAdapter默认是支持处理handler的
            return true;
        }

    RequestMappingHandlerAdapter支持处理我们程序中的UserController。

    3.预处理操作

    在处理请求之前会先执行预处理操作,比如执行拦截器。具体逻辑是遍历拦截器栈,找出能处理该请求的拦截器(每个拦截器都要配置拦截映射),然后调用该拦截器的preHandler()方法进行前置处理。在之后的第5步会逆序重走拦截器栈进行后置处理(同struts2)。

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
            //获取所有的拦截器
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                //依次执行每个拦截器(前置处理)
                for (int i = 0; i < interceptors.length; i++) {
                    HandlerInterceptor interceptor = interceptors[i];
                    if (!interceptor.preHandle(request, response, this.handler)) {//前置处理
                        triggerAfterCompletion(request, response, null);
                        return false;
                    }
                    this.interceptorIndex = i;
                }
            }
            return true;
        }

    4.真正的处理请求

    这里会真正的执行controller中的方法来处理请求。下面来看看我们常用的RequestMappingHandlerAdatper是如何处理请求的。

        @Override
        public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            return handleInternal(request, response, (HandlerMethod) handler);
        }
        
        protected ModelAndView handleInternal(HttpServletRequest request,
                HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
            checkRequest(request);
    
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                prepareResponse(response);
            }
    
            // Execute invokeHandlerMethod in synchronized block if required.
         //同步调用
            if (this.synchronizeOnSession) {
                HttpSession session = request.getSession(false);
                if (session != null) {
                    Object mutex = WebUtils.getSessionMutex(session);
                    synchronized (mutex) {//加锁
                //调用controller中的方法
                        return invokeHandlerMethod(request, response, handlerMethod);
                    }
                }
            }
         //非同步调用
            return invokeHandlerMethod(request, response, handlerMethod);
        }

    5.执行后处理操作

    第3步已经提到了,这里会逆序重新走一遍拦截器栈,也就是调用拦截器的postHandler()方法进行后置处理。

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
            HandlerInterceptor[] interceptors = 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);//后置处理
                }
            }
        }

    6.视图渲染和异常处理

    这一步主要是进行视图的渲染,如果前面步骤有异常,这里就进行异常处理。

    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) {
                    logger.debug("ModelAndViewDefiningException encountered", exception);
                    mv = ((ModelAndViewDefiningException) exception).getModelAndView();
                }
                else {
                    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                    //处理异常
                    mv = processHandlerException(request, response, handler, exception);
                    errorView = (mv != null);
                }
            }
    
            //执行到这里,说明没有发生异常
    
            // Did the handler return a view to render?
            if (mv != null && !mv.wasCleared()) {
                render(mv, request, response);
                if (errorView) {
                    WebUtils.clearErrorRequestAttributes(request);
                }
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                            "': assuming HandlerAdapter completed request handling");
                }
            }
    
            if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Concurrent handling started during a forward
                return;
            }
    
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, null);
            }
        }           

    处理异常的过程如下:

    遍历异常解析器来解析异常。通常我们会配置SpringMVC的全局异常解析器来进行统一异常处理,就是在这里使用它来处理异常的。

    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
                Object handler, Exception ex) throws Exception {
    
            // Check registered HandlerExceptionResolvers...
            ModelAndView exMv = null;
            for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
                exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
                if (exMv != null) {
                    break;
                }
            }
            if (exMv != null) {
                if (exMv.isEmpty()) {
                    request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
                    return null;
                }
                // We might still need view name translation for a plain error model...
                if (!exMv.hasView()) {
                    exMv.setViewName(getDefaultViewName(request));
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
                }
                WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
                return exMv;
            }
    
            throw ex;
        }
  • 相关阅读:
    ACTION 的跳转与参数传递
    action 与 action 之间的跳转
    图片不存在时,显示一个默认的图片 (自己理解)
    java 防止表单重复提交(serlvet)
    java防止表单重复提交
    了解 Windows Azure 存储计费 – 带宽、事务和容量
    微软开放技术热烈祝贺开源社成立!
    “开源社”(开源联盟)成立
    Azure SQL 数据库的灵活缩放预览版简介
    通过 PowerShell 支持 Azure Traffic Manager 外部端点和权重轮询机制
  • 原文地址:https://www.cnblogs.com/rouqinglangzi/p/8634638.html
Copyright © 2020-2023  润新知