什么是Servlet?

  1. Servlet是使用Java语言编写的运行在服务器端的程序。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet 主要用于处理客户端传来的 HTTP 请求,并返回一个响应,它能够处理的请求有doGet()和doPost()等方法
  2. Servlet由Servlet容器提供,所谓的Servlet容器是指提供了Servlet 功能的服务器(本书中指Tomcat),Servlet容器将Servlet动态地加载到服务器上。与HTTP 协议相关的Servlet使用HTTP请求和HTTP响应与客户端进行交互。因此,Servlet容器支持所有HTTP协议的请求和响应。Servlet应用程序的体系结构如图所示。<<javaWeb程序设计教程>>

什么是SpringMVC?

  1. Spring MVC一开始就定位于一个较为松散的组合,展示给用户的视图(View)、控制器返回的数据模型(Model)、定位视图的视图解析器(ViewResolver)和处理适配器(HandlerAdapter)等内容都是独立的。换句话说,通过Spring MVC很容易把后台的数据转换为各种类型的数据,以满足移动互联网数据多样化的要求。例如,Spring MVC可以十分方便地转换为目前最常用的JSON数据集,也可以转换为PDF、Excel和XML等。加之Spring MVC是基于Spring基础框架派生出来的Web框架,所以它天然就可以十分方便地整合到Spring框架中,而Spring整合Struts2还是比较繁复的.
  2. mvc架构设计:处理请求先到达控制器(Controller),控制器的作用是进行请求分发,这样它会根据请求的内容去访问模型层(Model);在现今互联网系统中,数据主要从数据库和NoSQL中来,而且对于数据库而言往往还存在事务的机制,为了适应这样的变化,设计者会把模型层再细分为两层,即服务层(Service)和数据访问层(DAO);当控制器获取到由模型层返回的数据后,就将数据渲染到视图中,这样就能够展现给用户了
  3. 图取自于<<深入浅出springboot2.x>>书籍

思考和疑问

早些年的时候,使用servlet开发web程序, 一般都是继承HttpServlet接口,请求访问时直接根据类名调用.但这样写的结果是,一个类只能处理一个请求.项目结构大概长这样

使用SpringMVC框架后,只需要配置简单的@RequestMapping("")就可以找到对应的方法,原来的servlet呢? 对springMVC的了解还是不够详细,所以

继续探讨以下几个问题

1.SpringMVC如何代替Servlet?

可以看到DispatcherServlet继承自HttpServlet,  前端控制器其实就相当于一个Servlet.

2. SpringMVC的工作流程?

图取自于<<深入浅出springboot2.x>>书籍

