SpringBoot中的SpringMVC的自动配置

注意,本文章尚未完成,暂为个人笔记用。

先来理解一下Servlet规范

Servlet规范建立在在Java语言的基础之上。

它主要规定了

  • Servlet的形式(包括Listener,Filter)
  • 对Servlet的处理方法

前者就决定了我们开发者如何写Servlet,也就是Web的代码。 后者决定了Tomcat这类的Servlet容器如何工作,也就是Tomcat容器如何识别开发者写的Servlet,并使其工作

Servlet规范是一个标准或者说一个约定而已,需要开发者和容器共同遵守。

从Servlet规范3.0开始,开发者可以不必须使用web.xml开声明自己写的servlet:实现ServletContainerInitializer接口。ServletContainerInitializer#onStartUp()会在容器启动时被调用,开发者可以在其中注册Servlet,Listener,Filter。

public interface ServletContainerInitializer {

    /**
     * Notifies this ServletContainerInitializer of the startup
     * of the application represented by the given ServletContext.
     *
     * 

If this ServletContainerInitializer is bundled in a JAR * file inside the WEB-INF/lib directory of an application, * its onStartup method will be invoked only once during the * startup of the bundling application. If this * ServletContainerInitializer is bundled inside a JAR file * outside of any WEB-INF/lib directory, but still * discoverable as described above, its onStartup method * will be invoked every time an application is started. * * @param c the Set of application classes that extend, implement, or * have been annotated with the class types specified by the * {@link javax.servlet.annotation.HandlesTypes HandlesTypes} annotation, * or null if there are no matches, or this * ServletContainerInitializer has not been annotated with * HandlesTypes * * @param ctx the ServletContext of the web application that * is being started and in which the classes contained in c * were found * * @throws ServletException if an error has occurred * 可以看到,onStartup方法接受的第一个参数c。是有@HandlesTypes注解的所有类的集合 */ public void onStartup(Set> c, ServletContext ctx) throws ServletException; } 复制代码

可以看到,这种设计的初衷是,并不是由ServletContainerInitializer的实现类来注册Servlet和Listener等,ServletContainerInitializer的实现类仅仅是一个入口,真正用来注册的是这些c类。

SpringBoot中Tomcat的启动,以及ServletContainerInitializer的实现

其中大部分内容的处理我们都已经了解,这次关键看onRefresh()方法

回到对Spring来说最关键的一个方法AbstractApplicationContext#refresh()

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            // 在这里启动Tomcat容器
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

复制代码

ServletWebServerApplicationContext#onRefresh()方法的实现如下

@Override
protected void onRefresh() {
	super.onRefresh();
	try {
		createWebServer();
	}
	catch (Throwable ex) {
		throw new ApplicationContextException("Unable to start web server", ex);
	}
}
复制代码

如上只是调了一个方法而已。 继续看ServletWebServerApplicationContext#createWebServer()

省略了异常处理的代码

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        // WebServer的工厂在这里取得的
        ServletWebServerFactory factory = getWebServerFactory();
        // 可以看到,在这里创建一个ApplicationContext的webServer属性
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        getSelfInitializer().onStartup(servletContext);
    }
    initPropertySources();
}
复制代码

先看getWebServerFactory()的代码如下,省略了异常处理代码

/**
 * Returns the {@link ServletWebServerFactory} that should be used to create the
 * embedded {@link WebServer}. By default this method searches for a suitable bean in
 * the context itself.
 */
