前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringMvc简单梳理

SpringMvc简单梳理

原创
作者头像
eeaters
修改2021-11-19 16:34:02
3550
修改2021-11-19 16:34:02
举报
文章被收录于专栏:阿杰阿杰

刚开始接触springmvc的时候就听说过一个核心的类: DispatchServlet , 现在以spring-webmvc为入口探索下springmvc的核心类及流程,

源码搞了一半的时候因为工作原因直接忙了两天工作事情,导致后面继续看源码有点看不进去了;但是好在主流程已经看完了

版本

spring-boot-starter-parent: 2.1.17.RELEASE

结论

后续针对主流程进行梳理时和网上的图基本保持了一致,因此直接引用百度百科上的图片

百度百科上的图
百度百科上的图

结论如下:

  1. 简单的梳理:项目启动时将所有映射保存起来,请求进来后会在映射关系中进行匹配然后执行一系列的拦截链后执行方法.
  2. DispatcherServlet是Servlet的实现类; 是请求的总入口;对请求进行调度管理
  3. 服务启动时tomcat会调用init(ServletContext)对servlet进行初始化, 因为ApplicationContext是成员变量, 所以可以看DispatchServlet#onRefresh方法可以看到依赖的其他对象(其实就是context.getBean())
  4. ModelFactory内部sessionAttributesHandler对应的ControllerAdvice, dataBinderFactory对应的额是@InitBinder,而HandlerInterceptor位于DispatcherServlet中;
  5. AbstractHandlerMapping在handlerMapping上实现了Ordered接口; 如果出现一个请求在多个mapping中有映射; 那么匹配到首个handlerMapping将先返回
  6. 内容协商的意思和上面的类似; 所有的ViewResolver都会ContentNegotiatingViewResolver包装起来对外提供能力; 然后在内部也是进行匹配; 先匹配到的就返回这个视图; 所以内容协商的优先级和加载顺序息息相关;

autoconfig

spring-mvc的autoconfig直接在spring-boot-autoconfigure的spring.factories中查找 以org.springframework.boot.autoconfigure.web为前缀的就是web相关的装配类, 这时候敏锐的嗅觉让我找到了这两个装配类

  • DispatcherServletAutoConfiguration
  • WebMvcAutoConfiguration

当我们点击到WebMvcAutoConfiguration中后可以看到, 该类的加载需要在DispatcherServletAutoConfiguration之后, 姑且认为我们本次源码的入口为

WebMvcAutoConfiguration吧

WebMvcAutoConfiguration

AutoConfigureBefore

这部分最主要的点是: 引入了嵌入式容器/创建了DispatchServlet, 使用流程图进行简单的梳理

代码语言:txt
复制
BeanPostProcessorsRegistrarWebMvcAutoConfigurationDispatcherServletAutoConfigurationDispatcherServletConfigurationDispatcherServlet-核心类,后面分析MultipartResolverServletWebServerFactoryAutoConfigurationEmbeddedTomcat-嵌入式容器EmbeddedJetty-嵌入式容器EmbeddedUndertow-嵌入式容器TaskExecutionAutoConfigurationTaskExecutorBuilder-builderThreadPoolTaskExecutor-任务线程池ValidationAutoConfigurationLocalValidatorFactoryBean-用javax.validation框架校验MethodValidationPostProcessor-一个BeanPostProcessorBeanPostProcessorsRegistrarWebServerFactoryCustomizerBeanPostProcessor容器初始化时可以配置化ErrorPageRegistrarBeanPostProcessor增加异常页面返回

效果图
效果图

WebMvcAutoConfigurationAdapter

WebMvcConfigurer的实现类, 也就是说该类除了引入一些bean之外 ,容器启动时会通过回调方式对容器进行配置化,

配置化包括: converts,taskExecutor,内容协商,拦截器, cors

引入Bean如下:

InternalResourceViewResolver和BeanNameViewResolver

视图解析器: ViewResolver的子类 ; 该类的核心功能用父类来表示再合适不过了; 这个类是支持国际化的,因此入参有Locale参数,