基于springboot开发使得SpringMVC的使用更为简便,可以通过Spring Boot的配置来定制这些组件的初始化

  1. /*
    * Copyright 2002-2019 the original author or authors.
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * https://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */ package org.springframework.web.servlet; import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Locale;
    import java.util.Map;
    import java.util.Properties;
    import java.util.Set;
    import java.util.stream.Collectors; import javax.servlet.DispatcherType;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactoryUtils;
    import org.springframework.beans.factory.BeanInitializationException;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.i18n.LocaleContext;
    import org.springframework.core.annotation.AnnotationAwareOrderComparator;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.support.PropertiesLoaderUtils;
    import org.springframework.core.log.LogFormatUtils;
    import org.springframework.http.server.ServletServerHttpRequest;
    import org.springframework.lang.Nullable;
    import org.springframework.ui.context.ThemeSource;
    import org.springframework.util.ClassUtils;
    import org.springframework.util.StringUtils;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.request.ServletWebRequest;
    import org.springframework.web.context.request.async.WebAsyncManager;
    import org.springframework.web.context.request.async.WebAsyncUtils;
    import org.springframework.web.multipart.MultipartException;
    import org.springframework.web.multipart.MultipartHttpServletRequest;
    import org.springframework.web.multipart.MultipartResolver;
    import org.springframework.web.util.NestedServletException;
    import org.springframework.web.util.WebUtils; /**
    * Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers
    * or HTTP-based remote service exporters. Dispatches to registered handlers for processing
    * a web request, providing convenient mapping and exception handling facilities.
    *
    * <p>This servlet is very flexible: It can be used with just about any workflow, with the
    * installation of the appropriate adapter classes. It offers the following functionality
    * that distinguishes it from other request-driven web MVC frameworks:
    *
    * <ul>
    * <li>It is based around a JavaBeans configuration mechanism.
    *
    * <li>It can use any {@link HandlerMapping} implementation - pre-built or provided as part
    * of an application - to control the routing of requests to handler objects. Default is
    * {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping} and
    * {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping}.
    * HandlerMapping objects can be defined as beans in the servlet's application context,
    * implementing the HandlerMapping interface, overriding the default HandlerMapping if
    * present. HandlerMappings can be given any bean name (they are tested by type).
    *
    * <li>It can use any {@link HandlerAdapter}; this allows for using any handler interface.
    * Default adapters are {@link org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter},
    * {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter}, for Spring's
    * {@link org.springframework.web.HttpRequestHandler} and
    * {@link org.springframework.web.servlet.mvc.Controller} interfaces, respectively. A default
    * {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter}
    * will be registered as well. HandlerAdapter objects can be added as beans in the
    * application context, overriding the default HandlerAdapters. Like HandlerMappings,
    * HandlerAdapters can be given any bean name (they are tested by type).
    *
    * <li>The dispatcher's exception resolution strategy can be specified via a
    * {@link HandlerExceptionResolver}, for example mapping certain exceptions to error pages.
    * Default are
    * {@link org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver},
    * {@link org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver}, and
    * {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver}.
    * These HandlerExceptionResolvers can be overridden through the application context.
    * HandlerExceptionResolver can be given any bean name (they are tested by type).
    *
    * <li>Its view resolution strategy can be specified via a {@link ViewResolver}
    * implementation, resolving symbolic view names into View objects. Default is
    * {@link org.springframework.web.servlet.view.InternalResourceViewResolver}.
    * ViewResolver objects can be added as beans in the application context, overriding the
    * default ViewResolver. ViewResolvers can be given any bean name (they are tested by type).
    *
    * <li>If a {@link View} or view name is not supplied by the user, then the configured
    * {@link RequestToViewNameTranslator} will translate the current request into a view name.
    * The corresponding bean name is "viewNameTranslator"; the default is
    * {@link org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator}.
    *
    * <li>The dispatcher's strategy for resolving multipart requests is determined by a
    * {@link org.springframework.web.multipart.MultipartResolver} implementation.
    * Implementations for Apache Commons FileUpload and Servlet 3 are included; the typical
    * choice is {@link org.springframework.web.multipart.commons.CommonsMultipartResolver}.
    * The MultipartResolver bean name is "multipartResolver"; default is none.
    *
    * <li>Its locale resolution strategy is determined by a {@link LocaleResolver}.
    * Out-of-the-box implementations work via HTTP accept header, cookie, or session.
    * The LocaleResolver bean name is "localeResolver"; default is
    * {@link org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver}.
    *
    * <li>Its theme resolution strategy is determined by a {@link ThemeResolver}.
    * Implementations for a fixed theme and for cookie and session storage are included.
    * The ThemeResolver bean name is "themeResolver"; default is
    * {@link org.springframework.web.servlet.theme.FixedThemeResolver}.
    * </ul>
    *
    * <p><b>NOTE: The {@code @RequestMapping} annotation will only be processed if a
    * corresponding {@code HandlerMapping} (for type-level annotations) and/or
    * {@code HandlerAdapter} (for method-level annotations) is present in the dispatcher.</b>
    * This is the case by default. However, if you are defining custom {@code HandlerMappings}
    * or {@code HandlerAdapters}, then you need to make sure that a corresponding custom
    * {@code RequestMappingHandlerMapping} and/or {@code RequestMappingHandlerAdapter}
    * is defined as well - provided that you intend to use {@code @RequestMapping}.
    *
    * <p><b>A web application can define any number of DispatcherServlets.</b>
    * Each servlet will operate in its own namespace, loading its own application context
    * with mappings, handlers, etc. Only the root application context as loaded by
    * {@link org.springframework.web.context.ContextLoaderListener}, if any, will be shared.
    *
    * <p>As of Spring 3.1, {@code DispatcherServlet} may now be injected with a web
    * application context, rather than creating its own internally. This is useful in Servlet
    * 3.0+ environments, which support programmatic registration of servlet instances.
    * See the {@link #DispatcherServlet(WebApplicationContext)} javadoc for details.
    *
    * @author Rod Johnson
    * @author Juergen Hoeller
    * @author Rob Harrop
    * @author Chris Beams
    * @author Rossen Stoyanchev
    * @see org.springframework.web.HttpRequestHandler
    * @see org.springframework.web.servlet.mvc.Controller
    * @see org.springframework.web.context.ContextLoaderListener
    */
    @SuppressWarnings("serial")
    public class DispatcherServlet extends FrameworkServlet { /** Well-known name for the MultipartResolver object in the bean factory for this namespace. */
    public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"; /** Well-known name for the LocaleResolver object in the bean factory for this namespace. */
    public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver"; /** Well-known name for the ThemeResolver object in the bean factory for this namespace. */
    public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver"; /**
    * Well-known name for the HandlerMapping object in the bean factory for this namespace.
    * Only used when "detectAllHandlerMappings" is turned off.
    * @see #setDetectAllHandlerMappings
    */
    public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping"; /**
    * Well-known name for the HandlerAdapter object in the bean factory for this namespace.
    * Only used when "detectAllHandlerAdapters" is turned off.
    * @see #setDetectAllHandlerAdapters
    */
    public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter"; /**
    * Well-known name for the HandlerExceptionResolver object in the bean factory for this namespace.
    * Only used when "detectAllHandlerExceptionResolvers" is turned off.
    * @see #setDetectAllHandlerExceptionResolvers
    */
    public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver"; /**
    * Well-known name for the RequestToViewNameTranslator object in the bean factory for this namespace.
    */
    public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator"; /**
    * Well-known name for the ViewResolver object in the bean factory for this namespace.
    * Only used when "detectAllViewResolvers" is turned off.
    * @see #setDetectAllViewResolvers
    */
    public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver"; /**
    * Well-known name for the FlashMapManager object in the bean factory for this namespace.
    */
    public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager"; /**
    * Request attribute to hold the current web application context.
    * Otherwise only the global web app context is obtainable by tags etc.
    * @see org.springframework.web.servlet.support.RequestContextUtils#findWebApplicationContext
    */
    public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT"; /**
    * Request attribute to hold the current LocaleResolver, retrievable by views.
    * @see org.springframework.web.servlet.support.RequestContextUtils#getLocaleResolver
    */
    public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER"; /**
    * Request attribute to hold the current ThemeResolver, retrievable by views.
    * @see org.springframework.web.servlet.support.RequestContextUtils#getThemeResolver
    */
    public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER"; /**
    * Request attribute to hold the current ThemeSource, retrievable by views.
    * @see org.springframework.web.servlet.support.RequestContextUtils#getThemeSource
    */
    public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE"; /**
    * Name of request attribute that holds a read-only {@code Map<String,?>}
    * with "input" flash attributes saved by a previous request, if any.
    * @see org.springframework.web.servlet.support.RequestContextUtils#getInputFlashMap(HttpServletRequest)
    */
    public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP"; /**
    * Name of request attribute that holds the "output" {@link FlashMap} with
    * attributes to save for a subsequent request.
    * @see org.springframework.web.servlet.support.RequestContextUtils#getOutputFlashMap(HttpServletRequest)
    */
    public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP"; /**
    * Name of request attribute that holds the {@link FlashMapManager}.
    * @see org.springframework.web.servlet.support.RequestContextUtils#getFlashMapManager(HttpServletRequest)
    */
    public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER"; /**
    * Name of request attribute that exposes an Exception resolved with a
    * {@link HandlerExceptionResolver} but where no view was rendered
    * (e.g. setting the status code).
    */
    public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION"; /** Log category to use when no mapped handler is found for a request. */
    public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; /**
    * Name of the class path resource (relative to the DispatcherServlet class)
    * that defines DispatcherServlet's default strategy names.
    */
    private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; /**
    * Common prefix that DispatcherServlet's default strategy attributes start with.
    */
    private static final String DEFAULT_STRATEGIES_PREFIX = "org.springframework.web.servlet"; /** Additional logger to use when no mapped handler is found for a request. */
    protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); private static final Properties defaultStrategies; static {
    // Load default strategy implementations from properties file.
    // This is currently strictly internal and not meant to be customized
    // by application developers.
    try {
    ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
    defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
    throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
    }
    } /** Detect all HandlerMappings or just expect "handlerMapping" bean?. */
    private boolean detectAllHandlerMappings = true; /** Detect all HandlerAdapters or just expect "handlerAdapter" bean?. */
    private boolean detectAllHandlerAdapters = true; /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean?. */
    private boolean detectAllHandlerExceptionResolvers = true; /** Detect all ViewResolvers or just expect "viewResolver" bean?. */
    private boolean detectAllViewResolvers = true; /** Throw a NoHandlerFoundException if no Handler was found to process this request? *.*/
    private boolean throwExceptionIfNoHandlerFound = false; /** Perform cleanup of request attributes after include request?. */
    private boolean cleanupAfterInclude = true; /** MultipartResolver used by this servlet. */
    @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; /**
    * Create a new {@code DispatcherServlet} that will create its own internal web
    * application context based on defaults and values provided through servlet
    * init-params. Typically used in Servlet 2.5 or earlier environments, where the only
    * option for servlet registration is through {@code web.xml} which requires the use
    * of a no-arg constructor.
    * <p>Calling {@link #setContextConfigLocation} (init-param 'contextConfigLocation')
    * will dictate which XML files will be loaded by the
    * {@linkplain #DEFAULT_CONTEXT_CLASS default XmlWebApplicationContext}
    * <p>Calling {@link #setContextClass} (init-param 'contextClass') overrides the
    * default {@code XmlWebApplicationContext} and allows for specifying an alternative class,
    * such as {@code AnnotationConfigWebApplicationContext}.
    * <p>Calling {@link #setContextInitializerClasses} (init-param 'contextInitializerClasses')
    * indicates which {@code ApplicationContextInitializer} classes should be used to
    * further configure the internal application context prior to refresh().
    * @see #DispatcherServlet(WebApplicationContext)
    */
    public DispatcherServlet() {
    super();
    setDispatchOptionsRequest(true);
    } /**
    * Create a new {@code DispatcherServlet} with the given web application context. This
    * constructor is useful in Servlet 3.0+ environments where instance-based registration
    * of servlets is possible through the {@link ServletContext#addServlet} API.
    * <p>Using this constructor indicates that the following properties / init-params
    * will be ignored:
    * <ul>
    * <li>{@link #setContextClass(Class)} / 'contextClass'</li>
    * <li>{@link #setContextConfigLocation(String)} / 'contextConfigLocation'</li>
    * <li>{@link #setContextAttribute(String)} / 'contextAttribute'</li>
    * <li>{@link #setNamespace(String)} / 'namespace'</li>
    * </ul>
    * <p>The given web application context may or may not yet be {@linkplain
    * ConfigurableApplicationContext#refresh() refreshed}. If it has <strong>not</strong>
    * already been refreshed (the recommended approach), then the following will occur:
    * <ul>
    * <li>If the given context does not already have a {@linkplain
    * ConfigurableApplicationContext#setParent parent}, the root application context
    * will be set as the parent.</li>
    * <li>If the given context has not already been assigned an {@linkplain
    * ConfigurableApplicationContext#setId id}, one will be assigned to it</li>
    * <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to
    * the application context</li>
    * <li>{@link #postProcessWebApplicationContext} will be called</li>
    * <li>Any {@code ApplicationContextInitializer}s specified through the
    * "contextInitializerClasses" init-param or through the {@link
    * #setContextInitializers} property will be applied.</li>
    * <li>{@link ConfigurableApplicationContext#refresh refresh()} will be called if the
    * context implements {@link ConfigurableApplicationContext}</li>
    * </ul>
    * If the context has already been refreshed, none of the above will occur, under the
    * assumption that the user has performed these actions (or not) per their specific
    * needs.
    * <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
    * @param webApplicationContext the context to use
    * @see #initWebApplicationContext
    * @see #configureAndRefreshWebApplicationContext
    * @see org.springframework.web.WebApplicationInitializer
    */
    public DispatcherServlet(WebApplicationContext webApplicationContext) {
    super(webApplicationContext);
    setDispatchOptionsRequest(true);
    } /**
    * Set whether to detect all HandlerMapping beans in this servlet's context. Otherwise,
    * just a single bean with name "handlerMapping" will be expected.
    * <p>Default is "true". Turn this off if you want this servlet to use a single
    * HandlerMapping, despite multiple HandlerMapping beans being defined in the context.
    */
    public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) {
    this.detectAllHandlerMappings = detectAllHandlerMappings;
    } /**
    * Set whether to detect all HandlerAdapter beans in this servlet's context. Otherwise,
    * just a single bean with name "handlerAdapter" will be expected.
    * <p>Default is "true". Turn this off if you want this servlet to use a single
    * HandlerAdapter, despite multiple HandlerAdapter beans being defined in the context.
    */
    public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) {
    this.detectAllHandlerAdapters = detectAllHandlerAdapters;
    } /**
    * Set whether to detect all HandlerExceptionResolver beans in this servlet's context. Otherwise,
    * just a single bean with name "handlerExceptionResolver" will be expected.
    * <p>Default is "true". Turn this off if you want this servlet to use a single
    * HandlerExceptionResolver, despite multiple HandlerExceptionResolver beans being defined in the context.
    */
    public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) {
    this.detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers;
    } /**
    * Set whether to detect all ViewResolver beans in this servlet's context. Otherwise,
    * just a single bean with name "viewResolver" will be expected.
    * <p>Default is "true". Turn this off if you want this servlet to use a single
    * ViewResolver, despite multiple ViewResolver beans being defined in the context.
    */
    public void setDetectAllViewResolvers(boolean detectAllViewResolvers) {
    this.detectAllViewResolvers = detectAllViewResolvers;
    } /**
    * Set whether to throw a NoHandlerFoundException when no Handler was found for this request.
    * This exception can then be caught with a HandlerExceptionResolver or an
    * {@code @ExceptionHandler} controller method.
    * <p>Note that if {@link org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler}
    * is used, then requests will always be forwarded to the default servlet and a
    * NoHandlerFoundException would never be thrown in that case.
    * <p>Default is "false", meaning the DispatcherServlet sends a NOT_FOUND error through the
    * Servlet response.
    * @since 4.0
    */
    public void setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound) {
    this.throwExceptionIfNoHandlerFound = throwExceptionIfNoHandlerFound;
    } /**
    * Set whether to perform cleanup of request attributes after an include request, that is,
    * whether to reset the original state of all request attributes after the DispatcherServlet
    * has processed within an include request. Otherwise, just the DispatcherServlet's own
    * request attributes will be reset, but not model attributes for JSPs or special attributes
    * set by views (for example, JSTL's).
    * <p>Default is "true", which is strongly recommended. Views should not rely on request attributes
    * having been set by (dynamic) includes. This allows JSP views rendered by an included controller
    * to use any model attributes, even with the same names as in the main JSP, without causing side
    * effects. Only turn this off for special needs, for example to deliberately allow main JSPs to
    * access attributes from JSP views rendered by an included controller.
    */
    public void setCleanupAfterInclude(boolean cleanupAfterInclude) {
    this.cleanupAfterInclude = cleanupAfterInclude;
    } /**
    * This implementation calls {@link #initStrategies}.
    */
    @Override
    protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
    } /**
    * Initialize the strategy objects that this servlet uses.
    * <p>May be overridden in subclasses in order to initialize further strategy objects.
    */
    protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
    } /**
    * Initialize the MultipartResolver used by this class.
    * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
    * no multipart handling is provided.
    */
    private void initMultipartResolver(ApplicationContext context) {
    try {
    this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
    if (logger.isTraceEnabled()) {
    logger.trace("Detected " + this.multipartResolver);
    }
    else if (logger.isDebugEnabled()) {
    logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
    }
    }
    catch (NoSuchBeanDefinitionException ex) {
    // Default is no multipart resolver.
    this.multipartResolver = null;
    if (logger.isTraceEnabled()) {
    logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
    }
    }
    } /**
    * Initialize the LocaleResolver used by this class.
    * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
    * we default to AcceptHeaderLocaleResolver.
    */
    private void initLocaleResolver(ApplicationContext context) {
    try {
    this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
    if (logger.isTraceEnabled()) {
    logger.trace("Detected " + this.localeResolver);
    }
    else if (logger.isDebugEnabled()) {
    logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
    }
    }
    catch (NoSuchBeanDefinitionException ex) {
    // We need to use the default.
    this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
    if (logger.isTraceEnabled()) {
    logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
    "': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
    }
    }
    } /**
    * Initialize the ThemeResolver used by this class.
    * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
    * we default to a FixedThemeResolver.
    */
    private void initThemeResolver(ApplicationContext context) {
    try {
    this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
    if (logger.isTraceEnabled()) {
    logger.trace("Detected " + this.themeResolver);
    }
    else if (logger.isDebugEnabled()) {
    logger.debug("Detected " + this.themeResolver.getClass().getSimpleName());
    }
    }
    catch (NoSuchBeanDefinitionException ex) {
    // We need to use the default.
    this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
    if (logger.isTraceEnabled()) {
    logger.trace("No ThemeResolver '" + THEME_RESOLVER_BEAN_NAME +
    "': using default [" + this.themeResolver.getClass().getSimpleName() + "]");
    }
    }
    } /**
    * Initialize the HandlerMappings used by this class.
    * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
    * we default to BeanNameUrlHandlerMapping.
    */
    private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null; if (this.detectAllHandlerMappings) {
    // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
    Map<String, HandlerMapping> matchingBeans =
    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
    if (!matchingBeans.isEmpty()) {
    this.handlerMappings = new ArrayList<>(matchingBeans.values());
    // We keep HandlerMappings in sorted order.
    AnnotationAwareOrderComparator.sort(this.handlerMappings);
    }
    }
    else {
    try {
    HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
    this.handlerMappings = Collections.singletonList(hm);
    }
    catch (NoSuchBeanDefinitionException ex) {
    // Ignore, we'll add a default HandlerMapping later.
    }
    } // Ensure we have at least one HandlerMapping, by registering
    // a default HandlerMapping if no other mappings are found.
    if (this.handlerMappings == null) {
    this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    if (logger.isTraceEnabled()) {
    logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
    "': using default strategies from DispatcherServlet.properties");
    }
    }
    } /**
    * Initialize the HandlerAdapters used by this class.
    * <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace,
    * we default to SimpleControllerHandlerAdapter.
    */
    private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null; if (this.detectAllHandlerAdapters) {
    // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
    Map<String, HandlerAdapter> matchingBeans =
    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
    if (!matchingBeans.isEmpty()) {
    this.handlerAdapters = new ArrayList<>(matchingBeans.values());
    // We keep HandlerAdapters in sorted order.
    AnnotationAwareOrderComparator.sort(this.handlerAdapters);
    }
    }
    else {
    try {
    HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
    this.handlerAdapters = Collections.singletonList(ha);
    }
    catch (NoSuchBeanDefinitionException ex) {
    // Ignore, we'll add a default HandlerAdapter later.
    }
    } // Ensure we have at least some HandlerAdapters, by registering
    // default HandlerAdapters if no other adapters are found.
    if (this.handlerAdapters == null) {
    this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
    if (logger.isTraceEnabled()) {
    logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
    "': using default strategies from DispatcherServlet.properties");
    }
    }
    } /**
    * Initialize the HandlerExceptionResolver used by this class.
    * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
    * we default to no exception resolver.
    */
    private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null; if (this.detectAllHandlerExceptionResolvers) {
    // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
    Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
    .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
    if (!matchingBeans.isEmpty()) {
    this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
    // We keep HandlerExceptionResolvers in sorted order.
    AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
    }
    }
    else {
    try {
    HandlerExceptionResolver her =
    context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
    this.handlerExceptionResolvers = Collections.singletonList(her);
    }
    catch (NoSuchBeanDefinitionException ex) {
    // Ignore, no HandlerExceptionResolver is fine too.
    }
    } // Ensure we have at least some HandlerExceptionResolvers, by registering
    // default HandlerExceptionResolvers if no other resolvers are found.
    if (this.handlerExceptionResolvers == null) {
    this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
    if (logger.isTraceEnabled()) {
    logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
    "': using default strategies from DispatcherServlet.properties");
    }
    }
    } /**
    * Initialize the RequestToViewNameTranslator used by this servlet instance.
    * <p>If no implementation is configured then we default to DefaultRequestToViewNameTranslator.
    */
    private void initRequestToViewNameTranslator(ApplicationContext context) {
    try {
    this.viewNameTranslator =
    context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
    if (logger.isTraceEnabled()) {
    logger.trace("Detected " + this.viewNameTranslator.getClass().getSimpleName());
    }
    else if (logger.isDebugEnabled()) {
    logger.debug("Detected " + this.viewNameTranslator);
    }
    }
    catch (NoSuchBeanDefinitionException ex) {
    // We need to use the default.
    this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
    if (logger.isTraceEnabled()) {
    logger.trace("No RequestToViewNameTranslator '" + REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
    "': using default [" + this.viewNameTranslator.getClass().getSimpleName() + "]");
    }
    }
    } /**
    * Initialize the ViewResolvers used by this class.
    * <p>If no ViewResolver beans are defined in the BeanFactory for this
    * namespace, we default to InternalResourceViewResolver.
    */
    private void initViewResolvers(ApplicationContext context) {
    this.viewResolvers = null; if (this.detectAllViewResolvers) {
    // Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
    Map<String, ViewResolver> matchingBeans =
    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
    if (!matchingBeans.isEmpty()) {
    this.viewResolvers = new ArrayList<>(matchingBeans.values());
    // We keep ViewResolvers in sorted order.
    AnnotationAwareOrderComparator.sort(this.viewResolvers);
    }
    }
    else {
    try {
    ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
    this.viewResolvers = Collections.singletonList(vr);
    }
    catch (NoSuchBeanDefinitionException ex) {
    // Ignore, we'll add a default ViewResolver later.
    }
    } // Ensure we have at least one ViewResolver, by registering
    // a default ViewResolver if no other resolvers are found.
    if (this.viewResolvers == null) {
    this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
    if (logger.isTraceEnabled()) {
    logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
    "': using default strategies from DispatcherServlet.properties");
    }
    }
    } /**
    * Initialize the {@link FlashMapManager} used by this servlet instance.
    * <p>If no implementation is configured then we default to
    * {@code org.springframework.web.servlet.support.DefaultFlashMapManager}.
    */
    private void initFlashMapManager(ApplicationContext context) {
    try {
    this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
    if (logger.isTraceEnabled()) {
    logger.trace("Detected " + this.flashMapManager.getClass().getSimpleName());
    }
    else if (logger.isDebugEnabled()) {
    logger.debug("Detected " + this.flashMapManager);
    }
    }
    catch (NoSuchBeanDefinitionException ex) {
    // We need to use the default.
    this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
    if (logger.isTraceEnabled()) {
    logger.trace("No FlashMapManager '" + FLASH_MAP_MANAGER_BEAN_NAME +
    "': using default [" + this.flashMapManager.getClass().getSimpleName() + "]");
    }
    }
    } /**
    * Return this servlet's ThemeSource, if any; else return {@code null}.
    * <p>Default is to return the WebApplicationContext as ThemeSource,
    * provided that it implements the ThemeSource interface.
    * @return the ThemeSource, if any
    * @see #getWebApplicationContext()
    */
    @Nullable
    public final ThemeSource getThemeSource() {
    return (getWebApplicationContext() instanceof ThemeSource ? (ThemeSource) getWebApplicationContext() : null);
    } /**
    * Obtain this servlet's MultipartResolver, if any.
    * @return the MultipartResolver used by this servlet, or {@code null} if none
    * (indicating that no multipart support is available)
    */
    @Nullable
    public final MultipartResolver getMultipartResolver() {
    return this.multipartResolver;
    } /**
    * Return the configured {@link HandlerMapping} beans that were detected by
    * type in the {@link WebApplicationContext} or initialized based on the
    * default set of strategies from {@literal DispatcherServlet.properties}.
    * <p><strong>Note:</strong> This method may return {@code null} if invoked
    * prior to {@link #onRefresh(ApplicationContext)}.
    * @return an immutable list with the configured mappings, or {@code null}
    * if not initialized yet
    * @since 5.0
    */
    @Nullable
    public final List<HandlerMapping> getHandlerMappings() {
    return (this.handlerMappings != null ? Collections.unmodifiableList(this.handlerMappings) : null);
    } /**
    * Return the default strategy object for the given strategy interface.
    * <p>The default implementation delegates to {@link #getDefaultStrategies},
    * expecting a single object in the list.
    * @param context the current WebApplicationContext
    * @param strategyInterface the strategy interface
    * @return the corresponding strategy object
    * @see #getDefaultStrategies
    */
    protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
    List<T> strategies = getDefaultStrategies(context, strategyInterface);
    if (strategies.size() != 1) {
    throw new BeanInitializationException(
    "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
    }
    return strategies.get(0);
    } /**
    * Create a List of default strategy objects for the given strategy interface.
    * <p>The default implementation uses the "DispatcherServlet.properties" file (in the same
    * package as the DispatcherServlet class) to determine the class names. It instantiates
    * the strategy objects through the context's BeanFactory.
    * @param context the current WebApplicationContext
    * @param strategyInterface the strategy interface
    * @return the List of corresponding strategy objects
    */
    @SuppressWarnings("unchecked")
    protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
    String key = strategyInterface.getName();
    String value = defaultStrategies.getProperty(key);
    if (value != null) {
    String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
    List<T> strategies = new ArrayList<>(classNames.length);
    for (String className : classNames) {
    try {
    Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
    Object strategy = createDefaultStrategy(context, clazz);
    strategies.add((T) strategy);
    }
    catch (ClassNotFoundException ex) {
    throw new BeanInitializationException(
    "Could not find DispatcherServlet's default strategy class [" + className +
    "] for interface [" + key + "]", ex);
    }
    catch (LinkageError err) {
    throw new BeanInitializationException(
    "Unresolvable class definition for DispatcherServlet's default strategy class [" +
    className + "] for interface [" + key + "]", err);
    }
    }
    return strategies;
    }
    else {
    return new LinkedList<>();
    }
    } /**
    * Create a default strategy.
    * <p>The default implementation uses
    * {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}.
    * @param context the current WebApplicationContext
    * @param clazz the strategy implementation class to instantiate
    * @return the fully configured strategy instance
    * @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
    * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean
    */
    protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
    return context.getAutowireCapableBeanFactory().createBean(clazz);
    } /**
    * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
    * for the actual dispatching.
    */
    @Override
    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);
    } 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);
    }
    }
    }
    } private void logRequest(HttpServletRequest request) {
    LogFormatUtils.traceDebug(logger, traceOn -> {
    String params;
    if (isEnableLoggingRequestDetails()) {
    params = request.getParameterMap().entrySet().stream()
    .map(entry -> entry.getKey() + ":" + Arrays.toString(entry.getValue()))
    .collect(Collectors.joining(", "));
    }
    else {
    params = (request.getParameterMap().isEmpty() ? "" : "masked");
    } String queryString = request.getQueryString();
    String queryClause = (StringUtils.hasLength(queryString) ? "?" + queryString : "");
    String dispatchType = (!request.getDispatcherType().equals(DispatcherType.REQUEST) ?
    "\"" + request.getDispatcherType().name() + "\" dispatch for " : "");
    String message = (dispatchType + request.getMethod() + " \"" + getRequestUri(request) +
    queryClause + "\", parameters={" + params + "}"); if (traceOn) {
    List<String> values = Collections.list(request.getHeaderNames());
    String headers = values.size() > 0 ? "masked" : "";
    if (isEnableLoggingRequestDetails()) {
    headers = values.stream().map(name -> name + ":" + Collections.list(request.getHeaders(name)))
    .collect(Collectors.joining(", "));
    }
    return message + ", headers={" + headers + "} in DispatcherServlet '" + getServletName() + "'";
    }
    else {
    return message;
    }
    });
    } /**
    * 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
    */
    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 {
    processedRequest = checkMultipart(request);
    multipartRequestParsed = (processedRequest != request); // Determine handler for the current request.
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) {
    noHandlerFound(processedRequest, response);
    return;
    } // Determine handler adapter for the current request.
    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;
    }
    } if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
    } // Actually invoke the handler.
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
    return;
    } applyDefaultViewName(processedRequest, mv);
    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);
    }
    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);
    }
    }
    }
    } /**
    * Do we need view name translation?
    */
    private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
    if (mv != null && !mv.hasView()) {
    String defaultViewName = getDefaultViewName(request);
    if (defaultViewName != null) {
    mv.setViewName(defaultViewName);
    }
    }
    } /**
    * Handle the result of handler selection and handler invocation, which is
    * either a ModelAndView or an Exception to be resolved to a ModelAndView.
    */
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
    @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
    @Nullable Exception exception) throws Exception { boolean errorView = false; if (exception != null) {
    if (exception instanceof ModelAndViewDefiningException) {
    logger.debug("ModelAndViewDefiningException encountered", exception);
    mv = ((ModelAndViewDefiningException) exception).getModelAndView();
    }
    else {
    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
    mv = processHandlerException(request, response, handler, exception);
    errorView = (mv != null);
    }
    } // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
    render(mv, request, response);
    if (errorView) {
    WebUtils.clearErrorRequestAttributes(request);
    }
    }
    else {
    if (logger.isTraceEnabled()) {
    logger.trace("No view rendering, null ModelAndView returned.");
    }
    } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    // Concurrent handling started during a forward
    return;
    } if (mappedHandler != null) {
    // Exception (if any) is already handled..
    mappedHandler.triggerAfterCompletion(request, response, null);
    }
    } /**
    * Build a LocaleContext for the given request, exposing the request's primary locale as current locale.
    * <p>The default implementation uses the dispatcher's LocaleResolver to obtain the current locale,
    * which might change during a request.
    * @param request current HTTP request
    * @return the corresponding LocaleContext
    */
    @Override
    protected LocaleContext buildLocaleContext(final HttpServletRequest request) {
    LocaleResolver lr = this.localeResolver;
    if (lr instanceof LocaleContextResolver) {
    return ((LocaleContextResolver) lr).resolveLocaleContext(request);
    }
    else {
    return () -> (lr != null ? lr.resolveLocale(request) : request.getLocale());
    }
    } /**
    * Convert the request into a multipart request, and make multipart resolver available.
    * <p>If no multipart resolver is set, simply use the existing request.
    * @param request current HTTP request
    * @return the processed request (multipart wrapper if necessary)
    * @see MultipartResolver#resolveMultipart
    */
    protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
    if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
    if (request.getDispatcherType().equals(DispatcherType.REQUEST)) {
    logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
    }
    }
    else if (hasMultipartException(request)) {
    logger.debug("Multipart resolution previously failed for current request - " +
    "skipping re-resolution for undisturbed error rendering");
    }
    else {
    try {
    return this.multipartResolver.resolveMultipart(request);
    }
    catch (MultipartException ex) {
    if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
    logger.debug("Multipart resolution failed for error dispatch", ex);
    // Keep processing error dispatch with regular request handle below
    }
    else {
    throw ex;
    }
    }
    }
    }
    // If not returned before: return original request.
    return request;
    } /**
    * Check "javax.servlet.error.exception" attribute for a multipart exception.
    */
    private boolean hasMultipartException(HttpServletRequest request) {
    Throwable error = (Throwable) request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE);
    while (error != null) {
    if (error instanceof MultipartException) {
    return true;
    }
    error = error.getCause();
    }
    return false;
    } /**
    * Clean up any resources used by the given multipart request (if any).
    * @param request current HTTP request
    * @see MultipartResolver#cleanupMultipart
    */
    protected void cleanupMultipart(HttpServletRequest request) {
    if (this.multipartResolver != null) {
    MultipartHttpServletRequest multipartRequest =
    WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
    if (multipartRequest != null) {
    this.multipartResolver.cleanupMultipart(multipartRequest);
    }
    }
    } /**
    * 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
    */
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
    for (HandlerMapping mapping : this.handlerMappings) {
    HandlerExecutionChain handler = mapping.getHandler(request);
    if (handler != null) {
    return handler;
    }
    }
    }
    return null;
    } /**
    * No handler found -> set appropriate HTTP response status.
    * @param request current HTTP request
    * @param response current HTTP response
    * @throws Exception if preparing the response failed
    */
    protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (pageNotFoundLogger.isWarnEnabled()) {
    pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
    }
    if (this.throwExceptionIfNoHandlerFound) {
    throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
    new ServletServerHttpRequest(request).getHeaders());
    }
    else {
    response.sendError(HttpServletResponse.SC_NOT_FOUND);
    }
    } /**
    * 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.
    */
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
    for (HandlerAdapter adapter : this.handlerAdapters) {
    if (adapter.supports(handler)) {
    return adapter;
    }
    }
    }
    throw new ServletException("No adapter for handler [" + handler +
    "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    } /**
    * Determine an error ModelAndView via the registered HandlerExceptionResolvers.
    * @param request current HTTP request
    * @param response current HTTP response
    * @param handler the executed handler, or {@code null} if none chosen at the time of the exception
    * (for example, if multipart resolution failed)
    * @param ex the exception that got thrown during handler execution
    * @return a corresponding ModelAndView to forward to
    * @throws Exception if no error ModelAndView found
    */
    @Nullable
    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
    @Nullable Object handler, Exception ex) throws Exception { // Success and error responses may use different content types
    request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); // Check registered HandlerExceptionResolvers...
    ModelAndView exMv = null;
    if (this.handlerExceptionResolvers != null) {
    for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
    exMv = resolver.resolveException(request, response, handler, ex);
    if (exMv != null) {
    break;
    }
    }
    }
    if (exMv != null) {
    if (exMv.isEmpty()) {
    request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
    return null;
    }
    // We might still need view name translation for a plain error model...
    if (!exMv.hasView()) {
    String defaultViewName = getDefaultViewName(request);
    if (defaultViewName != null) {
    exMv.setViewName(defaultViewName);
    }
    }
    if (logger.isTraceEnabled()) {
    logger.trace("Using resolved error view: " + exMv, ex);
    }
    else if (logger.isDebugEnabled()) {
    logger.debug("Using resolved error view: " + exMv);
    }
    WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
    return exMv;
    } throw ex;
    } /**
    * Render the given ModelAndView.
    * <p>This is the last stage in handling a request. It may involve resolving the view by name.
    * @param mv the ModelAndView to render
    * @param request current HTTP servlet request
    * @param response current HTTP servlet response
    * @throws ServletException if view is missing or cannot be resolved
    * @throws Exception if there's a problem rendering the view
    */
    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // Determine locale for request and apply it to the response.
    Locale locale =
    (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
    response.setLocale(locale); View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
    // We need to resolve the view name.
    view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
    if (view == null) {
    throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
    "' in servlet with name '" + getServletName() + "'");
    }
    }
    else {
    // No need to lookup: the ModelAndView object contains the actual View object.
    view = mv.getView();
    if (view == null) {
    throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
    "View object in servlet with name '" + getServletName() + "'");
    }
    } // Delegate to the View object for rendering.
    if (logger.isTraceEnabled()) {
    logger.trace("Rendering view [" + view + "] ");
    }
    try {
    if (mv.getStatus() != null) {
    response.setStatus(mv.getStatus().value());
    }
    view.render(mv.getModelInternal(), request, response);
    }
    catch (Exception ex) {
    if (logger.isDebugEnabled()) {
    logger.debug("Error rendering view [" + view + "]", ex);
    }
    throw ex;
    }
    } /**
    * Translate the supplied request into a default view name.
    * @param request current HTTP servlet request
    * @return the view name (or {@code null} if no default found)
    * @throws Exception if view name translation failed
    */
    @Nullable
    protected String getDefaultViewName(HttpServletRequest request) throws Exception {
    return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
    } /**
    * Resolve the given view name into a View object (to be rendered).
    * <p>The default implementations asks all ViewResolvers of this dispatcher.
    * Can be overridden for custom resolution strategies, potentially based on
    * specific model attributes or request parameters.
    * @param viewName the name of the view to resolve
    * @param model the model to be passed to the view
    * @param locale the current locale
    * @param request current HTTP servlet request
    * @return the View object, or {@code null} if none found
    * @throws Exception if the view cannot be resolved
    * (typically in case of problems creating an actual View object)
    * @see ViewResolver#resolveViewName
    */
    @Nullable
    protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
    Locale locale, HttpServletRequest request) throws Exception { if (this.viewResolvers != null) {
    for (ViewResolver viewResolver : this.viewResolvers) {
    View view = viewResolver.resolveViewName(viewName, locale);
    if (view != null) {
    return view;
    }
    }
    }
    return null;
    } private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,
    @Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception { if (mappedHandler != null) {
    mappedHandler.triggerAfterCompletion(request, response, ex);
    }
    throw ex;
    } /**
    * Restore the request attributes after an include.
    * @param request current HTTP request
    * @param attributesSnapshot the snapshot of the request attributes before the include
    */
    @SuppressWarnings("unchecked")
    private void restoreAttributesAfterInclude(HttpServletRequest request, Map<?, ?> attributesSnapshot) {
    // Need to copy into separate Collection here, to avoid side effects
    // on the Enumeration when removing attributes.
    Set<String> attrsToCheck = new HashSet<>();
    Enumeration<?> attrNames = request.getAttributeNames();
    while (attrNames.hasMoreElements()) {
    String attrName = (String) attrNames.nextElement();
    if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
    attrsToCheck.add(attrName);
    }
    } // Add attributes that may have been removed
    attrsToCheck.addAll((Set<String>) attributesSnapshot.keySet()); // Iterate over the attributes to check, restoring the original value
    // or removing the attribute, respectively, if appropriate.
    for (String attrName : attrsToCheck) {
    Object attrValue = attributesSnapshot.get(attrName);
    if (attrValue == null) {
    request.removeAttribute(attrName);
    }
    else if (attrValue != request.getAttribute(attrName)) {
    request.setAttribute(attrName, attrValue);
    }
    }
    } private static String getRequestUri(HttpServletRequest request) {
    String uri = (String) request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE);
    if (uri == null) {
    uri = request.getRequestURI();
    }
    return uri;
    } }

    DispatcherServlet

    @Controller表明这是一个控制器,然后@RequestMapping代表请求路径和控制器(或其方法)的映射关系,它会在Web服务器启动Spring MVC时,就被扫描到HandlerMapping的机制中存储,之后在用户发起请求被DispatcherServlet拦截后,通过URI和其他的条件,通过HandlerMapper机制就能找到对应的控制器(或其方法)进行响应。只是通过HandlerMapping返回的是一个HandlerExecutionChain对象

  2. /*
    * Copyright 2002-2020 the original author or authors.
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * https://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */ package org.springframework.web.servlet; import java.util.ArrayList;
    import java.util.List; import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory; import org.springframework.lang.Nullable;
    import org.springframework.util.CollectionUtils;
    import org.springframework.util.ObjectUtils; /**
    * Handler execution chain, consisting of handler object and any handler interceptors.
    * Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
    *
    * @author Juergen Hoeller
    * @since 20.06.2003
    * @see HandlerInterceptor
    */
    public class HandlerExecutionChain { private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class); private final Object handler; @Nullable
    private HandlerInterceptor[] interceptors; @Nullable
    private List<HandlerInterceptor> interceptorList; private int interceptorIndex = -1; /**
    * Create a new HandlerExecutionChain.
    * @param handler the handler object to execute
    */
    public HandlerExecutionChain(Object handler) {
    this(handler, (HandlerInterceptor[]) null);
    } /**
    * Create a new HandlerExecutionChain.
    * @param handler the handler object to execute
    * @param interceptors the array of interceptors to apply
    * (in the given order) before the handler itself executes
    */
    public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
    if (handler instanceof HandlerExecutionChain) {
    HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
    this.handler = originalChain.getHandler();
    this.interceptorList = new ArrayList<>();
    CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
    CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
    }
    else {
    this.handler = handler;
    this.interceptors = interceptors;
    }
    } /**
    * Return the handler object to execute.
    */
    public Object getHandler() {
    return this.handler;
    } /**
    * Add the given interceptor to the end of this chain.
    */
    public void addInterceptor(HandlerInterceptor interceptor) {
    initInterceptorList().add(interceptor);
    } /**
    * Add the given interceptor at the specified index of this chain.
    * @since 5.2
    */
    public void addInterceptor(int index, HandlerInterceptor interceptor) {
    initInterceptorList().add(index, interceptor);
    } /**
    * Add the given interceptors to the end of this chain.
    */
    public void addInterceptors(HandlerInterceptor... interceptors) {
    if (!ObjectUtils.isEmpty(interceptors)) {
    CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
    }
    } private List<HandlerInterceptor> initInterceptorList() {
    if (this.interceptorList == null) {
    this.interceptorList = new ArrayList<>();
    if (this.interceptors != null) {
    // An interceptor array specified through the constructor
    CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
    }
    }
    this.interceptors = null;
    return this.interceptorList;
    } /**
    * Return the array of interceptors to apply (in the given order).
    * @return the array of HandlerInterceptors instances (may be {@code null})
    */
    @Nullable
    public HandlerInterceptor[] getInterceptors() {
    if (this.interceptors == null && this.interceptorList != null) {
    this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[0]);
    }
    return this.interceptors;
    } /**
    * Apply preHandle methods of registered interceptors.
    * @return {@code true} if the execution chain should proceed with the
    * next interceptor or the handler itself. Else, DispatcherServlet assumes
    * that this interceptor has already dealt with the response itself.
    */
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
    for (int i = 0; i < interceptors.length; i++) {
    HandlerInterceptor interceptor = interceptors[i];
    if (!interceptor.preHandle(request, response, this.handler)) {
    triggerAfterCompletion(request, response, null);
    return false;
    }
    this.interceptorIndex = i;
    }
    }
    return true;
    } /**
    * Apply postHandle methods of registered interceptors.
    */
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
    throws Exception { HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
    for (int i = interceptors.length - 1; i >= 0; i--) {
    HandlerInterceptor interceptor = interceptors[i];
    interceptor.postHandle(request, response, this.handler, mv);
    }
    }
    } /**
    * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
    * Will just invoke afterCompletion for all interceptors whose preHandle invocation
    * has successfully completed and returned true.
    */
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
    throws Exception { HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
    for (int i = this.interceptorIndex; i >= 0; i--) {
    HandlerInterceptor interceptor = interceptors[i];
    try {
    interceptor.afterCompletion(request, response, this.handler, ex);
    }
    catch (Throwable ex2) {
    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
    }
    }
    }
    } /**
    * Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.
    */
    void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
    for (int i = interceptors.length - 1; i >= 0; i--) {
    HandlerInterceptor interceptor = interceptors[i];
    if (interceptor instanceof AsyncHandlerInterceptor) {
    try {
    AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptor;
    asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
    }
    catch (Throwable ex) {
    if (logger.isErrorEnabled()) {
    logger.error("Interceptor [" + interceptor + "] failed in afterConcurrentHandlingStarted", ex);
    }
    }
    }
    }
    }
    } /**
    * Delegates to the handler's {@code toString()} implementation.
    */
    @Override
    public String toString() {
    Object handler = getHandler();
    StringBuilder sb = new StringBuilder();
    sb.append("HandlerExecutionChain with [").append(handler).append("] and ");
    if (this.interceptorList != null) {
    sb.append(this.interceptorList.size());
    }
    else if (this.interceptors != null) {
    sb.append(this.interceptors.length);
    }
    else {
    sb.append(0);
    }
    return sb.append(" interceptors").toString();
    } }

    HandlerExecutionChain对象包含一个处理器(handler)。这里的处理器是对控制器(controller)的包装,因为我们的控制器方法可能存在参数,那么处理器就可以读入HTTP和上下文的相关参数,然后再传递给控制器方法。而在控制器执行完成返回后,处理器又可以通过配置信息对控制器的返回结果进行处理。从这段描述中可以看出,处理器包含了控制器方法的逻辑,此外还有处理器的拦截器(interceptor),这样就能够通过拦截处理器进一步地增强处理器的功能。得到了处理器(handler),还需要去运行,但是我们有普通HTTP请求,也有按BeanName的请求,甚至是WebSocket的请求,所以它还需要一个适配器去运行HandlerExecutionChain对象包含的处理器,这就是HandlerAdapter接口定义的实现类。可以看到在Spring MVC中最常用的HandlerAdapter的实现类,这便是HttpRequestHandlerAdapter。通过请求的类型,DispatcherServlet就会找到它来执行Web请求的HandlerExecutionChain对象包含的内容,这样就能够执行我们的处理器(handler)了。只是HandlerAdapter运行HandlerExecutionChain对象这步还比较复杂,我们这里暂时不进行深入讨论,放到后面再谈。在处理器调用控制器时,它首先通过模型层得到数据,再放入数据模型中,最后将返回模型和视图(ModelAndView)对象,这里控制器设置的视图名称设置为“user/details”,这样就走到了视图解析器(ViewResolver),去解析视图逻辑名称了。在代码清单中可以看到视图解析器(ViewResolver)的自动初始化。为了定制InternalResourceViewResolver初始化,可以在配置文件application.properties中进行配置

  3. 在Spring Boot的机制下定制InternalResourceViewResolver这个视图解析器的初始化,也就是在返回视图名称之后,它会以前缀(prefix)和后缀(suffix)以及视图名称组成全路径定位视图。例如,在控制器中返回的是“user/details”,那么它就会找到/WEB-INF/jsp/user/details.jsp作为视图(View)。严格地说,这一步也不是必需的,因为有些视图并不需要逻辑名称,在不需要的时候,就不再需要视图解析器工作了
  4. 视图解析器定位到视图后,视图的作用是将数据模型(Model)渲染,这样就能够响应用户的请求。这一步就是视图将数据模型渲染(View)出来,用来展示给用户查看。按照我们控制器的返回,就是/WEB-INF/jsp/user/details.jsp作为我们的视图

