zoukankan      html  css  js  c++  java
  • spring容器启动的加载过程(一)

    使用spring,我们在web.xml都会配置ContextLoaderListener

      <listener>
        <listener-class>
                org.springframework.web.context.ContextLoaderListener
            </listener-class>
      </listener>
    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

    //由于他继承自ContextLoader,加载的时候当然会先加载他啦,那我们来看看这个类



    }
    ContextLoader这个类有一段静态代码块
    private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";//这个文件是在org.springframework.web.context.ContextLoader.properties他里面是

    org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext //这个后面再研究 以后就会把spring所有重要的东西都能看到了。
    private static final Properties defaultStrategies; static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. try { ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);//根据上面的properties文件,就能得到一个默认的properties了。 } catch (IOException ex) { throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage()); } }

    第一步:我们在看看ContextLoaderListener的这个方法,在容器启动的时候,他会扫描web.xml的listener配置,然后自动调用Listener的contextInitialized初始化方法。

    public void contextInitialized(ServletContextEvent event) {
            this.contextLoader = createContextLoader();
            if (this.contextLoader == null) {
                this.contextLoader = this;
            }
            this.contextLoader.initWebApplicationContext(event.getServletContext());
        }

     第二步:initWebApplicationContext初始化web应用上下文

        public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
            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!");
            }
    
            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 {
                // Store context in local instance variable, to guarantee that
                // it is available on ServletContext shutdown.
                if (this.context == null) {
                    this.context = createWebApplicationContext(servletContext);//判断是否有WebApplicationContext,没有则创建,创建得到的是ConfigurableWebApplicationContext
                }
                if (this.context instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                    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 ->
                            // determine parent for root web application context, if any.
                            ApplicationContext parent = loadParentContext(servletContext);//决定根Web应用程序环境是否存在父应用程序环境,一般是返回null
                            cwac.setParent(parent);
                        }
                        configureAndRefreshWebApplicationContext(cwac, servletContext);//在这里是初始化web应用上下文的入口 ,spring的依赖注入也从这里开始了。
                    }
                }
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);//把创建的根Web应用程序环境保存到Servlet环境中,每个派遣器Servlet加载的子环境会应用这个环境作为父环境    
    
                ClassLoader ccl = Thread.currentThread().getContextClassLoader();//取得线程的类加载器
                if (ccl == ContextLoader.class.getClassLoader()) {
                    currentContext = this.context;//如果线程和本类拥有相同的类加载器,则使用静态变量保存即可,因为同一类加载器加载同一份静态变量    
                }
                else if (ccl != null) {
                    currentContextPerThread.put(ccl, this.context);//如果线程和本类拥有不同的类加载器,则使用线程的类加载器作为键值保存在一个映射对象里,保证析构时能拿到Web应用程序环境进行关闭操作
                }
    
                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 ex) {
                logger.error("Context initialization failed", ex);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);//如果产生任何异常,则保存异常对象到Servlet环境里 
                throw ex;
            }
            catch (Error err) {
                logger.error("Context initialization failed", err);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);//如果产生任何错误,则保存错误对象到Servlet环境里
                throw err;
            }
        }

    第三步:得到具体的应用上下文

    public static final String CONTEXT_CLASS_PARAM = "contextClass";
    
    
    private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
    
    
    private static final Properties defaultStrategies;
    
    //这个静态代码块,会去加载相同目录下的ContextLoader.properties,这个目录是org.springframework.web.context,这个配置文件的配置信息就只有org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
        static {
            // Load default strategy implementations from properties file.
            // This is currently strictly internal and not meant to be customized
            // by application developers.
            try {
                ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
                defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
            }
            catch (IOException ex) {
                throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
            }
    }
    
    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
            Class<?> contextClass = determineContextClass(sc);//决定要得到哪个ContextClass
            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);   //实例化Web应用程序环境类 
    }
        
    protected Class<?> determineContextClass(ServletContext servletContext) {
            String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);//获取web.xml配置的contextClass
            if (contextClassName != null) {//配置了contextClass,返回具体的Class
                try {
                    return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
                }
                catch (ClassNotFoundException ex) {
                    throw new ApplicationContextException(
                            "Failed to load custom context class [" + contextClassName + "]", ex);
                }
            }
            else {
                contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());//在上面的静态代码块中已经加载了XmlWebApplicationContext,返回的是XmlWebApplicationContext的全路径名:org.springframework.web.context.support.XmlWebApplicationContext
                try {
                    return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());//没有配置contextClass,则通过配置文件,返回了XmlWebApplicationContext的class
                }
                catch (ClassNotFoundException ex) {
                    throw new ApplicationContextException(
                            "Failed to load default context class [" + contextClassName + "]", ex);
                }
            }
    }

    第四步:用于封装ApplicationContext数据并且初始化所有相关Bean对象。它会从web.xml中读取名为contextConfigLocation的配置,这就是spring xml数据源设置,然后放到ApplicationContext中,最后调用传说中的refresh方法执行所有Java对象的创建。

        public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";  在wen.xml中配置的位置
        
        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
                // -> assign a more useful id based on available information
                String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
                //设置Web应用程序环境的ID  
                if (idParam != null) {
                    wac.setId(idParam);
                }
                else {
                    // Generate default id...
                     //如果 Servlet规范 <= 2.4,则使用web.xml里定义的应用程序名字定义Web应用程序名
                    if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
                        // Servlet <= 2.4: resort to name specified in web.xml, if any.
                        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                                ObjectUtils.getDisplayString(sc.getServletContextName()));
                    }
                    else {
                    // 如果Servlet规范是 2.5, 则使用配置的ContextPath定义Web应用程序名 
                        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                                ObjectUtils.getDisplayString(sc.getContextPath()));
                    }
                }
            }
            //保存Servlet环境    
            wac.setServletContext(sc);
            String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);//这里得到"contextConfigLocation"配置的值:classpath*:spring-config.xml,
            if (initParameter != null) {
                wac.setConfigLocation(initParameter);//设置保存ConfigLocation,待后面加载的时候,扫描里面的配置
            }
            customizeContext(sc, wac);//提供子类可互换Web应用程序环境的机会 占位符方法  
            wac.refresh();//刷新Web应用程序环境以加载Bean定义,这里才是把我们XML里定义的bean放入容器的时候  
        }
    "contextConfigLocation"在web.xml的配置
    <context-param>
         <param-name>contextConfigLocation</param-name>
        <param-value>
                    classpath*:spring-config.xml,
        </param-value>
    </context-param>

    第五步:bean的注入

        public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                prepareRefresh();
    
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//告诉子类刷新内部bean工厂。
    
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);//准备bean工厂给上下文使用
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);//允许在上下文后处理bean工厂的子类
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);//调用处理器工厂注册的bean上下文
    
                    // Register bean processors that intercept bean creation.
                    registerBeanPostProcessors(beanFactory);注册bean拦截器拦截bean的创建
    
                    // Initialize message source for this context.
                    initMessageSource();//给上下文初始化消息来源
    
                    // Initialize event multicaster for this context.
                    initApplicationEventMulticaster();//给上下文初始化事件多播
    
                    // Initialize other special beans in specific context subclasses.//在特定上下文初始化其他特殊bean子类
                    onRefresh();
    
                    // Check for listener beans and register them.
                    registerListeners();//检查监听器bean并注册他们
    
                    // Instantiate all remaining (non-lazy-init) singletons.
                    finishBeanFactoryInitialization(beanFactory);//实例化所有剩余(non-lazy-init)单例对象
    
                    // Last step: publish corresponding event.
                    finishRefresh();//发布相应的事件
                }
    
                catch (BeansException ex) {
                    // Destroy already created singletons to avoid dangling resources.
                    destroyBeans();//销毁已经创建的单例,避免悬挂的资源
    
                    // Reset 'active' flag.
                    cancelRefresh(ex);//重置“活跃”标志
    
                    // Propagate exception to caller.
                    throw ex;
                }
            }
        }
  • 相关阅读:
    c 头文件<ctype.h>(一)
    联想M490 开机U盘启动 快捷键
    多文件 定义全局变量的使用 extern
    Unity3D学习笔记(十一):布料和协程
    Unity3D学习笔记(十):Physics类和射线
    Unity3D学习笔记(九):摄像机
    Unity3D学习笔记(八):四元素和书籍推荐
    Unity3D学习笔记(七):叉乘和四元素
    Unity3D学习笔记(六):三角函数和点乘
    Unity3D学习笔记(五):坐标系、向量、3D数学
  • 原文地址:https://www.cnblogs.com/hjy9420/p/4215708.html
Copyright © 2011-2022 走看看