SpringMVC源码-SpringMVC框架中Spring父容器和SpringMVC子容器加载的流程以及SpringMVC九大内置组件的初始

news2024/11/18 23:39:29

一、Spring父容器启动

SpringMVC 的项目结构如下:
在这里插入图片描述

applicationContext.xml spring的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="com.mashibing"></context:component-scan>
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="suffix" value=".jsp"></property>
        <property name="prefix" value="/WEB-INF/jsp/"></property>
    </bean>
</beans>

spring-config.xml SpringMVC 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id = "user" class="com.mashibing.bean.User">
        <property name="username" value="zhangsan"></property>
        <property name="age" value="12"></property>
    </bean>
</beans>

web.xml该配置文件是在tomcat启动的时候加载的

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-config.xml</param-value>
    </context-param><!--Spring加载的配置文件-->

    <servlet>
        <servlet-name>mvc-test</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--Springmvc加载启动是放在servlet中执行的 servlet的生命周期 init在HttpServletBean  service destory-->
        <!--SpringMVC配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-test</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener><!--监听器 方法的入口 这里是tomcat调用的 -->
</web-app>

<!--配置文件可写监听器 过滤器这些   springmvc是基于spring进行扩展 启动springmvc之前先把spring启动起来 先启动tomcat容器  -->

ContextLoaderListener 的初始化和实例化是tomcat容器加载web.xml文件的时候完成的,ContextLoaderListener 类下的contextInitialized初始化根web应用程序上下文。

spring是父容器和springmvc子容器两个容器的作用是为了数据的隔离,子容器有parentFactory
此链路执行开始初始化web.xml文件的监听器listener类ContextLoaderListener

<init>:56, ContextLoaderListener (org.springframework.web.context)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
newInstance:150, DefaultInstanceManager (org.apache.catalina.core)
listenerStart:4691, StandardContext (org.apache.catalina.core)
startInternal:5230, StandardContext (org.apache.catalina.core)
start:183, LifecycleBase (org.apache.catalina.util)
addChildInternal:726, ContainerBase (org.apache.catalina.core)
addChild:698, ContainerBase (org.apache.catalina.core)
addChild:696, StandardHost (org.apache.catalina.core)
manageApp:1783, HostConfig (org.apache.catalina.startup)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:293, BaseModelMBean (org.apache.tomcat.util.modeler)
invoke:819, DefaultMBeanServerInterceptor (com.sun.jmx.interceptor)
invoke:801, JmxMBeanServer (com.sun.jmx.mbeanserver)
createStandardContext:460, MBeanFactory (org.apache.catalina.mbeans)
createStandardContext:408, MBeanFactory (org.apache.catalina.mbeans)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:293, BaseModelMBean (org.apache.tomcat.util.modeler)
invoke:819, DefaultMBeanServerInterceptor (com.sun.jmx.interceptor)
invoke:801, JmxMBeanServer (com.sun.jmx.mbeanserver)
invoke:468, MBeanServerAccessController (com.sun.jmx.remote.security)
doOperation:1468, RMIConnectionImpl (javax.management.remote.rmi)
access$300:76, RMIConnectionImpl (javax.management.remote.rmi)
run:1309, RMIConnectionImpl$PrivilegedOperation (javax.management.remote.rmi)
doPrivileged:-1, AccessController (java.security)
doPrivilegedOperation:1408, RMIConnectionImpl (javax.management.remote.rmi)
invoke:829, RMIConnectionImpl (javax.management.remote.rmi)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
dispatch:346, UnicastServerRef (sun.rmi.server)
run:200, Transport$1 (sun.rmi.transport)
run:197, Transport$1 (sun.rmi.transport)
doPrivileged:-1, AccessController (java.security)
serviceCall:196, Transport (sun.rmi.transport)
handleMessages:568, TCPTransport (sun.rmi.transport.tcp)
run0:826, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
lambda$run$0:683, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
run:-1, 393312068 (sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$25)
doPrivileged:-1, AccessController (java.security)
run:682, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
runWorker:1142, ThreadPoolExecutor (java.util.concurrent)
run:617, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)

调用ContextLoaderListener构造函数
在这里插入图片描述
监听事件开始执行,初始化根web应用程序上下文。
在这里插入图片描述
event.getServletContext()的结果:包含web.xml文件的contextConfigLocation
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-config.xml</param-value> </context-param>,获取到spring需要的配置文件,接下来开始执行spring容器的启动
在这里插入图片描述
ContextLoader##initWebApplicationContext