protected ServletWebServerFactory getWebServerFactory() {
    // Use bean names so that we don't consider the hierarchy
    String[] beanNames = getBeanFactory()
            .getBeanNamesForType(ServletWebServerFactory.class);
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
复制代码

可以看到,是在BeanFactory中寻找类型为ServletWebServerFactory的Bean。 也是事先注入的。可以猜测,是通过SpringBoot的自动配置的注解注入的。具体在什么地方,以后再看。

再看下一句getWebServer(),传入了一个ServletWebServerApplicationContext#selfInitialize() 这个地方比较难以理解,涉及到JDK8的lambda语法。看看

/**
 * Returns the {@link ServletContextInitializer} that will be used to complete the
 * setup of this {@link WebApplicationContext}.
 */
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    // 这里居然返回的是一个方法?
    // 因为这个方法的生命的返回类型是ServletContextInitializer
    // 所以返回的必须是这个类型的对象
    // 也就是说,返回了实现了ServletContextInitializer接口的一个类型的对象
    // 具体就是使用selfInitialize这个方法来实现ServletContextInitializer接口的onStartup方法
    return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
    prepareWebApplicationContext(servletContext);
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
            beanFactory);
    WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
            getServletContext());
    existingScopes.restore();
    WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
            getServletContext());
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}
复制代码

再来继续看TomcatServletWebServerFactory#getWebServer()的代码

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 创建一个Tomcat实例
	Tomcat tomcat = new Tomcat();
	File baseDir = (this.baseDirectory != null) ? this.baseDirectory
			: createTempDir("tomcat");
	tomcat.setBaseDir(baseDir.getAbsolutePath());
	Connector connector = new Connector(this.protocol);
	tomcat.getService().addConnector(connector);
	customizeConnector(connector);
	tomcat.setConnector(connector);
	tomcat.getHost().setAutoDeploy(false);
	configureEngine(tomcat.getEngine());
	for (Connector additionalConnector : this.additionalTomcatConnectors) {
		tomcat.getService().addConnector(additionalConnector);
	}
	prepareContext(tomcat.getHost(), initializers);
	// WebServer是SpringBoot的一个接口
	// 用来包装Tomcat等等服务器
	return getTomcatWebServer(tomcat);
}
复制代码

再继续看getTomcatWebServer()

/**
 * Factory method called to create the {@link TomcatWebServer}. Subclasses can
 * override this method to return a different {@link TomcatWebServer} or apply
 * additional processing to the Tomcat server.
 */
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    // TomcatWebServer是WebServer接口的一个实现
	return new TomcatWebServer(tomcat, getPort() >= 0);
}
复制代码

那再看看TomcatWebServer的构造方法

/**
 * Create a new {@link TomcatWebServer} instance.
 */
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
	Assert.notNull(tomcat, "Tomcat Server must not be null");
	this.tomcat = tomcat;
	this.autoStart = autoStart;
	initialize();
}
复制代码

再来看initialize()方法,就是启动Tomcat服务器

private void initialize() throws WebServerException {
    synchronized (this.monitor) {
        try {
            addInstanceIdToEngineName();

            Context context = findContext();
            context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource())
                        && Lifecycle.START_EVENT.equals(event.getType())) {
                    // Remove service connectors so that protocol binding doesn't
                    // happen when the service is started.
                    removeServiceConnectors();
                }
            });

            // Start the server to trigger initialization listeners
            // 在这里启动tomcat服务器
            this.tomcat.start();

            // We can re-throw failure exception directly in the main thread
            rethrowDeferredStartupExceptions();

            ContextBindings.bindClassLoader(context, context.getNamingToken(),
                        getClass().getClassLoader());


            // Unlike Jetty, all Tomcat threads are daemon threads. We create a
            // blocking non-daemon to stop immediate shutdown
            startDaemonAwaitThread();
        }
        catch (Exception ex) {
            stopSilently();
        }
    }
}
复制代码

那这样我们知道,Tomcat的服务器最终也是在AbstractApplicationContext.refresh()中被实例化,加上启动。

那第一节中提到的ServletContainerInitializer呢? 别急,回过头在,再看一次TomcatServletWebServerFactory#getWebServer()的代码 发现里面有一个ServletContextInitializer的概念

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 创建一个Tomcat实例
	Tomcat tomcat = new Tomcat();
	File baseDir = (this.baseDirectory != null) ? this.baseDirectory
			: createTempDir("tomcat");
	tomcat.setBaseDir(baseDir.getAbsolutePath());
	Connector connector = new Connector(this.protocol);
	tomcat.getService().addConnector(connector);
	customizeConnector(connector);
	tomcat.setConnector(connector);
	tomcat.getHost().setAutoDeploy(false);
	configureEngine(tomcat.getEngine());
	for (Connector additionalConnector : this.additionalTomcatConnectors) {
		tomcat.getService().addConnector(additionalConnector);
	}
	// 看看这里对initializers的处置
	prepareContext(tomcat.getHost(), initializers);
	return getTomcatWebServer(tomcat);
}
复制代码