代码语言:txt
复制
/**
 * Interface to be implemented by objects that can resolve views by name.
 *
 * <p>View state doesn't change during the running of the application,
 * so implementations are free to cache views.
 *
 * <p>Implementations are encouraged to support internationalization,
 * i.e. localized view resolution.
 */
public interface ViewResolver {

    /**
     * Resolve the given view by name.
     * <p>Note: To allow for ViewResolver chaining, a ViewResolver should
     * return {@code null} if a view with the given name is not defined in it.
     * However, this is not required: Some ViewResolvers will always attempt
     * to build View objects with the given name, unable to return {@code null}
     * (rather throwing an exception when View creation failed).
     * @param viewName name of the view to resolve
     * @param locale the Locale in which to resolve the view.
     * ViewResolvers that support internationalization should respect this.
     * @return the View object, or {@code null} if not found
     * (optional, to allow for ViewResolver chaining)
     * @throws Exception if the view cannot be resolved
     * (typically in case of problems creating an actual View object)
     */
    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;

}

ContentNegotiatingViewResolver

上面已经有了两个实体解析器, 这里单独把这个解析器拎出来原因是这个类是所有实体解析器的门面, 该类翻译一下即: 内容协商

比如jsp,thymleaf等, 这时候就是不同的解析器, 所有视图都会被包装在该类中; 请求时进行内容协商(个人理解为for-each, 如果真的出现一个请求对应了两个以上的视图解析器,那么就看谁排在前面了)

LocaleResolver

时区解析器,默认创建的是 AcceptHeaderLocaleResolver; 也就是说通过http的header中来获取时区信息

OrderedRequestContextFilter

这个类特别简单好像没什么东西一样; 其实就是为了注入RequestContextFilter,但是又想搞一个order用来排序, 核心就是RequestContextFilter

代码如下; RequestContextFilter是将ServletRequestAttributes放入本地线程变量中; DispatcherServlet也有相同功能;不过这个类适用的场景应该比较大 ; 而如果是普通的请求则里面if条件也会进行判断

代码语言:txt
复制
public class OrderedRequestContextFilter extends RequestContextFilter implements OrderedFilter {
    private int order = REQUEST_WRAPPER_FILTER_MAX_ORDER - 105;
    @Override
    public int getOrder() {
        return this.order;
    }
    public void setOrder(int order) {
        this.order = order;
    }
}
public class RequestContextFilter extends OncePerRequestFilter {
    

    @Override
    protected void doFilterInternal(
            HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
        initContextHolders(request, attributes);

        try {
            filterChain.doFilter(request, response);
        }
        finally {
            resetContextHolders();
            if (logger.isTraceEnabled()) {
                logger.trace("Cleared thread-bound request context: " + request);
            }
            attributes.requestCompleted();
        }
    }

    private void initContextHolders(HttpServletRequest request, ServletRequestAttributes requestAttributes) {
        LocaleContextHolder.setLocale(request.getLocale(), this.threadContextInheritable);
        RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
        if (logger.isTraceEnabled()) {
            logger.trace("Bound request context to thread: " + request);
        }
    }
}

FaviconConfiguration

配置图标的,比如百度页面左上角有一个猫爪,忽略

EnableWebMvcConfiguration

该配置类的继承关系是: EnableWebMvcConfiguration-->DelegatingWebMvcConfiguration --> WebMvcConfigurationSupport,先进行本类的分析

RequestMappingHandlerAdapter

HandlerAdapter的实现类, 肩负着执行实际的方法和执行前后的切面等事项, 在DispatchServlet中会调用该方法