/**
	 * Initialize Spring's web application context for the given servlet context,为给定的servlet上下文初始化Spring的web应用程序上下文。
	 * using the application context provided at construction time, or creating a new one使用在构造时提供的应用程序上下文,
	 * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and 或根据“{@link CONTEXT_CLASS_PARAM contextClass}”和“{@link CONFIG_LOCATION_PARAM contextConfigLocation}”上下文参数创建一个新上下文。
	 * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
	 * @param servletContext current servlet context
	 * @return the new WebApplicationContext
	 * @see #ContextLoader(WebApplicationContext)
	 * @see #CONTEXT_CLASS_PARAM
	 * @see #CONFIG_LOCATION_PARAM
	 */
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			// web.xml中存在多次ContextLoader定义
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}

		servletContext.log("Initializing Spring root WebApplicationContext");
		Log logger = LogFactory.getLog(ContextLoader.class);
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown. 将上下文存储在本地实例变量中,以保证它在ServletContext关闭时可用
			if (this.context == null) {//this是代指监听器 第一进去是null
				// 初始化context,第一次执行的时候获取到一个root webApplicationcontext 是在XmlWebApplicationContext的父类AbstractRefreshableWebApplicationContext构造方法setDisplayName中赋值为root webApplicationcontext
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {//判断是否激活 上下文尚未刷新->提供诸如设置父上下文、设置应用程序上下文id等服务
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent ->  context实例被注入时没有显式的父元素->
						// determine parent for root web application context, if any. 确定web应用程序根上下文的父元素(如果有的话)。
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);//子类容器也会调用 会把父容器设置进去的
					}
					configureAndRefreshWebApplicationContext(cwac, servletContext);//refresh刷新
				}
			}
			// 将创建的context对象记录在servletContext中,创建并且准备好了spring容器
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
			}

			return this.context;//IOC容器准备好勒 原生自带的spring容器
		}
		catch (RuntimeException | Error ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
	}

createWebApplicationContext
初始化context,第一次执行的时候获取到一个root webApplicationcontext 是在XmlWebApplicationContext的父类AbstractRefreshableWebApplicationContext构造方法setDisplayName中赋值displayName为root webApplicationcontext

	protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		// 获取contextClass的Class对象
		Class<?> contextClass = determineContextClass(sc);
		// 如果是自定义的contextClass对象,那么必须要实现ConfigurableWebApplicationContext此接口,否则无法直接运行
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);//进行实例化
	}

XmlWebApplicationContext类图:
XmlWebApplicationContext
在这里插入图片描述
initWebApplicationContext方法的loadParentContext:确定web应用程序根上下文的父元素(如果有的话)。子容器启动的时候会获取到父类放置进去

ContextLoader的configureAndRefreshWebApplicationContext

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value 应用程序上下文id仍然设置为其原始默认值
			// -> assign a more useful id based on available information 根据可用信息分配一个更有用的id
			String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);//CONTEXT_ID_PARAM=contextId
			if (idParam != null) {
				wac.setId(idParam);
			}
			else {
				// Generate default id... 生成唯一标识
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(sc.getContextPath()));
			}
		}

		wac.setServletContext(sc);//
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);//web.xml文件配置的 contextConfigLocation = classpath:spring-config.xml
		if (configLocationParam != null) {
			wac.setConfigLocation(configLocationParam);//设置要加载的spring配置文件
		}
        //当上下文被刷新时,wac环境的initpropertsources将在任何情况下被调用;在这里急切地确保servlet属性源在任何后处理或在刷新之前发生的初始化中使用
		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {//将基于{@code Servlet}的{@link StubPropertySource存根属性源}替换为使用给定的{@code servletContext}和{@code servletConfig}对象填充的实际实例。<p>此方法是幂等的,因为它可以被调用任意次数,但将用相应的实际属性源执行一次且仅一次的存根属性源替换。
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}

		customizeContext(sc, wac);//定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖 wac.refresh()#refresh#obtainFreshBeanFactory#refreshBeanFactory
		wac.refresh();//AbstractApplicationContext里执行调用  这步执行之后 spring容器启动成功
	}

读取到spring加载需要的配置文件 准备执行spring的刷新方法
在这里插入图片描述
wac.refresh();AbstractApplicationContext里执行调用 这步执行之后 spring容器启动成功

	postProcessBeanFactory(beanFactory);//spring项目没实现 在springMvc中启动spring会有 AbstractRefreshableWebApplicationContext 实现类


finishRefresh(); 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人,下面的publishEvent方法 将给定事件发布到所有监听器

wac.refresh执行结束之后可以看到spring配置文件的配置的bean标签被假造到一级缓存对象中去
在这里插入图片描述
user对象实例化初始化结束了
在这里插入图片描述
在这里插入图片描述
后续将创建的context对象记录在servletContext中,创建并且准备好了spring容器
在这里插入图片描述
至此spring容器启动完成,接下来开始加载springmvc容器。

二、SpringMVC子容器加载

SpringMVC是严格尊重servlet的生命周期的,初始化就是在init方法中。

    <servlet>
        <servlet-name>mvc-test</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--Springmvc加载启动是放在servlet中执行的 servlet的生命周期 init在HttpServletBean  service destory-->
        <!--SpringMVC配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>

