参考网站:https://www.cnblogs.com/RunForLove/p/5688731.html

通过对spring源码的解读,跟踪spring容器的启动过程,掌握SpringMVC是如何工作的;掌握Spring源码的设计和增强阅读源码的技巧。为可持续性的使用spring框架高效集成开发,解决系统一些疑难杂症提供强有力的支撑。

一、需要掌握的理论业务点:

1、Web容器初始化过程

2、Spring MVC 在web.xml中的配置

3、理解ServletContextListener

4、掌握理解ContextLoadListener

5、DispatcherServlet初始化(HttpServletBean • FrameworkServlet • DispatcherServlet)

6、ContextLoadListener与DispatcherServlet的关系

7、DispatcherServlet的设计

8、DispatcherServlet工作原理

二、分析各个理论节点的知识

1、要想了解spring容器在web容器中启动的过程,首先我们需要知道web容器在tomcat中的生命周期,方可理解spring容器是如何在web容器中启动、运行、以及销毁、的生命周期

上图展示了web容器在启动过程中主要业务走向,其官方给出的解释是:

When a web application is deployed into a container, the following steps must be performed, in this order, before the web application begins processing client requests.
1、Instantiate an instance of each event listener identified by a <listener> element in the deployment descriptor.
2、For instantiated listener instances that implement ServletContextListener, call the contextInitialized() method.
3、Instantiate an instance of each filter identified by a <filter> element in the deployment descriptor and call each filter instance's init() method.
4、Instantiate an instance of each servlet identified by a <servlet> element that includes a <load-on-startup> element in the order defined by the load-on-startup element values, and call each servlet instance's init() method.

翻译是:

web应用程序部署到容器中时,必须按照以下顺序在web应用程序开始处理客户机请求之前执行以下步骤。

实例化由部署描述符中的元素标识的每个事件侦听器的实例。

对于实现ServletContextListener的实例化侦听器实例,调用contextInitialized()方法。

实例化由部署描述符中的元素标识的每个过滤器的实例,并调用每个过滤器实例的init()方法。

实例化由元素标识的每个servlet的实例,该元素按加载启动元素值定义的顺序包含元素,并调用每个servlet实例的init()方法。

大致的意思是,tomcat在启动web容器的时候会启动一个叫ServletContextListener的监听器,每当在web容器中有ServletContextListener这个接口被实例化的时候,web容器会通知ServletContextListener被实例的对象去执行其contextInitialized()的方法进行相应的业务处理,而spring框架在设计的过程中ContextLoadListener这个类实现了ServletContextListener这个接口,因此每当有ContextLoadListener这个类被实例化的时候,web容器会通知他执行contextInitialized()这个方法

2、spring MVC在web.xml在的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<display-name>app</display-name>

	<!-- 加载Spring配置文件 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-starter.xml</param-value>
	</context-param>

	<!-- Spring监听器 -->
	<listener>
		<description>Spring监听器</description>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

 在web.xm配置中由<context-param>将spring的配置文件引入加载到web容器中,

<listener>配置项是在web容器中启用监听,其所配置的class是需要启用的监听器,因此spring容器的启用是同过org.springframework.web.context.ContextLoaderListener进行spring容器的启动,而ContextLoaderListener监听实现的是ServlectContextListener接口,因此在其实例化的过程中,contextInitialized()会被调用,从而进行spring容器的启动与创建的过程中

ContextLoaderListener中的contextInitialized()进行了spring容器的启动配置并且刷新实例化整个SpringApplicationContext中的Bean。因此,如果我们的Bean配置出错的话,在容器启动的时候,会抛异常出来的。