看看TomcatServletWebServerFactory#prepareContext()的代码

注意这里面所说的Context就不是Spring的ApplicationContext,而是Tomcat的Context。

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
    File documentRoot = getValidDocumentRoot();
    // 在这里new了一个Tomcat的Context
    TomcatEmbeddedContext context = new TomcatEmbeddedContext();
    if (documentRoot != null) {
        context.setResources(new LoaderHidingResourceRoot(context));
    }
    context.setName(getContextPath());
    context.setDisplayName(getDisplayName());
    context.setPath(getContextPath());
    File docBase = (documentRoot != null) ? documentRoot
            : createTempDir("tomcat-docbase");
    context.setDocBase(docBase.getAbsolutePath());
    context.addLifecycleListener(new FixContextListener());
    context.setParentClassLoader(
            (this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
                    : ClassUtils.getDefaultClassLoader());
    resetDefaultLocaleMapping(context);
    addLocaleMappings(context);
    context.setUseRelativeRedirects(false);
    configureTldSkipPatterns(context);
    WebappLoader loader = new WebappLoader(context.getParentClassLoader());
    loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
    loader.setDelegate(true);
    context.setLoader(loader);
    if (isRegisterDefaultServlet()) {
        addDefaultServlet(context);
    }
    if (shouldRegisterJspServlet()) {
        addJspServlet(context);
        addJasperInitializer(context);
    }
    context.addLifecycleListener(new StaticResourceConfigurer(context));
    // 在这里准备ServletContextInitializer
    ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
    // 这里看到把这个Context加入到Host的下面
    // 也就是Context是Host下面的概念了
    host.addChild(context);
    // 把准备好的ServletContextInitializer装入到Context中
    configureContext(context, initializersToUse);
    postProcessContext(context);
}
复制代码

那具体有几个ServletContextInitializer呢? 看看TomcatServletWebServerFactory#mergeInitializers()的代码

/**
 * Utility method that can be used by subclasses wishing to combine the specified
 * {@link ServletContextInitializer} parameters with those defined in this instance.
 */
protected final ServletContextInitializer[] mergeInitializers(
        ServletContextInitializer... initializers) {
    /*
     ** 一共是3个ServletContextInitializer
     */
    List mergedInitializers = new ArrayList<>();
    // 第一个,是一个匿名类
    // 这里也是lambda语法
    // add()的参数其实是一个ServletContextInitializer接口的实例。
    // 那ServletContextInitializer#onStartup(ServletContext servletContext)
    // 就是一句话的(servletContext) -> this.initParameters
    //        .forEach(servletContext::setInitParameter)
    mergedInitializers.add((servletContext) -> this.initParameters
            .forEach(servletContext::setInitParameter));
    // 第二个SessionConfiguringInitializer
    mergedInitializers.add(new SessionConfiguringInitializer(this.session));
    // 第三个,也就是上面提到的selfInitialize方法,也是一个匿名类
    mergedInitializers.addAll(Arrays.asList(initializers));
    mergedInitializers.addAll(this.initializers);
    return mergedInitializers.toArray(new ServletContextInitializer[0]);
}
复制代码

也来看看configureContext()

/**
 * Configure the Tomcat {@link Context}.
 */