servlet相关的类图如下:HttpServletBean的加载实例化初始化时在tomcat容器中进行完成的
在这里插入图片描述
init

	/**
	 * Map config parameters onto bean properties of this servlet 将配置参数映射到这个servlet的bean属性上,
	 * , and invoke subclass initialization.并调用子类初始化。
	 * @throws ServletException if bean properties are invalid (or required
	 * properties are missing), or if subclass initialization fails.如果bean属性无效(或者缺少必需的属性),或者子类初始化失败。
	 */
	@Override
	public final void init() throws ServletException {//该方法是在tomcat 容器中调用的

		// Set bean properties from init parameters.
		// 将servlet中配置的init-param参数封装到pvs变量中 contextConfigLocation:classpath:applicationContext.xml
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				// 将当前的servlet对象转化成BeanWrapper对象,从而能够以spring的方法来将pvs注入到该beanWrapper中
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);//this是DispatcherServlet
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				// 注册自定义属性编辑器,一旦有Resource类型的属性,将会使用ResourceEditor进行解析
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				// 模板方法,可以在子类调用,做一些初始化工作,bw代表的是DispatcherServlet
				initBeanWrapper(bw);
				// 以spring的方式来将pvs注入到该beanWrapper对象中,将配置的初始化值(contextConfigLocation)设置到DispatcherServlet
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// Let subclasses do whatever initialization they like.
		// 模板方法,子类初始化的入口方法,查看FrameworkServlet#initServletBean方法
		initServletBean();
	}

进入FrameworkServlet类的initServletBean方法
FrameworkServlet类下的initServletBean

/**{@link HttpServletBean}的重写方法,在任何bean属性设置后调用。创建这个servlet的WebApplicationContext。
	 * Overridden method of {@link HttpServletBean}, invoked after any bean properties
	 * have been set. Creates this servlet's WebApplicationContext.
	 */
	@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
		if (logger.isInfoEnabled()) {
			logger.info("Initializing Servlet '" + getServletName() + "'");
		}
		// 记录开启时间
		long startTime = System.currentTimeMillis();

		try {
			// 创建或刷新WebApplicationContext实例并对servlet功能所使用的变量进行初始化
  			this.webApplicationContext = initWebApplicationContext();
			// 模板方法,空实现,留给子类扩展
			initFrameworkServlet();
		}
		catch (ServletException | RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (logger.isDebugEnabled()) {
			String value = this.enableLoggingRequestDetails ?
					"shown which may lead to unsafe logging of potentially sensitive data" :
					"masked to prevent unsafe logging of potentially sensitive data";
			logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
					"': request parameters and headers will be " + value);
		}

		if (logger.isInfoEnabled()) {
			logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
		}
	}

FrameworkServlet类initWebApplicationContext方法

/**
	 * 此处同学们需要知道一个原理,所有的前后端交互的框架都是以servlet为基础的,所以在使用springmvc的时候,默认会把自己的容器设置成ServletContext
	 * 的属性,默认根容器的key为WebApplicaitonContext.Root,定义在WebApplicationContext中,所以在获取的时候只需要调用ServletContext.getAttribute即可
	 *
	 *
	 * Initialize and publish the WebApplicationContext for this servlet.
	 * <p>Delegates to {@link #createWebApplicationContext} for actual creation
	 * of the context. Can be overridden in subclasses.
	 * @return the WebApplicationContext instance
	 * @see #FrameworkServlet(WebApplicationContext)
	 * @see #setContextClass
	 * @see #setContextConfigLocation
	 */
	protected WebApplicationContext initWebApplicationContext() {
		// 获得根webApplicationContext对象
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		// 获得webApplicationContext wac对象
		WebApplicationContext wac = null;

		// 如果构造方法中已经传入webApplicationContext属性,则直接使用
		// 此方式主要用于servlet3.0之后的环境,也就是说可以通过ServletContext.addServlet的方法注册servlet,此时就可以在创建FrameworkServlet和
		// 其子类的时候通过构造方法传递已经准备好的webApplicationContext
		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time 在构造时注入了上下文实例 -> use it
			wac = this.webApplicationContext;
			// 如果是ConfigurationWebApplicationContext类型,并且未激活,则进行初始化
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				// 未激活
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					// 配置和刷新上下文环境
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		// 从servletContext获取对应的webApplicationContext对象
		// 此方式需要在配置Servlet的时候将servletContext中的webApplicationContext的name配置到contextAttribute属性就可以
		/*
		<servlet>
        <servlet-name>mvc-test</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--SpringMVC配置文件-->
        <init-param>
            <param-name>contextAttribute</param-name>
            <param-value>mashibing</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
		 */
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		// 当前面两种方式都无效的情况下会创建一个webApplicationContext对象,一般情况下都是使用这样的方式
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		// 将contextRefreshedEvent事件没有触发时调用此方法,模板方法,可以在子类重写
		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}

		// 将applicationContext设置到servletContext中
		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}

