SpringMVC

作者: 布拉德老瓜 | 来源:发表于2021-09-03 17:40 被阅读0次

    SpringMVC是一个基于三层处理架构的表现层处理框架。三层架构是指:表现层、业务层、数据访问层。表现层对应于业务中的视图、数据和servlet。MVC旨在对视图和servlet解耦,使控制器的逻辑与view分离,降低开发难度。

    1. Spring容器启动

    通常如果我们希望通过注解的方式来进行Spring MVC开发,会在***-servlet.xml中加入<mvc:annotation-driven/>标签来告诉Spring我们的目的。
    该标签被org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser解析,向Spring容器中注册了以下8个Bean实例。

    注册的3类8个实例
    • 1.1 RequestMappingHandlerMapping对象创建

    RequestMappingHandlerMapping实现了InitializingBean接口,在Spring创建RequestMappingHandlerMapping对象实例之后,调用初始化方法时,会调用其afterPropertiesSet()方法。

    protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
                throws Throwable {
    
            boolean isInitializingBean = (bean instanceof InitializingBean);
            if (isInitializingBean && 
                    (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
                if (System.getSecurityManager() != null) {
                  //....
                }                
                else {//调用InitializingBean的afterPropertiesSet方法。
                    ((InitializingBean) bean).afterPropertiesSet();
                }
            }
            //调用自定义初始化方法。。。省略,不关心
        }
    

    afterPropertiesSet()中直接调用initHandlerMethods()方法,去遍历beanDefinitionName中注册的bean, 判断是否被@Controller注解,如果是,则检查被标注为@RequestMapping的方法,创建RequestMappingInfo对象并将RequestMappingInfo:处理器方法(controller中的对应方法)注册到HandlerMapping。同时将url:RequestMappingInfo注册到HandlerMapping。 当获取到请求url时,通过url找到mapping,然后通过mapping找到method。

    public void afterPropertiesSet() {
            initHandlerMethods();
        }
    
        //Scan beans in the ApplicationContext, detect and register handler methods.
        protected void initHandlerMethods() {
            //扫描所有注册的Bean
            String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
               BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), 
                    Object.class) : getApplicationContext().getBeanNamesForType(Object.class));
            //遍历这些Bean,依次判断是否是处理器,并检测其HandlerMethod
            for (String beanName : beanNames) {
                if (isHandler(getApplicationContext().getType(beanName))){
                    detectHandlerMethods(beanName);
                }
            }
            //这个方法是个空实现,不管他
            handlerMethodsInitialized(getHandlerMethods());
        }
    

    2. 请求处理流程

    DispatcherServlet接收到请求doDispatch开始说起。

    请求处理流程 一图胜千言
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            // ... 省略
            try {
                try {
                    ModelAndView mv = null;
                    Object dispatchException = null;
    
                    try {
                        processedRequest = this.checkMultipart(request);
                        multipartRequestParsed = processedRequest != request;
                        // 获取拦截器链和handler
                        mappedHandler = this.getHandler(processedRequest);
                        // ... 省略诸多代码 
                        // 遍历interceptors, 调用interceptor.postHandle()                  
                        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                            return;
                        }
                        // 由于需要支持众多第三方的handler,如:servlet, controller, HttpRequestHandler
                        // 不能去修改人家的源码,要实现灵活的支持就可以考虑使用适配器模式
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                        // ... 省略诸多代码
                        // 遍历interceptors, 调用interceptor.postHandle()
                        mappedHandler.applyPostHandle(processedRequest, response, mv);
                    } catch (Exception var20) {
                        dispatchException = var20;
                    } catch (Throwable var21) {
                        dispatchException = new NestedServletException("Handler dispatch failed", var21);
                    }
                    // 捕获异常并处理
                    this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
                } catch (Exception var22) {
                    // 遍历interceptors, 调用interceptor.afterCompletion()
                    this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
                } catch (Throwable var23) {
                    this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
                }
    
            } finally {
               // ...
            }
        }
    

    3. 拦截器 原理与实现

    • 3.1 拦截器的实现

    实现HandlerInterceptor接口

    public class AuthInterceptor implements HandlerInterceptor {
        //...
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        }
    
        @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        }
    
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        }
    }
    
    • 3.2 注册拦截器

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Autowired
        private TimeInterceptor timeInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(timeInterceptor).excludePattern("pattern");
        }
    }
    
    • 3.3 拦截器是如何生效的

    1. 向容器中创建RequestMappingHandlerMapping对象时,通过WebMvcConfigurationSupport::requestMappingHandlerMapping()方法实现,对mapping注册拦截器,然后获取WebMvcConfigurer对象,执行其addInterceptors()方法,将拦截器注册。
    2. DispatcherServlet.doDispatch()方法中getHandler()会根据request对象从HandlerMapping获取拦截器链和HanderMethod。
    3. dispatcherServlet对象的applyPreHandle()等方法调用拦截器的pre/postHandle等方法。
        @Bean
        public RequestMappingHandlerMapping requestMappingHandlerMapping() {
            RequestMappingHandlerMapping mapping = this.createRequestMappingHandlerMapping();
            //...
            mapping.setInterceptors(this.getInterceptors());
            // ...
        }
    

    4. 异常处理机制

    • 4.1 定义异常处理方式

    1. @ExceptionHandler在特定Controller上处理(not recommend)
    2. 实现HandlerExceptionResolver接口完成全局异常处理
    3. @ControllerAdvice+@ExceptionHandler全局异常处理(recommend)
      这三种方式实现起来都比较简单,就不举例了。
    • 4.2 解析异常处理advice

    第一张图提到SpringMVC向容器注入ExceptionHandlerExceptionResolver,这个类同样实现了InitializingBean接口,在afterpropertiesSet()方法中,initExceptionhandlerAdviceCache()时,从容器中获取被标注为ControllerAdvice的bean,创建ExceptionHandlerMethodResolver并缓存起来。

        public void afterPropertiesSet() {
    
            //从容器中获取被标注为ControllerAdvice的bean,创建ExceptionHandlerMethodResolver并缓存
            this.initExceptionHandlerAdviceCache();
            List handlers;
            if (this.argumentResolvers == null) {
                handlers = this.getDefaultArgumentResolvers();
                this.argumentResolvers = (new HandlerMethodArgumentResolverComposite()).addResolvers(handlers);
            }
    
            if (this.returnValueHandlers == null) {
                handlers = this.getDefaultReturnValueHandlers();
                this.returnValueHandlers = (new HandlerMethodReturnValueHandlerComposite()).addHandlers(handlers);
            }
    
        }
    
        private void initExceptionHandlerAdviceCache() {
            if (this.getApplicationContext() != null) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Looking for exception mappings: " + this.getApplicationContext());
                }
    
                List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(this.getApplicationContext());
                AnnotationAwareOrderComparator.sort(adviceBeans);
                Iterator var2 = adviceBeans.iterator();
    
                while(var2.hasNext()) {
                    ControllerAdviceBean adviceBean = (ControllerAdviceBean)var2.next();
                    ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
                    if (resolver.hasExceptionMappings()) {
                        this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
                    }
    
                    if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {
                        this.responseBodyAdvice.add(adviceBean);
                    }
                }
    
            }
        }
    
    • 4.3 异常处理流程

    doDispatch()方法中,当HandlerMethod抛出异常时,异常被catch,然后作为参数传入processDispatchResult()方法

                    try {
                        processedRequest = this.checkMultipart(request);
                        multipartRequestParsed = processedRequest != request;
                        // 获取拦截器链和handler
                        mappedHandler = this.getHandler(processedRequest);
                        // ... 省略诸多代码 
                        // 遍历interceptors, 调用interceptor.postHandle()                  
                        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                            return;
                        }
    
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                        // ... 省略诸多代码
                        // 遍历interceptors, 调用interceptor.postHandle()
                        mappedHandler.applyPostHandle(processedRequest, response, mv);
                    } catch (Exception var20) {
                        dispatchException = var20;
                    } catch (Throwable var21) {
                        dispatchException = new NestedServletException("Handler dispatch failed", var21);
                    }
                    // 捕获异常并处理
                    this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
    

    processDispatchResult()方法通过processHandlerException完成异常处理,逻辑是:遍历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) {
                    mv = ((ModelAndViewDefiningException)exception).getModelAndView();
                } else {
                    Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                    mv = this.processHandlerException(request, response, handler, exception);
                    errorView = mv != null;
                }
            }
        }
    
        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;
                }
            }
        // ...
        }
    

    每个resolver的解决思路是:判断当前resolver是否适用于该handler和request,适用则进行解析。

        public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
            if (this.shouldApplyTo(request, handler)) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
                }
    
                this.prepareResponse(ex, response);
                ModelAndView result = this.doResolveException(request, response, handler, ex);
                if (result != null) {
                    this.logException(ex, request);
                }
    
                return result;
            } else {
                return null;
            }
        }
    

    doResolveException()根据当前handler对象和exception对象去exceptionHandlerCache中找到对应的ExceptionHandlerMethodResolver,拿到要被调用的Method对象,然后完成该方法的调用,返回modelAndView.

        protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
            if (handler != null) {
                Method handlerMethod = this.findBestExceptionHandlerMethod(handler, ex);
                if (handlerMethod != null) {
                    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    
                    try {
                        Object[] args = this.resolveHandlerArguments(handlerMethod, handler, webRequest, ex);
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Invoking request handler method: " + handlerMethod);
                        }
    
                        Object retVal = this.doInvokeMethod(handlerMethod, handler, args);
                        return this.getModelAndView(handlerMethod, retVal, webRequest);
                    } catch (Exception var9) {
                        this.logger.error("Invoking request method resulted in exception : " + handlerMethod, var9);
                    }
                }
            }
    
            return null;
        }
    

    整体流程简单来说就是:
    1.在启动过程中扫描ExceptionHandlerExceptionResolver接口的实现和被@ControllerAdvice注解的Bean,创建ExceptioResolver对象。
    2.捕获到controller中某方法的异常,遍历所有的ExceptioResolver,根据出现异常的方法handler和exception从异常处理Bean的缓存中找到相对应的methodResolver,解析出与(handler和exception)相对应的异常处理方法,并调用该方法对象完成异常处理。
    参考:
    SpringMVC解读系列

    相关文章

      网友评论

        本文标题:SpringMVC

        本文链接:https://www.haomeiwen.com/subject/ueekwltx.html