java面试

SpringMVC

2021-09-03  本文已影响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个实例

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. 拦截器 原理与实现

实现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 {
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private TimeInterceptor timeInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(timeInterceptor).excludePattern("pattern");
    }
}
  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. 异常处理机制

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

第一张图提到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);
                }
            }

        }
    }

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解读系列

上一篇下一篇

猜你喜欢

热点阅读