这里把spring容器设置为springmvc的父容器,看parent的一级缓存有spring配置文件的bean标签实例化的对象就可以知道了
在这里插入图片描述
ServletContext servlet的上下文对象,包含整个web.xml文件的配置
ServletConfig 被某个servlet持有,对应的servlet中的配置属性
添加监听器以及对应监听器的监听的事件类型
添加监听器sourceFilteringListener到wac中,实际监听的是ContextRefreshListener所监听的事件,监听ContextRefreshedEvent事件
当接收到消息之后会调用onApplicationEvent方法,调用onRefresh方法,并将refreshEventReceived标志设置为true,表示已经refresh过
this.applicationListeners.add(listener);AbstractApplicationContext的applicationListeners集合有了一个监听器
在这里插入图片描述
ContextRefreshListener

	/**
	 * Callback that receives refresh events from this servlet's WebApplicationContext.从servlet的WebApplicationContext接收刷新事件的回调
	 * <p>The default implementation calls {@link #onRefresh},
	 * triggering a refresh of this servlet's context-dependent state.默认的实现调用{@link onRefresh},触发这个servlet的上下文依赖状态的刷新。
	 * @param event the incoming ApplicationContext event 传入的ApplicationContext事件
	 */
	public void onApplicationEvent(ContextRefreshedEvent event) {
		// 标记 refreshEventReceived 为true
		this.refreshEventReceived = true;
		synchronized (this.onRefreshMonitor) {
			// 处理事件中的 ApplicationContext 对象,空实现,子类DispatcherServlet会实现
			onRefresh(event.getApplicationContext());
		}
	}

**wac.refresh();**开始刷新wac,从而初始化wac,现在完成SpringMVC的容器创建,加载配置文件生成对象

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			/**
			 * 前戏,做容器刷新前的准备工作
			 * 1、设置容器的启动时间
			 * 2、设置活跃状态为true
			 * 3、设置关闭状态为false
			 * 4、获取Environment对象,并加载当前系统的属性值到Environment对象中
			 * 5、准备监听器和事件的集合对象,默认为空的集合
			 */

			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory. 告诉子类刷新内部bean工厂。
			// 创建容器对象:DefaultListableBeanFactory
			// 加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinition
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			// beanFactory的准备工作,对各种属性进行填充
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				// 子类覆盖方法做额外的处理,此处我们自己一般不做任何扩展工作,但是可以查看web中的代码,是有具体实现的
				postProcessBeanFactory(beanFactory);//spring项目没实现 在springMvc中启动spring会有 AbstractRefreshableWebApplicationContext 实现类

				// Invoke factory processors registered as beans in the context.
				// 调用各种beanFactory处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 注册bean处理器,这里只是注册功能,真正调用的是getBean方法
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				// 为上下文初始化message源,即不同语言的消息体,国际化处理,在springmvc的时候通过国际化的代码重点讲
				initMessageSource();

				// Initialize event multicaster for this context.
				// 初始化事件监听多路广播器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				// 留给子类来初始化其他的bean
				onRefresh();//AbstractRefreshableWebApplicationContext 实现了

				// Check for listener beans and register them.
				// 在所有注册的bean中查找listener bean,注册到消息广播器中
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 初始化剩下的单实例(非懒加载的)
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
				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.
				// 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
				destroyBeans();

				// Reset 'active' flag.
				// 重置active标志
				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();
			}
		}
	}

refresh方法调用finishRefresh();

完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
protected void finishRefresh() {
		// Clear context-level resource caches (such as ASM metadata from scanning).
		// 清除上下文级别的资源缓存(如扫描的ASM元数据)
		// 清空在资源加载器中的所有资源缓存
		clearResourceCaches();
		// Initialize lifecycle processor for this context.
		// 为这个上下文初始化生命周期处理器
		// 初始化LifecycleProcessor.如果上下文中找到'lifecycleProcessor'的LifecycleProcessor Bean对象,
		// 则使用DefaultLifecycleProcessor
		initLifecycleProcessor();
		// Propagate refresh to lifecycle processor first.
		// 首先将刷新传播到生命周期处理器
		// 上下文刷新的通知,例如自动启动的组件
		getLifecycleProcessor().onRefresh();
		// Publish the final event.
		// 发布最终事件
		// 新建ContextRefreshedEvent事件对象,将其发布到所有监听器。
		publishEvent(new ContextRefreshedEvent(this));
		// Participate in LiveBeansView MBean, if active.
		// 参与LiveBeansView MBean,如果是活动的
		// LiveBeansView:Sping用于支持JMX 服务的类
		// 注册当前上下文到LiveBeansView,以支持JMX服务
		LiveBeansView.registerApplicationContext(this);
	}

initStrategies是初始化springMVC内置组件的方法,先贴出调用的方法轨迹:
publishEvent发布最终事件开始,一直到调用initStrategies,都是监听器相关