@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    //Spring 启动的句柄,spring容器开始启动的根目录
    if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        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!");
    } else {
        Log logger = LogFactory.getLog(ContextLoader.class);
        servletContext.log("Initializing Spring root WebApplicationContext");
        if(logger.isInfoEnabled()) {
            logger.info("Root WebApplicationContext: initialization started");
        }

        long startTime = System.currentTimeMillis();

        try {
            //处理spring容器是否已经创建(只创建没有创建spring的各个bean)
            if(this.context == null) {
                this.context = this.createWebApplicationContext(servletContext);
            }

            if(this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
                if(!cwac.isActive()) {
                    if(cwac.getParent() == null) {
                        ApplicationContext parent = this.loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }

                    //Spring容器创建完成后,加载spring容器的各个组件
                    this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }

            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.isDebugEnabled()) {
                logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
            }

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

            return this.context;
        } catch (RuntimeException var8) {
            logger.error("Context initialization failed", var8);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
            throw var8;
        } catch (Error var9) {
            logger.error("Context initialization failed", var9);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
            throw var9;
        }
    }
}

以上完成Spring容器的创建 ,并在代码中配置加载Spring容器启动的配置文件,以及调用Spring容器的加载入口

this.configureAndRefreshWebApplicationContext(cwac, servletContext);

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    String configLocationParam;
    if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
        configLocationParam = sc.getInitParameter("contextId");
        if(configLocationParam != null) {
            wac.setId(configLocationParam);
        } else {
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }

    wac.setServletContext(sc);
    //设置Spring容器启动的起始配置文件
    configLocationParam = sc.getInitParameter("contextConfigLocation");
    if(configLocationParam != null) {
        wac.setConfigLocation(configLocationParam);
    }

    ConfigurableEnvironment env = wac.getEnvironment();
    if(env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
    }

    this.customizeContext(sc, wac);
    //初始化spring的各个组件,以及spring容器中的各个bean
    wac.refresh();
}
Spring容器加载bean的过程入口:wac.refresh();
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    String configLocationParam;
    if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
        configLocationParam = sc.getInitParameter("contextId");
        if(configLocationParam != null) {
            wac.setId(configLocationParam);
        } else {
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }

    wac.setServletContext(sc);
    //设置Spring容器启动的起始配置文件
    configLocationParam = sc.getInitParameter("contextConfigLocation");
    if(configLocationParam != null) {
        wac.setConfigLocation(configLocationParam);
    }

    ConfigurableEnvironment env = wac.getEnvironment();
    if(env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
    }

    this.customizeContext(sc, wac);
    //初始化spring的各个组件,以及spring容器中的各个bean
    wac.refresh();
}

加spring容器中bean

public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
    beanFactory.addBeanPostProcessor(new PostProcessorRegistrationDelegate.BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList();
    List<BeanPostProcessor> internalPostProcessors = new ArrayList();
    List<String> orderedPostProcessorNames = new ArrayList();
    List<String> nonOrderedPostProcessorNames = new ArrayList();
    String[] var8 = postProcessorNames;
    int var9 = postProcessorNames.length;

    String ppName;
    BeanPostProcessor pp;
    for(int var10 = 0; var10 < var9; ++var10) {
        ppName = var8[var10];
        if(beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
            priorityOrderedPostProcessors.add(pp);
            if(pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        } else if(beanFactory.isTypeMatch(ppName, Ordered.class)) {
            orderedPostProcessorNames.add(ppName);
        } else {
            nonOrderedPostProcessorNames.add(ppName);
        }
    }

    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, (List)priorityOrderedPostProcessors);
    List<BeanPostProcessor> orderedPostProcessors = new ArrayList();
    Iterator var14 = orderedPostProcessorNames.iterator();

    while(var14.hasNext()) {
        String ppName = (String)var14.next();
        BeanPostProcessor pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
        orderedPostProcessors.add(pp);
        if(pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }

    sortPostProcessors(orderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, (List)orderedPostProcessors);
    //存放spring容器中加载的各个bean
    List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList();
    Iterator var17 = nonOrderedPostProcessorNames.iterator();

    while(var17.hasNext()) {
        ppName = (String)var17.next();
        pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
        nonOrderedPostProcessors.add(pp);
        if(pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }

    registerBeanPostProcessors(beanFactory, (List)nonOrderedPostProcessors);
    sortPostProcessors(internalPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, (List)internalPostProcessors);
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

通过SerlvetContextListener这一特性还可以做其它的运用,如缓存数据在服务启动过程中的初始化

https://blog.csdn.net/qq_31854907/article/details/86304955

 

 

 

 

 

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