Spring IoC容器构建过程分析(一)


注:本文分析的内容,针对的是Spring 2.5.6的版本
ApplicationContext是spring IoC容器的顶级接口,其类结构图如下:

    从上面的类图中可以看出, ApplicationContext继承了ResourceLoader接口,便于获取外部资源;也间接继承了 BeanFactory接口,这样可以在Spring容器中创建Bean对象;同时也继承了ApplicationEventPublisher接口,用于发送一些事件消息。
    通常我们使用这样的一行代码来创建并启动Spring容器:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
 虽然是简单的一行代码,但背后却做了很多事情,先看一下ClassPathXmlApplicationContext的构造方法:
	public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}
	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
在该构造方法中,最后调用了AbstractApplicationContext.refresh()方法,该方法的实现代码如下:
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) {
				// Destroy already created singletons to avoid dangling resources.
				beanFactory.destroySingletons();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}
		}
	}
这个方法非常关键,通过调用12个其他的方法,完成了整个IoC容器的构建。详细了解这个方法里面的每一行代码,基本上就掌握了Spring大部分的原理、功能和扩展点。下面分析每一个方法的所做的事情。

1、prepareRefresh()
该方法所做的事情相对比较简单:记录容器启动的时间,并设置容器处于活跃状态。

2、obtainFreshBeanFactory()
该方法的作用:创建BeanFactory实例,并解析Spring的xml配置文件。beanFactory的实现类是:ConfigurableListableBeanFactory。方法的实现如下:
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();

		if (logger.isInfoEnabled()) {
			logger.info("Bean factory for application context [" + getId() + "]: " +
					ObjectUtils.identityToString(beanFactory));
		}
		if (logger.isDebugEnabled()) {
			logger.debug(beanFactory.getBeanDefinitionCount() + " beans defined in " + this);
		}

		return beanFactory;
	}
该方法主要通过调用AbstractRefreshableApplicationContext.refreshBeanFactory()方法完成相关功能:
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory(); //1、创建beanFactory对象
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);//2、解析spring的xml配置文件,加载bean定义信息
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException(
					"I/O error parsing XML document for application context [" + getDisplayName() + "]", ex);
		}
	}
首先,通过createBeanFactory, 创建了DefaultListableBeanFactory类型的beanFactory实例。分析一下BeanFactory的类图结构,如下:

创建了beanFactory之后,通过调用AbstractXmlApplicationContext.loadBeanDefinitions方法,加载spring的xml配置文件,把用户通过配置文件定义的bean,解析成容器中以特定的数据结构描述的bean定义。AbstractXmlApplicationContext.loadBeanDefinitions方法的实现如下:
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //1、创建BeanDefinitionReader对象

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 2、设置xml文件schema的解析器,获取xsd文件。

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader); //3、加载spring的xml配置文件,并解析。
	}
第一步,创建bean的解析类:XmlBeanDefinitionReader,将bean的解析工作,托管给BeanDefinitionReader处理。BeanDefinitionReader的类结构图如下:

第二步, 创建ResourceEntityResolver,设置用于XML配置文件验证的实体分解器。该类的resolveEntity方法,实现对文档验证实体的转换:根据spring定义bean的xml配置文件中的“http://www.springframework.org/schema/beans”形式的url,从spring.jar包中的META-INF\spring.schema文件中,找到对应的DTD或XSD文件在本地的路径。
第三步,调用initBeanDefinitionReader方法,该方法是一个空实现,允许子类去覆盖,可以用来设置自定义的EntityResolver实现类,以及NamespaceHandlerResolver实现类。这也是spring的一个扩展点。
第四步,调用loadBeanDefinitions方法,该方法最终调用XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource)方法实现xml配置文件的加载及bean的注册,方法的实现如下:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}

		Set currentResources = (Set) this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected recursive loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			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();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.set(null);
			}
		}
	}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            int validationMode = getValidationModeForResource(resource);
            Document doc = this.documentLoader.loadDocument(
                    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
            return registerBeanDefinitions(doc, resource);
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }
在doLoadBeanDefinitions方法中,调用DefaultDocumentLoader.loadDocument方法,使用JAXP加载xml文档;然后调用registerBeanDefinitions方法,通过DefaultBeanDefinitionDocumentReader.registerBeanDefinitions,注册定义的bean。registerBeanDefinitions方法实现如下:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;

		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();

		BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);

		preProcessXml(root);// 前置处理方法,是一个空实现
		parseBeanDefinitions(root, delegate);<span><span class="comment">// 解析整个文档,循环处理各个子节点</span></span>
		postProcessXml(root);// 后置处理方法,也是一个空实现
	}
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root.getNamespaceURI())) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    String namespaceUri = ele.getNamespaceURI();
                    if (delegate.isDefaultNamespace(namespaceUri)) {//<span><span class="comment">如果是默认名字空间(beans),则直接使用解析</span><span></span></span>
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);//对于非默认名字空间,如util,使用对应的NamespaceHandlerResolver实现类的parse方法进行解析。
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }
在bean的注册过程中,spring 用到了 NamespaceHandlerResolver接口它是XML配置文件中的各种名字空间(如: context)定义的节点(如 : context:property-placeholder)对应解析器的分解器。通过 Namespace的SystemId(例如:http://www.springframework.org/schema/util),根据spring.jar的META-INF/spring.handlers映射文件,找到对应的解析器的类路径,然后使用该解析类,解析对应的节点。而对于无前缀的beans默认名字空间节点:采用BeanDefinitionParserDelegate完成节点的解析。
    至此,完成了BeanFactory实例的创建和Spring的xml配置文件的解析。
3、prepareBeanFactory
创建好 BeanFactory 后,接着调用prepareBeanFactory方法,添加一些 Spring 本身需要的一些工具类:
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		// Tell the internal bean factory to use the context's class loader.
		beanFactory.setBeanClassLoader(getClassLoader());

		// Populate the bean factory with context-specific resource editors.
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this));

		// Configure the bean factory with context callbacks.
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

		// BeanFactory interface not registered as resolvable type in a plain factory.
		// MessageSource registered (and found for autowiring) as a bean.
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);

		// Detect a LoadTimeWeaver and prepare for weaving, if found.
		if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME) && JdkVersion.isAtLeastJava15()) {
			// Register the (JDK 1.5 specific) LoadTimeWeaverAwareProcessor.
			try {
				Class ltwapClass = ClassUtils.forName(
						"org.springframework.context.weaving.LoadTimeWeaverAwareProcessor",
						AbstractApplicationContext.class.getClassLoader());
				BeanPostProcessor ltwap = (BeanPostProcessor) BeanUtils.instantiateClass(ltwapClass);
				((BeanFactoryAware) ltwap).setBeanFactory(beanFactory);
				beanFactory.addBeanPostProcessor(ltwap);
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException("Spring's LoadTimeWeaverAwareProcessor class is not available");
			}
			// Set a temporary ClassLoader for type matching.
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
	}
该方法主要分成四步,如下:
第一步
,设置类加载器;
第二步,设置属性编辑器注册类,用来注册相关的属性编辑器。ResourceEditorRegistrar类,注册的属性编辑器如下:
public void registerCustomEditors(PropertyEditorRegistry registry) {
		ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader);
		registry.registerCustomEditor(Resource.class, baseEditor);
		registry.registerCustomEditor(InputStream.class, new InputStreamEditor(baseEditor));
		registry.registerCustomEditor(File.class, new FileEditor(baseEditor));
		registry.registerCustomEditor(URL.class, new URLEditor(baseEditor));

		ClassLoader classLoader = this.resourceLoader.getClassLoader();
		registry.registerCustomEditor(Class.class, new ClassEditor(classLoader));
		registry.registerCustomEditor(URI.class, new URIEditor(classLoader));

		if (this.resourceLoader instanceof ResourcePatternResolver) {
			registry.registerCustomEditor(Resource[].class,
					new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader));
		}
	}
例如InputStreamEditor这个属性编辑器,可以将spring配置文件中,用字符串表示的文件路径,转成Resource资源类,注入到bean的对应属性中。同样,我们可以通过自定义的属性编辑器,将spring配置文件中以某种格式定义的字符串,转成对应的Java对象。spring的属性编辑器类图结构如下:

第三步:设置内置的BeanPostProcessor:ApplicationContextAwareProcessor。该BeanPostProcessor的作用是,为实现特殊接口的bean,注入容器类(例如为实现ApplicationContextAware接口的类,注入ApplicationContext对象实例),如下:
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (bean instanceof ResourceLoaderAware) {
			((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
		}
		if (bean instanceof ApplicationEventPublisherAware) {
			((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
		}
		if (bean instanceof MessageSourceAware) {
			((MessageSourceAware) bean).setMessageSource(this.applicationContext);
		}
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
		}
		return bean;
	}
第四步:调用ignoreDependencyInterface,设置忽略自动注入的接口(因为这些接口已经通过ApplicationContextAwareProcessor注入了)。
第五步:调用registerResolvableDependency,注入特殊的对象。

4、postProcessBeanFactory
该方法是spring的一个扩展点之一,是一个空方法,留给子类去扩展。子类可以重写该方法,对已经构建的 BeanFactory 的配置根据需要进行修改。例如调用beanFactory.registerResolvableDependency,注入特殊的类。

猜你喜欢

转载自blog.csdn.net/caihaijiang/article/details/35795781