/** * Process the actual dispatching to the handler. * <p>The handler will be obtained by applying the servlet's HandlerMappings in order. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters * to find the first that supports the handler class. * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers * themselves to decide which methods are acceptable. * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure */ protectedvoiddoDispatch(HttpServletRequest request, HttpServletResponse response)throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false;
// Determine handler for the current request. // 根据请求的url,获取与之匹配的handler,并将其与拦截器(HandlerInterceptor)列表一起包装为HandlerExecutionChain返回 // 这里的handler可能是HandlerMethod,可能是Controller,也可能是HttpRequestHandler或Servlet 对象 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; }
// Determine handler adapter for the current request. // 将不同类型的handler统一转换成HandlerAdapter,以便统一调用 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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } }
// 遍历拦截器(HandlerInterceptor)列表,依次调用拦截器的前置处理方法(preHandle) // 如果其中有拦截器返回false,表示该拦截器已经处理了响应本身,这里就直接返回 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }
publicabstractclassFrameworkServletextendsHttpServletBeanimplementsApplicationContextAware{ ... /** * Called by Spring via {@link ApplicationContextAware} to inject the current * application context. This method allows FrameworkServlets to be registered as * Spring beans inside an existing {@link WebApplicationContext} rather than * {@link #findWebApplicationContext() finding} a * {@link org.springframework.web.context.ContextLoaderListener bootstrapped} context. * <p>Primarily added to support use in embedded servlet containers. * @since 4.0 */ @Override publicvoidsetApplicationContext(ApplicationContext applicationContext){ if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) { this.webApplicationContext = (WebApplicationContext) applicationContext; this.webApplicationContextInjected = true; } } /** * Overridden method of {@link HttpServletBean}, invoked after any bean properties * have been set. Creates this servlet's WebApplicationContext. */ @Override protectedfinalvoidinitServletBean()throws ServletException { getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } long startTime = System.currentTimeMillis(); try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); } if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } } /** * Initialize and publish the WebApplicationContext for this servlet. * <p>Delegates to {@link #createWebApplicationContext} for actual creation * of the context. Can be overridden in subclasses. * @return the WebApplicationContext instance * @see #FrameworkServlet(WebApplicationContext) * @see #setContextClass * @see #setContextConfigLocation */ protected WebApplicationContext initWebApplicationContext(){ WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { // 调用子类的onRefresh onRefresh(wac); } } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; } ... }
/** * Initialize the strategy objects that this servlet uses. * <p>May be overridden in subclasses in order to initialize further strategy objects. */ protectedvoidinitStrategies(ApplicationContext context){ initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); } ... }
/** * Callback that receives refresh events from this servlet's WebApplicationContext. * <p>The default implementation calls {@link #onRefresh}, * triggering a refresh of this servlet's context-dependent state. * @param event the incoming ApplicationContext event */ publicvoidonApplicationEvent(ContextRefreshedEvent event){ this.refreshEventReceived = true; synchronized (this.onRefreshMonitor) { onRefresh(event.getApplicationContext()); } }
...
/** * ApplicationListener endpoint that receives events from this servlet's WebApplicationContext * only, delegating to {@code onApplicationEvent} on the FrameworkServlet instance. */ privateclassContextRefreshListenerimplementsApplicationListener<ContextRefreshedEvent> {