代码语言:txt
复制
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean {
    private static final boolean shouldIgnoreXml = SpringProperties.getFlag("spring.xml.ignore");

    /**
     * MethodFilter that matches {@link InitBinder @InitBinder} methods.
     */
    public static final MethodFilter INIT_BINDER_METHODS = method ->
            AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);

    /**
     * MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods.
     */
    public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
            (!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
                    AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));

    //自定义的入参解析器
    @Nullable
    private List<HandlerMethodArgumentResolver> customArgumentResolvers;

    //所有的参数解析器的包装类,  {@link getDefaultArgumentResolvers()}方法中可以看到默认添加的二十多个解析器,自定义的也会放到里面
    @Nullable
    private HandlerMethodArgumentResolverComposite argumentResolvers;

    //initBinder是用于执行方法前的前置方法, 个人理解为test模块的 @Before注解的方法,
    @Nullable
    private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;

    //同customArgumentResolvers,这个是处理返回值的
    @Nullable
    private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;

    //同HandlerMethodArgumentResolverComposite, 这个是返回解析器的包装
    @Nullable
    private HandlerMethodReturnValueHandlerComposite returnValueHandlers;

    //通过接口的注释可以知道典型的实现是检测到指定的返回类型就直接将一个已知的结果返回出去
    @Nullable
    private List<ModelAndViewResolver> modelAndViewResolvers;

    //内容协商, 前面有写到过
    private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
    
    //转化器, 项目中经常见到Jackson的Convert和Fastjson的convert, 应该不会太陌生
    private List<HttpMessageConverter<?>> messageConverters;

    //类实现了InitialBean,所以初始化会调用afterPropertiesSet方法,然后调用initControllerAdviceCache进行初始化
    //扫描@ControllerAdvice的类, 作用不言而喻, 是全局异常拦截处理的方式之一 ,也可配合InitBinder进行请求初始化
    private final List<Object> requestResponseBodyAdvice = new ArrayList<>();

    //用于数据绑定; {@link WebMvcConfigurationSupport#getConfigurableWebBindingInitializer}这里进行初始化并set进来
    //里面放置类ConversionService转化服务和 validator 绑定器来实现功能
    @Nullable
    private WebBindingInitializer webBindingInitializer;

    //异步执行时的任务线程池
    private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync");

    @Nullable
    private Long asyncRequestTimeout;

     //{@see AsyncSupportConfigurer} 异步执行的拦截器
    private CallableProcessingInterceptor[] callableInterceptors = new CallableProcessingInterceptor[0];
    
    //{@see AsyncSupportConfigurer} 延时返回执行结果的拦截器
    private DeferredResultProcessingInterceptor[] deferredResultInterceptors = new DeferredResultProcessingInterceptor[0];

    //响应式适配器的注册中心, 如java9+的Flow,rxjava 2/3,reactor 等的桥接
    private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();

    private boolean ignoreDefaultModelOnRedirect = false;

    private int cacheSecondsForSessionAttributeHandlers = 0;

    private boolean synchronizeOnSession = false;

    //session属性的存储
    private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();

    //用于获取方法或者构造器上的参数
    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    @Nullable
    private ConfigurableBeanFactory beanFactory;

    //下面五个均是缓存,首次通过反射获取到想要的信息后放置到map中,提升后续请求的性能
    private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64);

    private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64);

    private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();

    private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64);

    private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>();
    
    //DispatchServlet进行视图解析后,将数据的处理转到该类中执行; 调用的handle方法直接传到该方法中执行
    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ModelAndView mav;
        checkRequest(request);
        
        //这里可以获取到一个线索, springmvc可以通过一个设置来实现操作的互斥,默认是false,不过无法实现分布式场景下的互斥
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // No HttpSession available -> no mutex necessary
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No synchronization on session demanded at all...
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                prepareResponse(response);
            }
        }

        return mav;
    }
    
    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        //对request+response进行封装, 提供便利性的操作
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            //initBinder方法,类似于test模块的@Before注解标注的方法,会在执行controller方法前先执行@InitBinder方法
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            //通过反射执行方法的处理方法,后面通过invokeAndHandle来实际执行
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if (this.returnValueHandlers != null) {
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            invocableMethod.setDataBinderFactory(binderFactory);
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);

            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

            if (asyncManager.hasConcurrentResult()) {
                Object result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(logger, traceOn -> {
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }
            
             //实际执行,因为webRequest中包含了request和response,所以不需要返回值,这里执行后response中存在了返回内容
            //执行包括 参数解析 -> 执行 -> 结果对象处理 三步
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }
            //ModelAndView 包含了 viewName+status+model
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }
    
}

