/** * ServletContainerInitializers (SCIs) are registered via an entry in the * file META-INF/services/javax.servlet.ServletContainerInitializer that must be * included in the JAR file that contains the SCI implementation. * <p> * SCI processing is performed regardless of the setting of metadata-complete. * SCI processing can be controlled per JAR file via fragment ordering. If * absolute ordering is defined, then only the JARs included in the ordering * will be processed for SCIs. To disable SCI processing completely, an empty * absolute ordering may be defined. * <p> * SCIs register an interest in annotations (class, method or field) and/or * types via the {@link javax.servlet.annotation.HandlesTypes} annotation which * is added to the class. * * @since Servlet 3.0 */publicinterfaceServletContainerInitializer{/** * Receives notification during startup of a web application of the classes * within the web application that matched the criteria defined via the * {@link javax.servlet.annotation.HandlesTypes} annotation. * * @param c The (possibly null) set of classes that met the specified * criteria * @param ctx The ServletContext of the web application in which the * classes were discovered * * @throws ServletException If an error occurs */voidonStartup(Set<Class<?>>c,ServletContextctx)throwsServletException;}
实现了 ServletContainerInitializer 接口的类必须在一个 JAR 包中的 META-INF/services/javax.servlet.ServletContainerInitializer 文件中进行注册。该文件的内容应该包含实现类的全限定名,例如:
Servlet 3.0+ 容器会自动去扫描 classpath 下所有实现了 WebApplicationInitializer 接口的类,如果没有找到相关类,那么 An INFO-level log message will be issued notifying the user,会有一条INFO级别 的日志。
@OverridepublicvoidonStartup(@NullableSet<Class<?>>webAppInitializerClasses,ServletContextservletContext)throwsServletException{List<WebApplicationInitializer>initializers=Collections.emptyList();if(webAppInitializerClasses!=null){initializers=newArrayList<>(webAppInitializerClasses.size());for(Class<?>waiClass:webAppInitializerClasses){// Be defensive: Some servlet containers provide us with invalid classes,// no matter what @HandlesTypes says...if(!waiClass.isInterface()&&!Modifier.isAbstract(waiClass.getModifiers())&&WebApplicationInitializer.class.isAssignableFrom(waiClass)){try{initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass).newInstance());}catch(Throwableex){thrownewServletException("Failed to instantiate WebApplicationInitializer class",ex);}}}}if(initializers.isEmpty()){servletContext.log("No Spring WebApplicationInitializer types detected on classpath");return;}servletContext.log(initializers.size()+" Spring WebApplicationInitializers detected on classpath");AnnotationAwareOrderComparator.sort(initializers);for(WebApplicationInitializerinitializer:initializers){initializer.onStartup(servletContext);}}
@Overridepublicfinalvoidinit()throwsServletException{// 源码里面设置了 ServletConfigPropertyValues// 暂时不做实现// Let subclasses do whatever initialization they like.initServletBean();}
@OverrideprotectedvoidinitServletBean()throwsServletException{logger.info("Initializing Spring "+getClass().getSimpleName()+" '"+getServletName()+"'");logger.info("Initializing Servlet '"+getServletName()+"'");longstartTime=System.currentTimeMillis();// 初始化 Web IOC 容器this.webApplicationContext=initWebApplicationContext();// 默认空实现,且无子类重写initFrameworkServlet();logger.info("Completed initialization in "+(System.currentTimeMillis()-startTime)+" ms");}
默认的策略由DispatcherServlet.properties决定,在 Spring 源码中是BeanNameUrlHandlerMapping、RequestMappingHandlerMapping、RouterFunctionMapping,而 Simple-Spring 中为RequestMappingHandlerMapping。
/** * 与 DispatcherServlet 类相关的类路径资源的名称,用于定义 DispatcherServlet 的默认策略名称 * 注意:路径应保持一致 */privatestaticfinalStringDEFAULT_STRATEGIES_PATH="DispatcherServlet.properties";protected<T>List<T>getDefaultStrategies(ApplicationContextcontext,Class<T>strategyInterface){if(defaultStrategies==null){try{// Load default strategy implementations from properties file.// This is currently strictly internal and not meant to be customized// by application developers.ClassPathResourceresource=newClassPathResource(DEFAULT_STRATEGIES_PATH,DispatcherServlet.class);defaultStrategies=PropertiesLoaderUtils.loadProperties(resource);}catch(IOExceptionex){thrownewIllegalStateException("Could not load '"+DEFAULT_STRATEGIES_PATH+"': "+ex.getMessage());}}Stringkey=strategyInterface.getName();Stringvalue=defaultStrategies.getProperty(key);if(value!=null){String[]classNames=StringUtils.commaDelimitedListToStringArray(value);List<T>strategies=newArrayList<>(classNames.length);for(StringclassName:classNames){try{Class<?>clazz=ClassUtils.forName(className,DispatcherServlet.class.getClassLoader());Objectstrategy=createDefaultStrategy(context,clazz);strategies.add((T)strategy);}catch(ClassNotFoundExceptionex){thrownewBeanInitializationException("Could not find DispatcherServlet's default strategy class ["+className+"] for interface ["+key+"]",ex);}catch(LinkageErrorerr){thrownewBeanInitializationException("Unresolvable class definition for DispatcherServlet's default strategy class ["+className+"] for interface ["+key+"]",err);}}returnstrategies;}else{returnCollections.emptyList();}}protectedObjectcreateDefaultStrategy(ApplicationContextcontext,Class<?>clazz){returncontext.getAutowireCapableBeanFactory().createBean(clazz);}
/** * Delegate GET requests to processRequest/doService. * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead}, * with a {@code NoBodyResponse} that just captures the content length. * @see #doHead */@OverrideprotectedfinalvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{processRequest(request,response);}
/** * 暴露特定于 DispatcherServlet 的请求属性,并将实际的请求分派委托给 {@link #doDispatch} 方法 */@OverrideprotectedvoiddoService(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{// 日志记录,请求包含支持,添加框架级对象,解析闪存信息,路径解析/* 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); } */// 设置 Web 应用上下文 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,getWebApplicationContext());// 核心请求分发逻辑 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); } } */}
/** * Return the HandlerExecutionChain for this request. * <p>Tries all handler mappings in order. * * @param request current HTTP request * @return the HandlerExecutionChain, or {@code null} if no handler could be found */@NullableprotectedHandlerExecutionChaingetHandler(HttpServletRequestrequest)throwsException{if(this.handlerMappings!=null){for(HandlerMappingmapping:this.handlerMappings){HandlerExecutionChainhandler=mapping.getHandler(request);if(handler!=null){returnhandler;}}}returnnull;}
/** * Return the HandlerAdapter for this handler object. * * @param handler the handler object to find an adapter for * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error. */protectedHandlerAdaptergetHandlerAdapter(HandlerMethodhandler)throwsServletException{if(this.handlerAdapters!=null){for(HandlerAdapteradapter:this.handlerAdapters){if(adapter.support(handler)){returnadapter;}}}thrownewServletException("No adapter for handler ["+handler+"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");}