protected void configureContext(Context context,
        ServletContextInitializer[] initializers) {
    // 这里是关键,这就是第一节说的ServletContainerInitializer的实现
    // 可以看到刚才铺垫那么多,制造的ServletContextInitializer,都传给了TomcatStarter
    TomcatStarter starter = new TomcatStarter(initializers);
    if (context instanceof TomcatEmbeddedContext) {
        // Should be true
        ((TomcatEmbeddedContext) context).setStarter(starter);
    }
    // 把这个Tomcat实例放到context中
    context.addServletContainerInitializer(starter, NO_CLASSES);
    for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
        context.addLifecycleListener(lifecycleListener);
    }
    for (Valve valve : this.contextValves) {
        context.getPipeline().addValve(valve);
    }
    for (ErrorPage errorPage : getErrorPages()) {
        new TomcatErrorPage(errorPage).addToContext(context);
    }
    for (MimeMappings.Mapping mapping : getMimeMappings()) {
        context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
    }
    configureSession(context);
    for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
        customizer.customize(context);
    }
}
复制代码

可以看到,几个ServletContextInitializer都被以TomcatStarter的形式放到了TomcatContext中。

TomcatStarter的具体实现

来看看TomcatStarter的代码,省略了异常处理的代码

/**
 * {@link ServletContainerInitializer} used to trigger {@link ServletContextInitializer
 * ServletContextInitializers} and track startup errors.
 */
class TomcatStarter implements ServletContainerInitializer {

    private static final Log logger = LogFactory.getLog(TomcatStarter.class);

    private final ServletContextInitializer[] initializers;

    private volatile Exception startUpException;

    TomcatStarter(ServletContextInitializer[] initializers) {
        this.initializers = initializers;
    }

    @Override
    public void onStartup(Set> classes, ServletContext servletContext)
            throws ServletException {
        for (ServletContextInitializer initializer : this.initializers) {
            initializer.onStartup(servletContext);
        }

    }

    public Exception getStartUpException() {
        return this.startUpException;
    }

}
复制代码

可以看到,这个ServletContailerInitializer的实现较为特别,classes这个参数没有被使用。 而是使用了ApplicationContext中准备好的ServletContextInitializer。在onStartup中仅仅是依次调用这几个ServletContextInitializer

那上一节我们说有一个ServletContextInitializer就是ApplicationContext中的一个匿名类,最终会调用selfInitialize()方法。

private void selfInitialize(ServletContext servletContext) throws ServletException {
    prepareWebApplicationContext(servletContext);
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
            beanFactory);
    WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
            getServletContext());
    existingScopes.restore();
    WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
            getServletContext());
    // 上面的代码都先不看,直接看这里
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}
复制代码

可以看到,这个ServletContextInitializer的功能就是找到容器中的类型为ServletContextInitializer的Bean,然后调用之。

SpringMVC的自动配置

存在一个被WebMvcAutoConfiguration引入的DispatcherServletAutoConfiguration

至于WebMvcAutoConfiguration是怎么被引入的,日后再看吧。

其中有一个内部类DispatcherServletRegistrationConfiguration

看看这个内部类的代码

@Configuration
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {

    private final ServerProperties serverProperties;

    private final WebMvcProperties webMvcProperties;

    private final MultipartConfigElement multipartConfig;

    public DispatcherServletRegistrationConfiguration(
            ServerProperties serverProperties, WebMvcProperties webMvcProperties,
            ObjectProvider multipartConfigProvider) {
        this.serverProperties = serverProperties;
        this.webMvcProperties = webMvcProperties;
        this.multipartConfig = multipartConfigProvider.getIfAvailable();
    }

    /**
     * 可以看到在这里注入了一个DispatcherServletRegistrationBean
     ** 
    @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
    @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    public DispatcherServletRegistrationBean dispatcherServletRegistration(
            DispatcherServlet dispatcherServlet) {
        DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
                dispatcherServlet, this.serverProperties.getServlet().getPath());
        registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
        registration.setLoadOnStartup(
                this.webMvcProperties.getServlet().getLoadOnStartup());
        if (this.multipartConfig != null) {
            registration.setMultipartConfig(this.multipartConfig);
        }
        return registration;
    }

}
复制代码

DispatcherServlet是SpringMVC的核心

下面截取了DispatcherServlet的一部分属性和方法。

public class DispatcherServlet extends FrameworkServlet {
    /** List of HandlerMappings used by this servlet */
    @Nullable
    private List handlerMappings;

    /** List of HandlerAdapters used by this servlet */
    @Nullable
    private List handlerAdapters;
    
    /**
     * This implementation calls {@link #initStrategies}.
     */
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * 

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 HandlerMappings used by this class. *

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 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); } } /** * Initialize the HandlerAdapters used by this class. *

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 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); } } } 复制代码

看最关键的Servlet方法(省略了异常处理的代码)

/**
 * 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 {

    // Keep a snapshot of the request attributes in case of an include,
    // to be able to restore the original attributes after the include.
    Map 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);
            }
        }
    }
}
复制代码

可以看到主要调用了doDispatch()方法 继续看doDispatch()的代码

/**
 * Process the actual dispatching to the handler.
 * 

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. *

All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers * themselves to decide which methods are acceptable. */ 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实例包括了interceptor,是一个执行链 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; } } // 调用interceptor的前半部的处理 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. // 使用HandlerAdapter来处理 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); // 调用interceptor的后半部的处理 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); } } } } 复制代码