RequestMappingHandlerMapping

bean创建的代码, 该bean主要是负责对请求进行映射, 内部存放了内容协商、url匹配器等信息,

代码语言:txt
复制
    @Bean
    @SuppressWarnings("deprecation")
    public RequestMappingHandlerMapping requestMappingHandlerMapping(
            @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
            @Qualifier("mvcConversionService") FormattingConversionService conversionService,
            @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

        RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
        mapping.setOrder(0);
        mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
        mapping.setContentNegotiationManager(contentNegotiationManager);
        mapping.setCorsConfigurations(getCorsConfigurations());

        PathMatchConfigurer pathConfig = getPathMatchConfigurer();
        if (pathConfig.getPatternParser() != null) {
            mapping.setPatternParser(pathConfig.getPatternParser());
        }
        else {
            mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
            mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());

            Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
            if (useSuffixPatternMatch != null) {
                mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
            }
            Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
            if (useRegisteredSuffixPatternMatch != null) {
                mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
            }
        }
        Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
        if (useTrailingSlashMatch != null) {
            mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
        }
        if (pathConfig.getPathPrefixes() != null) {
            mapping.setPathPrefixes(pathConfig.getPathPrefixes());
        }

        return mapping;
    }

实际工作需要执行的方法

代码语言:txt
复制
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    
    
    @Nullable
    private Object defaultHandler;

    @Nullable
    private PathPatternParser patternParser;

    private UrlPathHelper urlPathHelper = new UrlPathHelper();

    private PathMatcher pathMatcher = new AntPathMatcher();

    private final List<Object> interceptors = new ArrayList<>();

    private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
    @Nullable
    private HandlerMethodMappingNamingStrategy<T> namingStrategy;

    private final MappingRegistry mappingRegistry = new MappingRegistry();
    
    //父类:AbstractHandlerMapping的方法,dispatcherservlet通过这里来获取到要执行的方法链
    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //下面有分析; 对于通常用的controller来说; 这个类的实现类是AbstractHandlerMethodMapping,所以返回的是HandlerMethod
        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 = obtainApplicationContext().getBean(handlerName);
        }

        // Ensure presence of cached lookupPath for interceptors and others
        if (!ServletRequestPathUtils.hasCachedPath(request)) {
            initLookupPath(request);
        }

        //将interceptors都放进去
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

        if (logger.isTraceEnabled()) {
            logger.trace("Mapped to " + handler);
        }
        else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
            logger.debug("Mapped to " + executionChain.getHandler());
        }
        //无视; 这是cors跨域资源; 先不做探究
        if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
            CorsConfiguration config = getCorsConfiguration(handler, request);
            if (getCorsConfigurationSource() != null) {
                CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
                config = (globalConfig != null ? globalConfig.combine(config) : config);
            }
            if (config != null) {
                config.validateAllowCredentials();
            }
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }

        return executionChain;
    }


    @Override
    @Nullable
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        //通过UrlPathHelper对request进行分析; 获取到实际的请求路径
        String lookupPath = initLookupPath(request);
        this.mappingRegistry.acquireReadLock();
        try {
            //重点来了; 这里通过启动时注册好的映射关系,获取到要执行的Handler,等待后续通过反射来调用,
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
            this.mappingRegistry.releaseReadLock();
        }
    }
    
}

其他类...略

DispatchServlet

该类是在 DispatcherServletConfiguration中进行声明的bean ; 放到最后分析并将前面的类进行归并

前置说明: 该类的父类是HttpServlet; 而doGet/doPost等基本都调用processRequest方法,processReqeust会调用doService方法最终流转到doDispatch方法中

下面就这三个方法进行简单的分析

代码语言:txt
复制
public class DispatcherServlet extends FrameworkServlet {

    @Nullable
    private MultipartResolver multipartResolver;