initStrategies:523, DispatcherServlet (org.springframework.web.servlet)
onRefresh:514, DispatcherServlet (org.springframework.web.servlet)
onApplicationEvent:901, FrameworkServlet (org.springframework.web.servlet)
onApplicationEvent:1277, FrameworkServlet$ContextRefreshListener (org.springframework.web.servlet)
onApplicationEvent:1273, FrameworkServlet$ContextRefreshListener (org.springframework.web.servlet)
onApplicationEvent:64, GenericApplicationListenerAdapter (org.springframework.context.event)
onApplicationEventInternal:109, SourceFilteringListener (org.springframework.context.event)
onApplicationEvent:73, SourceFilteringListener (org.springframework.context.event)
doInvokeListener:215, SimpleApplicationEventMulticaster (org.springframework.context.event)
invokeListener:202, SimpleApplicationEventMulticaster (org.springframework.context.event)
multicastEvent:164, SimpleApplicationEventMulticaster (org.springframework.context.event)
publishEvent:440, AbstractApplicationContext (org.springframework.context.support)
publishEvent:379, AbstractApplicationContext (org.springframework.context.support)
finishRefresh:1053, AbstractApplicationContext (org.springframework.context.support)
refresh:618, AbstractApplicationContext (org.springframework.context.support)
configureAndRefreshWebApplicationContext:759, FrameworkServlet (org.springframework.web.servlet)
createWebApplicationContext:715, FrameworkServlet (org.springframework.web.servlet)
createWebApplicationContext:773, FrameworkServlet (org.springframework.web.servlet)
initWebApplicationContext:625, FrameworkServlet (org.springframework.web.servlet)
initServletBean:536, FrameworkServlet (org.springframework.web.servlet)
init:185, HttpServletBean (org.springframework.web.servlet)
init:158, GenericServlet (javax.servlet)
initServlet:1164, StandardWrapper (org.apache.catalina.core)
loadServlet:1117, StandardWrapper (org.apache.catalina.core)
load:1010, StandardWrapper (org.apache.catalina.core)
loadOnStartup:4957, StandardContext (org.apache.catalina.core)
startInternal:5264, StandardContext (org.apache.catalina.core)
start:183, LifecycleBase (org.apache.catalina.util)
addChildInternal:726, ContainerBase (org.apache.catalina.core)
addChild:698, ContainerBase (org.apache.catalina.core)
addChild:696, StandardHost (org.apache.catalina.core)
manageApp:1783, HostConfig (org.apache.catalina.startup)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:293, BaseModelMBean (org.apache.tomcat.util.modeler)
invoke:819, DefaultMBeanServerInterceptor (com.sun.jmx.interceptor)
invoke:801, JmxMBeanServer (com.sun.jmx.mbeanserver)
createStandardContext:460, MBeanFactory (org.apache.catalina.mbeans)
createStandardContext:408, MBeanFactory (org.apache.catalina.mbeans)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:293, BaseModelMBean (org.apache.tomcat.util.modeler)
invoke:819, DefaultMBeanServerInterceptor (com.sun.jmx.interceptor)
invoke:801, JmxMBeanServer (com.sun.jmx.mbeanserver)
invoke:468, MBeanServerAccessController (com.sun.jmx.remote.security)
doOperation:1468, RMIConnectionImpl (javax.management.remote.rmi)
access$300:76, RMIConnectionImpl (javax.management.remote.rmi)
run:1309, RMIConnectionImpl$PrivilegedOperation (javax.management.remote.rmi)
doPrivileged:-1, AccessController (java.security)
doPrivilegedOperation:1408, RMIConnectionImpl (javax.management.remote.rmi)
invoke:829, RMIConnectionImpl (javax.management.remote.rmi)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
dispatch:346, UnicastServerRef (sun.rmi.server)
run:200, Transport$1 (sun.rmi.transport)
run:197, Transport$1 (sun.rmi.transport)
doPrivileged:-1, AccessController (java.security)
serviceCall:196, Transport (sun.rmi.transport)
handleMessages:568, TCPTransport (sun.rmi.transport.tcp)
run0:826, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
lambda$run$0:683, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
run:-1, 1102395047 (sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$25)
doPrivileged:-1, AccessController (java.security)
run:682, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
runWorker:1142, ThreadPoolExecutor (java.util.concurrent)
run:617, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)

三、SpringMVC 9大内置组件开始进行初始化

