美文网首页Spring boot
spring启动之xml

spring启动之xml

作者: 三也视界 | 来源:发表于2020-11-23 13:08 被阅读0次

    本文主要是整体启动流程请查看springboot原理(核心原理、启动流程、执行流程)中的10. 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

    本系列源码都是基于spring-4.3.8版本之上,其他版本略有差异,但总体的核心思想相同。同时,为了使贴出的源代码尽可能的紧凑,可能会删去一些异常捕获、日志输出等代码。若文中存在纰漏错误,欢迎指正。

    image

    如上图ContextLoaderListener继承自ContextLoader又实现了ServletContextListener,

    前者用于初始化Spring容器,后者用于在web容器初始化时通知自己。

    由于ContextLoaderListener实现了ServletContextListener,当web容器初始化时,会调用ServletContextListener#contextInitialized,开始初始化servlet上下文监听器,ContextLoaderListener就是其中一个。

           public void contextInitialized(ServletContextEvent event) {
            initWebApplicationContext(event.getServletContext());
        }
    

    而ContextLoaderListener通过调用父类ContextLoader#initWebApplicationContext方法展开了Spring初始化之路。

      public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
            // 当前servletContext若已存在上下文,则说明已开始启动,则抛出异常,终止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!");
            }
     
            // 判断当前上下文是否存在,不存在,则进行创建
            if (this.context == null) {
                this.context = createWebApplicationContext(servletContext);
            }
            // 若没有进行过特殊配置,那么该context的类型默认为XmlWebApplicationContext,具体可见createWebApplicationContext
            // 且context的类型必然是ConfigurableWebApplicationContext的子类
            if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        ApplicationContext parent = loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }
                    // 该方法为spring容器真正的初始化入口
                    configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }
            // 将上下文设置到servletContext中,用于给其他容器获取,如SpringMvc的初始化Servlet就是通过该方法获取到父容器的
            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);
            }
     
            return this.context;
        }
    
        protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
            // 创建context的第一步,即需要确定该context的类型
            Class<?> contextClass = determineContextClass(sc);
            // 不论其最终是何类型,其必须实现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);
        }
    
        protected Class<?> determineContextClass(ServletContext servletContext) {
            // 默认从servletContext中获取初始化参数,即我们web.xml中可配置的contextClass中获取类的全限定名
            String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
            if (contextClassName != null) {
                try {
                    return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
                }
                catch (ClassNotFoundException ex) {
                    throw new ApplicationContextException(
                            "Failed to load custom context class [" + contextClassName + "]", ex);
                }
            }
            else {
                // 若没有获取到,则会以默认的策略,从本类的同级目录下以WebApplicationContext全限定名为key读取ContextLoader.properties的配置
                // 最终获取到的即org.springframework.web.context.support.XmlWebApplicationContext
                contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
                try {
                    return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
                }
                catch (ClassNotFoundException ex) {
                    throw new ApplicationContextException(
                            "Failed to load default context class [" + contextClassName + "]", ex);
                }
            }
        }
    
         protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
            if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
                // 从web.xml中获取初始化参数contextId,作为当前容器的唯一ID
                String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
                if (idParam != null) {
                    wac.setId(idParam);
                }
                else {
                    wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                            ObjectUtils.getDisplayString(sc.getContextPath()));
                }
            }
     
            // 将当前ServletContext设置到当前容器中
            wac.setServletContext(sc);
            // 从web.xml中读取初始化参数contextConfigLocation,读取出的配置文件路径稍后将逐个加载
            String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
            if (configLocationParam != null) {
                // 将配置文件路径设置到本容器中
                wac.setConfigLocation(configLocationParam);
            }
     
            ConfigurableEnvironment env = wac.getEnvironment();
            if (env instanceof ConfigurableWebEnvironment) {
                ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
            }
     
            // 个性化启动上下文
            customizeContext(sc, wac);
            /**
             * 容器加载的核心方法,
             * 不论是以注解方式启动的AnnotationConfigWebApplicationContext
             * 还是spring-boot的启动类SpringApplication
             * 最终都需要调用该方法,即AbstractApplicationContext#refresh
             */
            wac.refresh();
        }
    
    
        protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
            /**
             * 决定上下文需要初始化的类
             * 从<init-param>中获取"globalInitializerClasses"、"contextInitializerClasses"
             */
            List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
                    determineContextInitializerClasses(sc);
     
            for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
                Class<?> initializerContextClass =
                        GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
                if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
                    throw new ApplicationContextException(String.format(
                            "Could not apply context initializer [%s] since its generic parameter [%s] " +
                            "is not assignable from the type of application context used by this " +
                            "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
                            wac.getClass().getName()));
                }
                this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
            }
     
            // 排序并逐个初始化ApplicationContextInitializer
            AnnotationAwareOrderComparator.sort(this.contextInitializers);
            for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
                initializer.initialize(wac);
            }
        }
    

    由于AbstractApplicationContext#refresh方法太长,限于本篇篇幅,该方法的细节将在之后进行源码分析。

    相关文章

      网友评论

        本文标题:spring启动之xml

        本文链接:https://www.haomeiwen.com/subject/ggxzbktx.html