spring webmvc启动流程

1.servlet注册

Servlet的注册与运行

  • Servlet程序必须通过Servlet容器来启动运行,并且存储目录有特殊的要求,通常需要存储在<WEB应用程序目录>\WEB-INF\classes\目录中
  • Servlet程序必须在WEB应用程序的web.xml文件中进行注册和映射其访问路径,才可以被Servlet引擎加载和被外界访问
  • 一个元素用于注册一个Servlet,它包含两个主要的子元素:和,分别用于设置Servlet的注册名称和Servlet完整的类名。
  • 一个元素用于映射一个已经注册的Servlet的一个对外访问路径,包含有两个子元素:和,分别用于指定Servlet的注册名称和Servlet的对外访问路径。

Servlet的映射细节

  • 同一个Servlet可以被映射到多个URL上,即多个元素的子元素的设置值可以是同一个Servlet的注册名。
  • 在Servlet映射到URL中也可以使用通配符,但是只能有两种固定的格式:一种格式是“.扩展名”,另一种格式是以“/开头以“/*”结尾。
<servlet-mapping>
 <!-- 需要一个和Servlet节点的Servlet-name子节点的文本节点一致 -->
 <servlet-name>HelloServlet</servlet-name>
 <!-- 映射具体的访问路径:/代表当前WEB应用的根目录 -->
 <url-pattern>*.do</url-pattern>
 </servlet-mapping>

<servlet-mapping>
 <!-- 需要一个和Servlet节点的Servlet-name子节点的文本节点一致 -->
 <servlet-name>HelloServlet</servlet-name>
 <!-- 映射具体的访问路径:/代表当前WEB应用的根目录 -->
 <url-pattern>/*</url-pattern>
 </servlet-mapping>

ServletConfig:
封装了Servlet的配置信息,并且可以获取ServletContext对象

配置Servlet的初始化参数:

 <!-- 配置Servlet的初始化参数  .该节点必须在load-on-startup 节点的前面-->
 <init-param>
 <!-- 参数名 -->
 <param-name>user</param-name>
 <!-- 参数值 -->
 <param-value>root</param-value>
 </init-param>
 
  <init-param>
 <!-- 参数名 -->
 <param-name>password</param-name>
 <!-- 参数值 -->
 <param-value>12306</param-value>
 </init-param>

获取Servlet的初始化参数
getInitParameter(String name):获取指定参数名的初始化参数

getInitParameterNames():获取参数名组成的Enumeration对象

 
	@Override
	public void init(ServletConfig servletconfig) throws ServletException {
		System.out.println("init");
		
		String user=servletconfig.getInitParameter("user");
		System.out.println("user:"+user);
		
		Enumeration<String > names=servletconfig.getInitParameterNames();
		while(names.hasMoreElements()){
			String name=names.nextElement();
			String value=servletconfig.getInitParameter(name);
			System.out.println("^^"+name+":"+value);
		}
	}

getServletName:获取Servlet的配置名称

2.IoC容器创建

在测试时,经常使用这种方式来创建spring容器

/创建基于注解的springIOC容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopBeanConfig.class);
//创建基于配置文件的springIOC容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-beans.xml");

无论哪种方式,最终都会调用AbstractApplicationContext的一个重要方法——refresh(),首先来看这个方法的spring源码

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

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

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

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

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

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

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

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

                // 9. Initialize other special beans in specific context subclasses.
                onRefresh();

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

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

                // 12. 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();
            }
        }
    }

重点步骤简析

  1. prepareRefresh 准备刷新容器

(1) initPropertySources() 自定义属性设置,空方法,留给子类继承

(2) getEnvironment.validateRequiredProperties 首先获取环境配置,然后校验必需属性

(3) this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
初始化事件监听器

(4) this.earlyApplicationEvents = new LinkedHashSet<>(); 初始化早期事件

  1. obtainFreshBeanFactory 获取组件工厂

(1) refreshBeanFactory 新建一个组件工厂,类型为DefaultListableBeanFactory, 然后对这个组件工厂设置了一个序列化ID

(2) getBeanFactory 返回刚刚创建的组件工厂

  1. prepareBeanFactory 对组件工厂做各种预处理设置

(1) 在组件工厂中设置类加载器、属性解析器等

(2) 在组件工厂中添加部分组件后置处理器,例如ApplicationContextAwareProcessor、ApplicationListenerDetector

(3) 在组件工厂中设置忽略自动注入的接口

(4) 设置自动装配规则

(5) 在组件工厂中注册一些组件,例如环境配置ConfigurableEnvironment

  1. postProcessBeanFactory 组件工厂的后置处理工作

  2. invokeBeanFactoryPostProcessors 执行组件工厂后置处理器

这一步是在组件工厂的标准初始化(1-4)之后进行的,主要是执行BeanFactoryPostProcessor及其子接口的

BeanFactoryPostProcessor的子接口主要是指BeanDefinitionRegistryPostProcessor,可以向容器中注册新的组件,这个接口的特点是有两个方法,一个是自身的postProcessBeanDefinitionRegistry,另一个继承自BeanFactoryPostProcessor的postProcessBeanFactory,从源码可以看出,spring会先执行BeanDefinitionRegistryPostProcessor类型的组件的自身方法,然后执行其继承方法,最后才调用非BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor的后置处理方法

(1) 从容器器中获取BeanDefinitionRegistryPostProcessor类型的组件

(2) 将BeanDefinitionRegistryPostProcessor类型的组件按照顺序分类并排序,即是否实现了PriorityOrdered、Ordered接口

(3) 依次执行实现了PriorityOrdered接口的、实现了Ordered接口的、没有实现任何顺序接口的组件的postProcessBeanDefinitionRegistry方法

(4) 执行所有BeanDefinitionRegistryPostProcessor组件的postProcessBeanFactory方法

(5) 从容器中获取其他的BeanFactoryPostProcessor类型的组件,即不是BeanDefinitionRegistryPostProcessor类型的

(6) 剩下的步骤跟上面类似,就是先按照实现的顺序接口分类,在每个类别下排序,然后依次执行它们的postProcessBeanFactory方法

  1. registerBeanPostProcessors 注册组件后置处理器,这种处理器用于拦截bean的创建过程

beanPostProcessor有很多子接口,每种子接口的执行时机各有不同

|-DestructionAwareBeanPostProcessor

|-InstantiationAwareBeanPostProcessor

|-MergedBeanDefinitionPostProcessor

|-SmartInstantiationAwareBeanPostProcessor

(1) 获取所有的beanPostProcessor的组件名

(2) 将所有的组件按优先顺序分为三类:

|-实现了PriorityOrdered接口的列表priorityOrderedPostProcessors

|-实现了Ordered接口的列表orderedPostProcessors

|-没有实现任何顺序接口的列表nonOrderedPostProcessors

还有一种特殊情况,凡是MergedBeanDefinitionPostProcessor类型的,都放在internalPostProcessors中

(3) 注册priorityOrderedPostProcessors

(4) 注册orderedPostProcessors

(5) 注册nonOrderedPostProcessors

(6) 注册internalPostProcessors

(7) 注册ApplicationListenerDetector,它的作用是在组件初始化之后判断其是否为 ApplicationListner类型,如果是,则将其添加进容器的监听器集合

  1. initMessageSource 初始化消息源组件,用于消息绑定、消息解析等功能,并且提供国际化解决方案

(1) 获取beanFactory

(2) 判断beanFactory中是否包含id为messageSource的组件

(3) 如果已存在,则赋值给容器的messageSource属性,这种情况是我们自己在容器中注册了这个组件

(4) 如果不存在,则新建一个DelegatingMessageSource,并赋值给容器的messageSource属性, 然后在beanFactory中注册这个新组件,并设置其id为messageSource

  1. initApplicationEventMulticaster 初始化事件广播器

(1) 获取beanFactory

(2) 判断beanFactory中是否存在id为applicationEventMulticaster的组件

(3) 如果已存在,则赋值给容器的applicationEventMulticaster属性,这种情况是我们自己在容器中注册了这个组件

(4) 如果不存在,则新建一个SimpleApplicationEventMulticaster,并赋值给容器的applicationEventMulticaster属性,然后在beanFactory中注册这个新组件,并设置其id为applicationEventMulticaster

  1. onRefresh 没有任何操作,留给子类继承的,我们可以自定义子容器,在重写方法中做一些我们想要的操作

  2. registerListeners 注册事件监听器

(1) 获取容器的属性applicationListeners,这是一个事件监听器的集合,将集合中的每个元素都添加进事件广播器

(2) 从容器中获取所有ApplicationListener类型的组件,将这些组件添加进事件广播器

(3) 发布早期事件,即容器的earlyApplicationEvents属性(参考第1(4)步),然后清空早期事件

  1. finishBeanFactoryInitialization 完成剩下的单实例bean的初始化

(1) 进入DefaultListableBeanFactory.preInstantiateSingletons方法,获取容器中所有的组件id列表

(2) 遍历组件id列表,对每个组件,获取其组件定义信息,即RootBeanDefinition

(3) 从组件定义信息中筛选掉抽象类、非单实例、懒加载的,这些bean在创建容器时并不初始化,另外还有工厂组件,即实现了FactoryBean接口的,需要另外一套逻辑进行初始化

(4) 从缓存中获取单实例bean,即DefaultSingletonBeanRegistry类的singletonObjects属性,所有被创建过的单实例bean都会被缓存在这个映射中;如果缓存中存在,说明这个组件之前被创建过,直接返回

(5) 如果缓存中不存在,则开始新建

① 将组件标记为已创建,即将其id存入AbstractBeanFactory的alreadyCreated属性中

② 获取组件的定义信息,即RootBeanDefinition

③ 从定义信息中获取该组件依赖的组件,如果存在,则重新从第11(4)步开始执行,创建这些依赖的组件

④ 创建完依赖组件(如果存在)之后,以下开始正式新建目标组件

⑤ 给组件后置处理器一个机会用代理对象代替目标对象,即执行InstantiationAwareBeanPostProcessor 类型的组件后置处理器的postProcessBeforeInstantiation、postProcessAfterInitialization方法

⑥ Allow post-processors to modify the merged bean definition,即执行MergedBeanDefinitionPostProcessor类型组件后置处理器的postProcessMergedBeanDefinition方法

⑦ 执行AbstractAutowireCapableBeanFactory.populateBean方法,即属性赋值。在属性赋值之前,首先拿到InstantiationAwareBeanPostProcessor类型的组件后置处理器,并执行postProcessAfterInstantiation、postProcessProperties、postProcessPropertyValues方法;

然后才执行该类的applyPropertyValues方法,利用反射调用组件的setter方法进行属性赋值
    ⑧ 执行以下三种aware接口的方法:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
    ⑨ 执行组件后置处理器的初始化前方法,即BeanPostProcessor.postProcessBeforeInitialization
⑩ 执行组件的初始化方法,即InitializingBean.afterPropertiesSet,以及在容器中自定义的initMethod
   ⑪ 执行组件后置处理器的初始化后方法,即BeanPostProcessor.postProcessAfterInitialization

⑫ 如果需要,注册组件的销毁方法,例如DisposableBean.destroy,以及在容器中自定义的destroyMethod 这里只是注册,并不调用

(6) 通过第11(5)步,单实例bean已经创建并初始化完成,接着,会调用AbstractBeanFactory的父类方法——
DefaultSingletonBeanRegistry.getSingleton方法,将新建的bean存入singletonObjects属性中,即缓存

(7) 回到DefaultListableBeanFactory.preInstantiateSingletons方法(见第11(1)步)

如果新建的bean实现了SmartInitializingSingleton接口,则执行afterSingletonsInstantiated回调方法

  1. finishRefresh 完成容器刷新

(1) 初始化生命周期处理器(LifecycleProcessor),先从BeanFactory中按类型获取,如果没有就新建一个DefaultLifecycleProcessor,并注册进BeanFactory

(2) 获取上一步注册的生命周期处理器,回调其onRefresh方法

(3) 发布容器刷新事件,即ContextRefreshedEvent

3.bean初始化

https://blog.csdn.net/u014082714/article/details/82388931

4.MVC的流程

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">
    <display-name>springtest</display-name>
 
    <context-param>
        <!-- 初始化器 -->
        <param-name>globalInitializerClasses</param-name>
        <param-value>com.wt.test.webmvc.config.MyContextInitializer</param-value>
    </context-param>
    <!-- 配置spring容器监听器 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext-*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>com.wt.test.webmvc.config.MyDataServletContextListener</listener-class>
    </listener>
 
    <!-- 前端控制器 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>com.wt.test.webmvc.config.MyDispatcherServlet</servlet-class>
        <!-- 加载springmvc配置 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- 配置文件的地址 如果不配置contextConfigLocation, 默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml"即:springmvc-serlvet.xml -->
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
        <init-param>
            <!-- 初始化器 -->
            <param-name>contextInitializerClasses</param-name>
            <param-value>com.wt.test.webmvc.config.MyContextInitializer</param-value>
        </init-param>
        <init-param>
            <param-name>contextId</param-name>
            <param-value>abcdefg</param-value>
        </init-param>
        <!-- 配置一个已经存在的容器,可以是上面的父容器,value值是容器在servletContext的Attribute中的key,
		我这里将contextLoaderListener初始化的父容器加到属性中,mvc初始化的时候会查找该值,存在容器就不会重新初始化mvc子容器,
        默认会存在父子容器,当然也可以不初始化父容器,只用servlet一个mvc子容器,
		测试的时候请注释掉,不然视图解析路径会有问题,因为配置的解析器是在springmvc.xml中,子容器会读取这个文件 -->
        <init-param>
            <param-name>contextAttribute</param-name>
            <param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!-- 可以配置/ ,此工程 所有请求全部由springmvc解析,此种方式可以实现 RESTful方式,需要特殊处理对静态文件的解析不能由springmvc解析
            可以配置*.do*.action,所有请求的url扩展名为.do.action由springmvc解析,此种方法常用 不可以/*,如果配置/*,返回jsp也由springmvc解析,这是不对的。 -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
 
    <!-- post乱码处理 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
 
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.css</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        ...
        静态文件的解析用默认的default,都跟上面一样配置,为了简短这个web,下边就不ctrl+v了
    </servlet-mapping>
    
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
</web-app>

springmvc启动过程中,我们需要配置的web如上图所示,这个基本上算是我们平时常用的比较完善的配置了。当然我们在做项目的时候完全可以省略掉ContextLoaderListener这个父容器的配置。因为我们的dispatcherServlet的init方法中是会初始化mvc容器的,也就是我们常说的子容器,contextLoaderListener加载的是父容器。

父容器的初始化
contextLoaderListener是实现了ServletContextListener的。我们直接来看contextInitialized(ServletContextEvent)方法。

//ContextLoaderListener.java,这货继承了ContextLoader
public void contextInitialized(ServletContextEvent event) {
		this.contextLoader = createContextLoader();
		if (this.contextLoader == null) {
			this.contextLoader = this;
		}
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}

//ContextLoader.java
private static final Properties defaultStrategies;
 
	static {
		...
            //这个文件与当前class同目录
            //它的值是org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
			ClassPathResource resource = new ClassPathResource("ContextLoader.properties", ContextLoader.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		...
	}
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    .....
        //创建父容器context,默认是XmlWebApplicationContext
        if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
    .....
       	//将初始化完的context放到当前servletContxt当中,
        //key是WebApplicationContext.class.getName() + ".ROOT"
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
    .....
}
 
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		Class<?> contextClass = determineContextClass(sc);
		.....
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
 
protected Class<?> determineContextClass(ServletContext servletContext) {
    	// 初始化的父容器的class名字,通过web.xml的servlet的<init-param>属性来配置contextClass的的值
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
    if (contextClassName != null) {
        ......
    } else {
        //默认值,在这个类的静态初始化块中
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
}

从上面可以看出我们其实默认初始化了一个XmlWebApplicationContext类型的父容器,并且将其放到了servletContext的attribute当中。

子容器的初始化
子容器的初始化是在DispatcherServlet中完成的,springmvc对所有请求的拦截也是基于servlet的,这个类就是spring队请求处理的核心,所有的请求都会到这里,然后由它去分发。既然这货是个servlet,那我们来看一看它的init方法,这个方法在他的父类HttpServletBean中。

// HttpServletBean.java
public final void init() throws ServletException {
	...
	//子类实现FrameworkServlet.java
	initServletBean();
	...
}

// FrameworkServlet.java
protected final void initServletBean() throws ServletException {
	...
    try {
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
	...
}
 
protected WebApplicationContext initWebApplicationContext() {
    //这个地方获取的就是我们之前初始化后放到servletContext的Attribute中的父容器XmlWebApplicationContext,有兴趣的可以点进去看看
	WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    ...
    if (wac == null) {
		// 这里就是去找一个已经存在的容器,servlet的<init-param>属性中配置contextAttribute值,
         // 因为我们之前启动了一个父容器,所以如果我们把父容器的key[WebApplicationContext.class.getName() + ".ROOT"]配在这个地方,我们就可以拿到父容器了。
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		// 正常情况下初始化子容器,传进去的rootContext就是之前初始化的父容器。
		wac = createWebApplicationContext(rootContext);
	} 
    if (!this.refreshEventReceived) {
		// 初始化requestMapping等,方法在DispacherServlet.java中
		onRefresh(wac);
	}
    if (this.publishContext) {
			// 生成子容器的名称,
			String attrName = getServletContextAttributeName();
        	// 将子容器也赋值给servletContext的Attribute
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}
    return wac;
}
 
protected WebApplicationContext findWebApplicationContext() {
		String attrName = getContextAttribute();
		...
		WebApplicationContext wac =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
		...
		return wac;
	}
 
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		// 子容器的类型,可以通过当前servlet的getContextClass方法获得,我的xml文件就是配置过的	
    	Class<?> contextClass = getContextClass();
		...
		wac.setEnvironment(getEnvironment());
    	// 可以看到这里我们设置了子容器的父容器为之前初始化的XmlWebApplicationContext
		wac.setParent(parent);
		wac.setConfigLocation(getContextConfigLocation());
		...
         // 配置和刷新容获取并调用调用web.xml配置的ContextInitializers
		configureAndRefreshWebApplicationContext(wac);
		return wac;
}
 
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    ...
    //由子类实现,默认为空实现
    postProcessWebApplicationContext(wac);
    //获取并调用web.xml配置的ContextInitializers
	applyInitializers(wac);
    ...
}
 
protected void applyInitializers(ConfigurableApplicationContext wac) {
    //获取由web.xml <context-param>配置的initializer,key为globalInitializerClasses
		String globalClassNames = getServletContext().getInitParameter("globalInitializerClasses");
		if (globalClassNames != null) {
			for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
				this.contextInitializers.add(loadInitializer(className, wac));
			}
		}
//获取由web.xml 的servlet配置的<init-param>配置的initializer,key为contextInitializerClasses
		if (this.contextInitializerClasses != null) {
			for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS)) {
				this.contextInitializers.add(loadInitializer(className, wac));
			}
		}
		//排序
		AnnotationAwareOrderComparator.sort(this.contextInitializers);
		for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
			initializer.initialize(wac);
		}
}
 
public String getServletContextAttributeName() {
    // 生成子容器名称
	return SERVLET_CONTEXT_PREFIX + getServletName();
}

onRefresh(wac)初始化web处理请求的组件

//DispathcerServlet.java
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}
 
protected void initStrategies(ApplicationContext context) {
    //初始化文件上传使用的MultiResolver
	initMultipartResolver(context);
    //初始化LocaleResolver,用于国际化
	initLocaleResolver(context);
    //初始化主题解析器ThemeResolver
	initThemeResolver(context);
    //初始化请求映射的HandlerMaping
	initHandlerMappings(context);
    //初始化请求处理的适配器HandlerAdapter
	initHandlerAdapters(context);
    //初始化异常处理解析器HandlerExceptionResolver
	initHandlerExceptionResolvers(context);
    //初始化用于从请求中获取viewname的RequtstToviewNameResolver
	initRequestToViewNameTranslator(context);
    //初始化视图解析器ViewResolver
	initViewResolvers(context);
    //初始化FlashMapperManager
	initFlashMapManager(context);
}

从上面可以看出我们其实创建了的子容器也同样放到了servletContext的attribute当中。
总的初始化流程可以总结如下:

  1. 通过ContextLoaderListener创建父容器;
  2. 将父容器放到servletContext的Attribute中;
  3. 通过servlet的init()方法开始创建子容器;
    3.1. 获取servletContext中的父容器;
    3.2. 通过配置的contextClass的值查找是否已经存在一个容器;
    3.3. 如果上一步没有找到,则开始重新创建一个子容器;
    3.4. 将父容器设置为子容器的parent;
    3.5. 刷新子容器
    3.5.1 调用子类扩展方法postProcessWebApplicationContext(wac);
    3.5.2 查找并调用web.xml配置的contextInitializers;
  4. 通过onRefresh(wac)方法初始化requestMapping等;
  5. 将初始化的子容器也丢给servletContext的Attribute;
  6. 执行子类扩展的initFrameworkServlet()方法,如果没有实现就是空的;
  7. springmvc初始化结束。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值