initStrategies初始化

	/**初始化
	 * 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) {
		// 初始化 MultipartResolver:主要用来处理文件上传.如果定义过当前类型的bean对象,那么直接获取,如果没有的话,可以为null
		initMultipartResolver(context);
		// 初始化 LocaleResolver:主要用来处理国际化配置,基于URL参数的配置(AcceptHeaderLocaleResolver),基于session的配置(SessionLocaleResolver),基于cookie的配置(CookieLocaleResolver)
		initLocaleResolver(context);
		// 初始化 ThemeResolver:主要用来设置主题Theme
		initThemeResolver(context);
		// 初始化 HandlerMapping:映射器,用来将对应的request跟controller进行对应
		initHandlerMappings(context);
		// 初始化 HandlerAdapter:处理适配器,主要包含Http请求处理器适配器,简单控制器处理器适配器,注解方法处理器适配器
		initHandlerAdapters(context);
		// 初始化 HandlerExceptionResolver:基于HandlerExceptionResolver接口的异常处理
		initHandlerExceptionResolvers(context);
		// 初始化 RequestToViewNameTranslator:当controller处理器方法没有返回一个View对象或逻辑视图名称,并且在该方法中没有直接往response的输出流里面写数据的时候,spring将会采用约定好的方式提供一个逻辑视图名称
		initRequestToViewNameTranslator(context);
		// 初始化 ViewResolver: 将ModelAndView选择合适的视图进行渲染的处理器
		initViewResolvers(context);
		// 初始化 FlashMapManager: 提供请求存储属性,可供其他请求使用
		initFlashMapManager(context);
	}

1.初始化 MultipartResolver:主要用来处理文件上传
初始化 MultipartResolver:主要用来处理文件上传.如果定义过当前类型的bean对象,那么直接获取,如果没有的话,可以为null
initMultipartResolver(context);

	/**
	 * Initialize the MultipartResolver used by this class. 初始化该类使用的MultipartResolver。
	 * <p>If no bean is defined with the given name in the BeanFactory for this namespace, 如果在BeanFactory中没有为这个名称空间定义带有给定名称的bean,
	 * no multipart handling is provided. 不提供多部分处理。
	 */
	private void initMultipartResolver(ApplicationContext context) {
		try {
			// 从spring上下文获取名称为 multipartResolver ,类型为MultipartResolver的Bean  实现multipartResolver接口 可以自己定义上传文件组件 本web.xml文件没有自定义 所以此处获取不到的
			this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);//multipartResolver
			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");
			}
		}
	}

2.初始化 LocaleResolver:主要用来处理国际化配置