Servlet和springMVC的更多相关文章

  1. 带着新人简单看看servlet到springmvc

    好久都没有写博客了,不是因为自己懒了,而是总感觉自己知道的只是太少了,每次想写博客的时候都不知道怎么下手,不过最近看到一篇博客说的是springmvc,给了我比较大的兴趣,感觉一下子对整个spring ...

  2. struts2,servlet和springmvc的单例多例问题

    struts2,servlet和springmvc的单例多例问题 原创 2017年06月12日 09:59:21 标签: struts2 / servlet / springmvc / 单例 / 多例 ...

  3. 文件上传(Servlet/Struts2/SpringMVC)

    文件下载(Servlet/Struts2)的链接:http://www.cnblogs.com/ghq120/p/8328093.html 文件上传 Servlet实现 要实现文件上传的功能,必须在f ...

  4. servlet到springmvc的演进

    1.简单看看servlet 1.1.servlet继承关系 先看看下面servlet的这个继承关系,有点印象即可(可以暂时忽略ServletConfig,这个接口就是让我们可以从web.xml文件中拿 ...

  5. Servlet Struts2 SpringMVC 获取参数与导出数据 方法比较

    servlet中通过request.getParameter()从网页获取参数 通过request session servletContext几个域对象的setAttribute(String ,O ...

  6. Servlet和SpringMVC补课

    1.web.xml加载顺序 http://mianhuaman.iteye.com/blog/1105522 关键点:ServletContext -> context-param -> ...

  7. 回顾Servlet及SpringMVC

    什么是Servlet? Servlet是运行在Web服务器或应用服务器上的程序,它是作为来自Web浏览器或其他HTTP客户端的请求和HTTP服务器上的数据库 或应用程序之间的中间层. servlet架 ...

  8. 3. servlet 和 springmvc框架关系

    在springmvc上我们会编写很多相关的配置 编写springmvc.xml 配置映射器 配置 处理适配器 ... web.xml 配置前端控制器 (DispatcherServlet) 官网: h ...

  9. Servlet、SPringMVC、Struts等防止表单反复提交的多种处理方法

    第一种处理方法(非拦截器): 眼下这样的方法不建议,由于JSP规范不建议写JAVA代码.这样的能够方便另外一种处理方法的理解,另外一种方法引入拦截器的思想,原理基本一样,模仿Struts的Token机 ...

  10. servlet模拟SpringMVC

    1. web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=&q ...

随机推荐

  1. 【Azure Data Lake Storage】如何才能保留Blob中的文件目录结构(即使文件夹中文件数量为0的情况下)?

    问题描述 在使用Azure Storage Account Blob时候,发现当文件夹中的Blob(文件)被全部输出后,文件夹也会消失? 为什么它不能像Windows系统中的文件夹一样,即使是一个空文 ...

  2. 【Azure 存储服务】使用REST API操作Azure Storage Table,删除数据(Delete Entity)

    问题描述 使用Azure Storage Table的REST API,实现根据过滤条件删除满足条件的数据,调用方法为  Delete Entity (Azure Storage) 问题实现 第一步: ...

  3. 【Azure 环境】Azure Key Vault 采用自签名证书,是否需要CA provider

    关于 Azure Key Vault 证书,密钥保管库证书支持适用于 x509 证书管理,它提供以下行为: 允许证书所有者通过密钥保管库创建过程或通过导入现有证书来创建证书. 包括自签名证书和证书颁发 ...

  4. Rtsp转Flv在浏览器中播放

    目录 概述 环境 项目目录清单 项目搭建步骤 引入相关npm依赖 实例化一个express应用 创建WebsocketServer并解析rtsp 使用flv播放 浏览器中测试 代码 引用 概述 众所周 ...

  5. 视觉slam十四讲CH5 ---相机与图像

    视觉slam十四讲 ---CH5 相机与图像 视觉slam中,作为主要传感器的相机自然起到着重要的作用,而相机拍摄的图像及其处理也是我们要做的工作之一. 1. 相机模型 单目相机的针孔模型 上图中的模 ...

  6. ENVI为遥感影像设置空间坐标系的方法

      本文介绍基于ENVI软件,对不含有任何地理参考信息的栅格遥感影像添加地理坐标系或投影坐标系等地理参考信息的方法.   我们先来看一下本文需要实现的需求.现有以下两景遥感影像,其位于不同的空间位置: ...

  7. git拉项目, 1.新建目录 2 git clone 地址 . (重点最后的点)

  8. dist目录打war包命令 jar -cvf yourName_web.war *

    进入dist目录 "build:war": "cd dist && jar -cvf ../yourName_web.war *",

  9. 基于Python的子进程获取键盘输入

    一 概念 众所周知,python中的获取键盘输入,input函数是没办法用在子程序的,这就限制了它的用途.想要在子程序中获取键盘输入.唯有 fn=sys.stdin.fileno函数了. 二 实例解析 ...

  10. 后端基础SQL—数据库简介与SQL语法

    数据库简介与SQL语法 1.数据库简介 2.数据库结构 3.SQL语句 4.SQL基本语法 5.MySQL基础查询语句 6.高级查询与子查询 7.渗透测试常用函数 8.判断闭合类型 一.数据库简介 数 ...