    /** LocaleResolver used by this servlet. */
    @Nullable
    private LocaleResolver localeResolver;

    /** ThemeResolver used by this servlet. */
    @Nullable
    private ThemeResolver themeResolver;

    /** List of HandlerMappings used by this servlet. */
    @Nullable
    private List<HandlerMapping> handlerMappings;

    /** List of HandlerAdapters used by this servlet. */
    @Nullable
    private List<HandlerAdapter> handlerAdapters;

    /** List of HandlerExceptionResolvers used by this servlet. */
    @Nullable
    private List<HandlerExceptionResolver> handlerExceptionResolvers;

    /** RequestToViewNameTranslator used by this servlet. */
    @Nullable
    private RequestToViewNameTranslator viewNameTranslator;

    /** FlashMapManager used by this servlet. */
    @Nullable
    private FlashMapManager flashMapManager;

    /** List of ViewResolvers used by this servlet. */
    @Nullable
    private List<ViewResolver> viewResolvers;
    
    
    
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = buildLocaleContext(request);

        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
        
        //注意这里是将LocalContext和request放置到了本地线程变量中; 用过的都对上面的类有一定的了解
        initContextHolders(request, localeContext, requestAttributes);

        try {
            doService(request, response);
        }
        catch (ServletException | IOException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }

        finally {
            //使用本地线程变量一定要在finally块中激进型响应的处理
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }
            logResult(request, response, failureCause, asyncManager);
            //发送完成事件
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logRequest(request);

        // Keep a snapshot of the request attributes in case of an include,
        // to be able to restore the original attributes after the include.
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }

        // Make framework objects available to handlers and view objects.
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

        if (this.flashMapManager != null) {
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }

        RequestPath previousRequestPath = null;
        if (this.parseRequestPath) {
            previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
            ServletRequestPathUtils.parseAndCache(request);
        }

        try {
            doDispatch(request, response);
        }
        finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Restore the original attribute snapshot, in case of an include.
                if (attributesSnapshot != null) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
            if (this.parseRequestPath) {
                ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
            }
        }
    }
    
    //这里为DispatchServlet中的核心方法,网上springmvc的12个步骤均在这个方法中可以找到
    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 {
                //多媒体请求时会进行转化,如果转化了,那么下面的判断就为true
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                //比如:Controller 和 JMX整合的web请求 会生成不同的handlerMappings ,这里遍历进行分析; 具体逻辑参考ReqeustMappingHanderMapping
                //虽然只是一个简单的getHandler,但是内部缺已经通过视图解析器解析到了要执行的handler,并且将拦截链加进去了
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                //handlerAdpater是实际调用方法执行的类; 上文已经有了要执行的目标方法了,
                //这个不止是执行方法; 并且也包含了方法实际执行和视图解析等操作了
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = HttpMethod.GET.matches(method);
                if (isGet || HttpMethod.HEAD.matches(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                //前置处理; mappedHandler内的interceptors(主要是iniBinder和@ControllerAdvice)不同; 这里是web的统一拦截,经常会在用户登录信息校验中碰到这个类的使用, 还有日志跟踪的tid等
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                //英文说的很清晰了,终于执行方法了; 通过反射调用后还会解析为视图对象
                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                //后置处理; 还是和上面一样的interceptors; 但是里面有前置和后置两个方法
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            //对结果进行处理; 比如我们在aop中对结果或异常进行处理,内容协商的reder方法就在这里执行
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", 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);
                }
            }
        }
    }

    
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 版本
  • 结论
  • autoconfig
  • WebMvcAutoConfiguration
    • AutoConfigureBefore
      • WebMvcAutoConfigurationAdapter
        • InternalResourceViewResolver和BeanNameViewResolver
        • ContentNegotiatingViewResolver
        • LocaleResolver
        • OrderedRequestContextFilter
      • FaviconConfiguration
        • EnableWebMvcConfiguration
          • RequestMappingHandlerAdapter
          • RequestMappingHandlerMapping
          • 其他类...略
        • DispatchServlet
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档