初始化 LocaleResolver:主要用来处理国际化配置,基于URL参数的配置(AcceptHeaderLocaleResolver),基于session的配置(SessionLocaleResolver),基于cookie的配置(CookieLocaleResolver)
		initLocaleResolver(context);
	private void  initLocaleResolver(ApplicationContext context) {
		try {
			// 从上下文中获取Bean名称为 LocaleResolver的对象
			this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);//localeResolver 进行国际化相关处理
			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.
			// 从配置文件中获取默认的AcceptHeaderLocaleResolver对象
			this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
						"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
			}
		}
	}
	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_test.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) {
		// 获得strategyInterface对应的value值
		String key = strategyInterface.getName();
		// 创建value对应的对象们,并返回
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
			// 基于","分隔,创建classNames数组
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
			// 创建strategyInterface集合
			List<T> strategies = new ArrayList<>(classNames.length);
			// 遍历classNames数组,创建对应的类,添加到strategyInterface中
			for (String className : classNames) {
				try {
					// 获得className类
					Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
					// 创建className对应的类,并添加到strategies中
					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<>();
		}
	}

defaultStrategies的初始化是在:这个静态代码块
在这里插入图片描述
DEFAULT_STRATEGIES_PATH类路径资源的名称(相对于DispatcherServlet类),它定义了DispatcherServlet的默认策略名称。
private static final String DEFAULT_STRATEGIES_PATH = “DispatcherServlet_test.properties”;
在这里插入图片描述
DispatcherServlet_test.properties

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
	org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

3.初始化 ThemeResolver:主要用来设置主题Theme
// 初始化 ThemeResolver:主要用来设置主题Theme
initThemeResolver(context);

	/**
	 * 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.
			// 从配置文件中获取默认的FixedThemeResolver
			this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No ThemeResolver '" + THEME_RESOLVER_BEAN_NAME +
						"': using default [" + this.themeResolver.getClass().getSimpleName() + "]");
			}
		}
	}

4.初始化 HandlerMapping:映射器,用来将对应的request跟controller进行对应
// 初始化 HandlerMapping:映射器,用来将对应的request跟controller进行对应
initHandlerMappings(context);

	/**初始化该类使用的HandlerMappings。<p>如果没有在BeanFactory中为这个命名空间定义HandlerMapping bean,我们默认为BeanNameUrlHandlerMapping。
	 * 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) {
		// 将handlerMappings置空
		this.handlerMappings = null;

		// 如果开启探测功能,则扫描已注册的HandlerMapping的bean,添加到handlerMappings中
		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			// 扫描已注册的handlerMapping的bean
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			// 添加到handlerMappings中,并进行排序
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		// 如果关闭探测功能,则获取Bean名称为handlerMapping对应的bean,将其添加到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.
		// 如果未获得到,则获得默认配置的handlerMapping类
		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_test.properties");
			}
		}
	}

默认配置的handlerMapping
在这里插入图片描述

5.初始化 HandlerAdapter:处理适配器
初始化 HandlerAdapter:处理适配器,主要包含Http请求处理器适配器,简单控制器处理器适配器,注解方法处理器适配器
initHandlerAdapters(context);

	/**
	 * 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.
		// 如果未获得到,则获得默认配置的HandlerAdapter类,HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,RequestMappingHandlerAdapter
		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_test.properties");
			}
		}

默认配置的HandlerAdapter类,
在这里插入图片描述
6.初始化 HandlerExceptionResolver
初始化 HandlerExceptionResolver:基于HandlerExceptionResolver接口的异常处理
initHandlerExceptionResolvers(context);

	/**
	 * 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) {
		// 置空 handlerExceptionResolver 处理
		this.handlerExceptionResolvers = null;

		// 自动扫描handlerExceptionResolver类型的bean
		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);
			}
		}
		// 获取名字为HANDLER_EXCEPTION_RESOLVER_BEAN_NAME的bean
		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.
		// 如果未获得到,则获取默认配置的handlerExceptionResolver类,ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver,DefaultHandlerExceptionResolver
		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_test.properties");
			}
		}
	}

在这里插入图片描述

7.初始化 RequestToViewNameTranslator
初始化 RequestToViewNameTranslator:当controller处理器方法没有返回一个View对象或逻辑视图名称,并且在该方法中没有直接往response的输出流里面写数据的时候,spring将会采用约定好的方式提供一个逻辑视图名称
initRequestToViewNameTranslator(context);

/**
	 * 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.
			// 如果未找到,则获取默认的 RequestToViewNameTranslator 对象,DefaultRequestToViewNameTranslator
			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() + "]");
			}
		}
	}

在这里插入图片描述

8.初始化 ViewResolver
初始化 ViewResolver: 将ModelAndView选择合适的视图进行渲染的处理器
initViewResolvers(context);

/**
	 * 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) {
		// 置空 viewResolvers 处理
		this.viewResolvers = null;

		/// 自动扫描 ViewResolver 类型的 Bean
		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);
			}
		}
		// 获得名字为VIEW_RESOLVER_BEAN_NAME的bean
		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.
		// 如果未获得到,则获取默认配置的ViewResolver对象
		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_test.properties");
			}
		}
	}

9.初始化 FlashMapManager
初始化 FlashMapManager: 提供请求存储属性,可供其他请求使用
initFlashMapManager(context);

/**
	 * 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.
			// 未找到,则获取默认的 FlashMapManager 对象,SessionFlashMapManager
			this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No FlashMapManager '" + FLASH_MAP_MANAGER_BEAN_NAME +
						"': using default [" + this.flashMapManager.getClass().getSimpleName() + "]");
			}
		}
	}

SpringMVC框架中Spring父容器和SpringMVC子容器加载的流程以及SpringMVC九大内置组件的初始已经完成接下来开始进行发送请求相关的源码。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2172661.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Boot 进阶-如何自定义SpringBoot日志配置?

在之前的文章中我们介绍了Spring Boot中的日志框架,并且也介绍了SpringBoot日志框架中日志级别的调整。这篇文章我们主要来介绍关于如何让日志框架更加符合我们自己的需求。那么首先我们就来看一下日志文件输出路径的配置。 如何指定日志文件的输出位置 在Spring Boot中日志是…

Keepalived+MySQL 高可用集群

基础架构如下 准备干净的实验环境 [rootmysql1 ~]# systemctl stop firewalld [rootmysql1 ~]# cat /etc/sysconfig/selinux |grep "SELINUXdisabled" SELINUXdisabled [rootmysql1 ~]# setenforce 0 setenforce: SELinux is disabled [rootmysql1 ~…

动静态库(Linux)

文章目录 前言一、静态库二、动态库三、深入理解动态库总结 前言 我们之前用过c语言的库.Linux中默认的都是使用动态库&#xff0c;如果想要使用静态库&#xff0c;就必须加上-static选项。默认都是安装的动态库&#xff0c;系统中一般没有静态库&#xff0c;如果要使用&#…

算法复杂度之时间复杂度

一 . 数据结构前言 1.1 数据结构 数据结构(Data structure) 是计算机存储&#xff0c;组织数据的方式&#xff0c;指互相之间存在一种或多种特定关系的数据元素的集合。没有一种单一的数据结构对所有用途都有用&#xff0c;所以要学习各式各样的数据结构&#xff0c;如&#…

使用kaggle命令下载数据集

目录 报错 解决方案 报错 使用kaggle命令下载数据集报错了&#xff0c; 解决方案 &#xff08;1&#xff09;首先&#xff0c;确保已经安装 Python 和包管理器 pip。 运行以下命令以使用命令行访问 Kaggle API&#xff1a; pip install kaggle 可能需要在 Mac/Linux 上执行…

【BurpSuite】SQL注入 | SQL injection(1-2)

&#x1f3d8;️个人主页&#xff1a; 点燃银河尽头的篝火(●’◡’●) 如果文章有帮到你的话记得点赞&#x1f44d;收藏&#x1f497;支持一下哦 【BurpSuite】SQL注入 | SQL injection&#xff08;1-2&#xff09; 实验一 Lab: SQL injection vulnerability in WHERE clause…

Maven项目常见各类 QA

一、pom.xml文件 1.1 there is no POM in this directory [ERROR] The goal you specified requires a project to execute but there is no POM in this directory (/home/cys/SEtesting/example/smartut-report). Please verify you invoked Maven from the correct directo…

如何实现工业设备联网?天拓四方

一、引言 随着信息技术的快速发展&#xff0c;工业设备联网已成为推动工业4.0和智能制造的核心技术之一。工业设备联网通过将传统的工业设备与互联网、云计算、大数据等技术相结合&#xff0c;实现了设备之间的互联互通&#xff0c;数据共享与智能分析&#xff0c;极大地提高了…

【计算机网络 - 基础问题】每日 3 题(二十七)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

机器学习-KNN

KNN&#xff1a;K最邻近算法&#xff08;K-Nearest Neighbor,KNN&#xff09; 用特征空间中距离待分类对象的最近的K个样例点的类别来预测。 投票法&#xff1a;K 个样例的对数类别。 k1:最近邻分类 k 通常是奇数&#xff08;因为我们根据这个K数据判断类别&#xff0c;如果…

敏感字段加密 - 华为OD统一考试(E卷)

2024华为OD机试(E卷+D卷+C卷)最新题库【超值优惠】Java/Python/C++合集 题目描述 【敏感字段加密】给定一个由多个命令字组成的命令字符串: 1、字符串长度小于等于127字节,只包含大小写字母,数字,下划线和偶数个双引号; 2、命令字之间以一个或多个下划线 进行分割; 3、可…

Spring Cloud Gateway接入WebSocket:实现实时通信

在现代的微服务架构中&#xff0c;实时通信变得越来越重要。Spring Cloud Gateway作为Spring Cloud生态中的API网关&#xff0c;提供了动态路由、监控、弹性、安全等功能。本文将介绍如何通过Spring Cloud Gateway接入WebSocket&#xff0c;实现服务之间的实时通信。 为什么需…

1. AOSP源码导入到AndroidStudio

1. AOSP源码导入到AndroidStudio 原文地址:http://www.androidcrack.com/index.php/archives/6/ ⚠️ 在执行一下操作前, 请先完整的编译一次系统, 若不清楚如何编译系统. 请访问下面文章 http://www.androidcrack.com/index.php/archives/3/ 1. 生成idegen.jar source build…

Python中流行的开源OCR项目

以下是一些Python中流行的开源OCR项目&#xff1a; PaddleOCR&#xff1a;由百度开发的OCR工具库&#xff0c;支持多种语言的文字识别&#xff0c;包括中英文&#xff0c;同时支持倾斜、竖排等多种方向的文字识别。它提供了超轻量级的PP-OCRv3模型&#xff0c;适合在资源受限的…

EasyExcel使用介绍

EasyExcel使用 1、EasyExcel介绍 1.1 官网介绍 传统操作Excel大多都是利用Apach POI进行操作的&#xff0c;但是POI框架并不完善&#xff0c;使用过程非常繁琐且有较多的缺陷&#xff1a; 动态操作Excel非常繁琐,对于新手来说&#xff0c;很难在短时间内上手;读写时需要占用…

助力企业信息化,开源免费工作流引擎AntFlow推出重榜功能tidb支持,为工作流引擎水平扩展提供无限可能

在现代企业管理中&#xff0c;流程审批的高效性直接影响到工作的流畅度与生产力。选择一款高效的、现代的、合适企业办公特点的流程引擎到头重要。AntFlow是一款结合中国式办公精心打造的、设计上仿钉钉的工作流引擎。后端即可嵌入到现有业务系统&#xff0c;也可以做为独立的流…

打造未来社交:区块链社交DAO的颠覆性开发之路

随着区块链技术的不断发展&#xff0c;去中心化自治组织&#xff08;DAO&#xff09;逐渐成为一种创新的社交模式。结合区块链的透明性和不可篡改性&#xff0c;社交DAO为用户提供了一种全新的参与和治理方式&#xff0c;重塑了社交网络的构建与互动方式。本文将探讨区块链社交…

【工具-VMware Workstation-ubuntu】

VMware Workstation-ubuntu ■ ubuntu 和 win11 共享文件夹出现在/mnt/hgfs 目录下。■■■■ ■ ubuntu 和 win11 共享文件夹出现在/mnt/hgfs 目录下。 执行 vmware-hgfsclient 显示有共享文件夹。ls 却查看不到 执行 sudo vmhgfs-fuse .host:/ /mnt/hgfs -o nonempty -o all…

828华为云征文|部署漫画阅读器和管理器 Teemii

828华为云征文&#xff5c;部署漫画阅读器和管理器 Teemii 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 Teemii3.1 Teemii 介绍3.2 Teemii 部署3.3 Teemii 使用 四、总结…

英伟达新显卡RTX 5090和5080曝光,性能差距惊人!涨价已成定局?

你是不是也觉得现在的电脑游戏越来越吃配置了&#xff1f;别急&#xff0c;英伟达即将发布的两款新显卡 RTX 5090 和 RTX 5080 可能会成为你的救星。不过&#xff0c;这两款显卡之间的性能差距可不是一星半点&#xff0c;而且价格可能也会让人咋舌。想知道更多细节吗&#xff1…