这里以RequestMappingHandlerAdatper为例

/**
 * This implementation expects the handler to be an {@link HandlerMethod}.
 */
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {
	return handleInternal(request, response, (HandlerMethod) handler);
}
复制代码

继续看

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    checkRequest(request);

    // Execute invokeHandlerMethod in synchronized block if required.
    // 这里是是判断是否有Session级别的同步处理,先不看
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No HttpSession available -> no mutex necessary
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    }
    else {
        // No synchronization on session demanded at all...
        // 可以看到,直接调用了handlerMothod
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

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

    return mav;
}
复制代码

再来看RequestMappingHandlerAdapter#invokeHandlerMethod

/**
 * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
 * if view resolution is required.
 * @since 4.2
 * @see #createInvocableHandlerMethod(HandlerMethod)
 */
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        // 做了一大堆准备就是为了创建一个invocableMethod
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            // 设置methodArgumentResolvers,这里并不做选择可以使用的,而是全设置进去
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            // 设置handlerReturnValueHandlers,这里并不做选择可以使用的,而是全设置进去
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

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

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

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

        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            if (logger.isDebugEnabled()) {
                logger.debug("Found concurrent result value [" + result + "]");
            }
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }
        // 把对Controller方法本身的执行,委托给ServletInvocableHandlerMethod类
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        webRequest.requestCompleted();
    }
}
复制代码

既然委托给了ServletInvocableHandlerMethod,那就看看ServletInvocableHandlerMethod#invokeAndHandle()

/**
 * Invoke the method and handle the return value through one of the
 * configured {@link HandlerMethodReturnValueHandler}s.
 */
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 在这里直接执行Controller方法获得returnValue
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    setResponseStatus(webRequest);

    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    mavContainer.setRequestHandled(false);
    try {
        // 在这里选择一个returnValueHandler来处理returnValue
        // 视情况是否返回ModalAndView
        this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {
        throw ex;
    }
}
复制代码
/**
 * Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it.
found.
 */
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    // 根据返回值的类型选择一个return value handler
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
    }
    
    // 接下来看这里的代码
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
复制代码
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    // 可以看到mavContainer仅仅设置了一个标志位
    // 并没有返回ModalAndView
    mavContainer.setRequestHandled(true);
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

    // Try even with null return value. ResponseBodyAdvice could get involved.
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
复制代码

这里选择的return value handler是RequestResponseBodyMethodProcessor。这个handler的适用条件是Controller的类有@ResponseBody或者方法有@ResponseBody注解。

RequestResponseBodyMethodProcessor#handleReturnValue()中,会遍历MessageConverter用来把返回值转换成HTTP消息。

那其中有一个叫MappingJackson2HttpMessageConverter的,这个Converter能处理typed bean或者HashMap类型。

转载于:https://juejin.im/post/5bb2f642e51d450e3e16c90d

你可能感兴趣的:(SpringBoot中的SpringMVC的自动配置)