zoukankan      html  css  js  c++  java
  • 对spring web启动时IOC源码研究

    1. 研究IOC首先创建一个简单的web项目,在web.xml中我们都会加上这么一句
    <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
    
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>

    这代表了web容器启动的时候会首先进入ContextLoaderListener这个类,并且之后会去加载classpath下的applicationContext.xml文件。那么重点就在ContextLoaderListener上,点开源码:

    /**
         * Initialize the root web application context.
         */
        @Override
        public void contextInitialized(ServletContextEvent event) {
            initWebApplicationContext(event.getServletContext());
        }
    
    
        /**
         * Close the root web application context.
         */
        @Override
        public void contextDestroyed(ServletContextEvent event) {
            closeWebApplicationContext(event.getServletContext());
            ContextCleanupListener.cleanupAttributes(event.getServletContext());
        }

    里面主要为ServletContextListener接口的两个实现方法。web容器会首先调用contextInitialized方法,传入tomcat封装的容器资源,之后调用父类的初始化容器方法。

    /**
    
    * The root WebApplicationContext instance that this loader manages.
    
    */
    
    private WebApplicationContext context;
    
    
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
          ...........省略// Store context in local instance variable, to guarantee that
                // it is available on ServletContext shutdown.
                if (this.context == null) {
                    this.context = createWebApplicationContext(servletContext);
                }
                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);
                            cwac.setParent(parent);
                        }
                        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);
                }
      ......省略
                return this.context;
            
        }

    这个方法里主要步骤createWebApplicationContext方法用来创建XmlWebApplicationContext这个root根容器,这个容器就是取自servletContextEvent。

    loadParentContext方法用来加载父容器。主要方法configureAndRefreshWebApplicationContext用来配置和刷新root容器,在方法内最主要的就是refresh方法,里面实现了最主要的功能。

    @Override
        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();
    
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    registerBeanPostProcessors(beanFactory);
    
                    // Initialize message source for this context.
                    initMessageSource();
    
                    // Initialize event multicaster for this context.
                    initApplicationEventMulticaster();
    
                    // Initialize other special beans in specific context subclasses.
                    onRefresh();
    
                    // Check for listener beans and register them.
                    registerListeners();
    
                    // Instantiate all remaining (non-lazy-init) singletons.
                    finishBeanFactoryInitialization(beanFactory);
    
                    // Last step: publish corresponding event.
                    finishRefresh();
                }
    
                catch (BeansException ex) {
                    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;
                }
            }
        }

    prepareRefresh方法用来准备之后需要用到的环境。

    obtainFreshBeanFactory方法获取beanFactory

    @Override
        protected final void refreshBeanFactory() throws BeansException {
            if (hasBeanFactory()) {
                destroyBeans();
                closeBeanFactory();
            }
            try {
                DefaultListableBeanFactory beanFactory = createBeanFactory();
                beanFactory.setSerializationId(getId());
                customizeBeanFactory(beanFactory);
                loadBeanDefinitions(beanFactory);
                synchronized (this.beanFactoryMonitor) {
                    this.beanFactory = beanFactory;
                }
            }
            catch (IOException ex) {
                throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
            }
        }

    实际返回的beanFactory为其实现类DefaultListableBeanFactory,实例化该类用来为之后装载xml中实例化的类。

    loadBeanDefinitions为重要的方法,用来真正的加载类了,之前的都是准备工作。

    @Override
        protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
            // Create a new XmlBeanDefinitionReader for the given BeanFactory.
            XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
            // Configure the bean definition reader with this context's
            // resource loading environment.
            beanDefinitionReader.setEnvironment(getEnvironment());
            beanDefinitionReader.setResourceLoader(this);
            beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
            // Allow a subclass to provide custom initialization of the reader,
            // then proceed with actually loading the bean definitions.
            initBeanDefinitionReader(beanDefinitionReader);
            loadBeanDefinitions(beanDefinitionReader);
        }

    在XmlWebApplicationContext中覆写此方法,内部创建了XmlBeanDefinitionReader类,用来作为xml中bean的读操作器,初始化环境后加载此类

        protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
            String[] configLocations = getConfigLocations();
            if (configLocations != null) {
                for (String configLocation : configLocations) {
                    reader.loadBeanDefinitions(configLocation);
                }
            }
        }

    之后获得之前初始化时放入数组的配置文件信息(比如:classpath:applicationContext.xml),用XmlBeanDefinitionReader加载此配置文件。

    之后对xml文件信息进行编解码操作:

        /**
         * Load bean definitions from the specified XML file.
         * @param resource the resource descriptor for the XML file
         * @return the number of bean definitions found
         * @throws BeanDefinitionStoreException in case of loading or parsing errors
         */
        @Override
        public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
            return loadBeanDefinitions(new EncodedResource(resource));
        }

    关键步骤:

    InputStream inputStream = encodedResource.getResource().getInputStream();
                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
                    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                }
                finally {
                    inputStream.close();
                }

    之后正式加载获取到的xml资源解析

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                throws BeanDefinitionStoreException {
                Document doc = doLoadDocument(inputSource, resource);
                return registerBeanDefinitions(doc, resource);
        }

    主要步骤是用sax对xml文件解析成Document元素,再注册进beanDefinitions容器中。

    doLoadDocument方法的处理方法和sax对xml文档的处理方式就差不多了,具体可以参考sax解析xml流程

    解析的过程很多,比如

        private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
            if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {//解析import
                importBeanDefinitionResource(ele);
            }
            else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {//解析alias
                processAliasRegistration(ele);
            }
            else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {//解析bean
                processBeanDefinition(ele, delegate);
            }
            else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {//解析beans
                // recurse
                doRegisterBeanDefinitions(ele);
            }
        }

    将各个标签解析后注入容器。

  • 相关阅读:
    物理学——总结
    创建场景和赛道——挑战:为赛道建立一个新的单元测试
    物理学——牛顿运动定律
    物理学——挑战:实现道路碰撞检测
    1291. Gearwheels 夜
    hdu 4442 Physical Examination 夜
    hdu 4450 Draw Something 夜
    1129. Door Painting 夜
    hdu 4431 Mahjong 夜
    1128. Partition into Groups 夜
  • 原文地址:https://www.cnblogs.com/sky-chen/p/6536588.html
Copyright © 2011-2022 走看看