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

http://blog.csdn.net/caihaijiang/article/details/35795781

ApplicationContext是spring IoC容器的顶级接口,其类结构图如下: 

Spring IoC容器构建过程分析(一)_第1张图片
    从上面的类图中可以看出, ApplicationContext继承了ResourceLoader接口,便于获取外部资源;也间接继承了 BeanFactory接口,这样可以在Spring容器中创建Bean对象;同时也继承了ApplicationEventPublisher接口,用于发送一些事件消息。
    通常我们使用这样的一行代码来创建并启动Spring容器:
[java]  view plain
  1. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  
 虽然是简单的一行代码,但背后却做了很多事情,先看一下ClassPathXmlApplicationContext的构造方法:
[java]  view plain
  1. public ClassPathXmlApplicationContext(String configLocation) throws BeansException {  
  2.     this(new String[] {configLocation}, truenull);  
  3. }  
  4. public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)  
  5.         throws BeansException {  
  6.   
  7.     super(parent);  
  8.     setConfigLocations(configLocations);  
  9.     if (refresh) {  
  10.         refresh();  
  11.     }  
  12. }  
在该构造方法中,最后调用了AbstractApplicationContext.refresh()方法,该方法的实现代码如下:
[java]  view plain
  1. public void refresh() throws BeansException, IllegalStateException {  
  2.         synchronized (this.startupShutdownMonitor) {  
  3.             //1、 Prepare this context for refreshing.  
  4.             prepareRefresh();  
  5.   
  6.             //2、 Tell the subclass to refresh the internal bean factory.  
  7.             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
  8.   
  9.             //3、 Prepare the bean factory for use in this context.  
  10.             prepareBeanFactory(beanFactory);  
  11.   
  12.             try {  
  13.                 //4、 Allows post-processing of the bean factory in context subclasses.  
  14.                 postProcessBeanFactory(beanFactory);  
  15.   
  16.                 //5、 Invoke factory processors registered as beans in the context.  
  17.                 invokeBeanFactoryPostProcessors(beanFactory);  
  18.   
  19.                 //6、 Register bean processors that intercept bean creation.  
  20.                 registerBeanPostProcessors(beanFactory);  
  21.   
  22.                 //7、 Initialize message source for this context.  
  23.                 initMessageSource();  
  24.   
  25.                 //8、 Initialize event multicaster for this context.  
  26.                 initApplicationEventMulticaster();  
  27.   
  28.                 //9、 Initialize other special beans in specific context subclasses.  
  29.                 onRefresh();  
  30.   
  31.                 //10、 Check for listener beans and register them.  
  32.                 registerListeners();  
  33.   
  34.                 //11、 Instantiate all remaining (non-lazy-init) singletons.  
  35.                 finishBeanFactoryInitialization(beanFactory);  
  36.   
  37.                 //12、 Last step: publish corresponding event.  
  38.                 finishRefresh();  
  39.             }  
  40.   
  41.             catch (BeansException ex) {  
  42.                 // Destroy already created singletons to avoid dangling resources.  
  43.                 beanFactory.destroySingletons();  
  44.   
  45.                 // Reset 'active' flag.  
  46.                 cancelRefresh(ex);  
  47.   
  48.                 // Propagate exception to caller.  
  49.                 throw ex;  
  50.             }  
  51.         }  
  52.     }  
这个方法非常关键,通过调用12个其他的方法,完成了整个IoC容器的构建。详细了解这个方法里面的每一行代码,基本上就掌握了Spring大部分的原理、功能和扩展点。下面分析每一个方法的所做的事情。

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

2、obtainFreshBeanFactory()
该方法的作用:创建BeanFactory实例,并解析Spring的xml配置文件。beanFactory的实现类是:ConfigurableListableBeanFactory。方法的实现如下:
[java]  view plain
  1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {  
  2.     refreshBeanFactory();  
  3.     ConfigurableListableBeanFactory beanFactory = getBeanFactory();  
  4.   
  5.     if (logger.isInfoEnabled()) {  
  6.         logger.info("Bean factory for application context [" + getId() + "]: " +  
  7.                 ObjectUtils.identityToString(beanFactory));  
  8.     }  
  9.     if (logger.isDebugEnabled()) {  
  10.         logger.debug(beanFactory.getBeanDefinitionCount() + " beans defined in " + this);  
  11.     }  
  12.   
  13.     return beanFactory;  
  14. }  
该方法主要通过调用AbstractRefreshableApplicationContext.refreshBeanFactory()方法完成相关功能:
[java]  view plain
  1. protected final void refreshBeanFactory() throws BeansException {  
  2.     if (hasBeanFactory()) {  
  3.         destroyBeans();  
  4.         closeBeanFactory();  
  5.     }  
  6.     try {  
  7.         DefaultListableBeanFactory beanFactory = createBeanFactory(); //1、创建beanFactory对象  
  8.         customizeBeanFactory(beanFactory);  
  9.         loadBeanDefinitions(beanFactory);//2、解析spring的xml配置文件,加载bean定义信息  
  10.         synchronized (this.beanFactoryMonitor) {  
  11.             this.beanFactory = beanFactory;  
  12.         }  
  13.     }  
  14.     catch (IOException ex) {  
  15.         throw new ApplicationContextException(  
  16.                 "I/O error parsing XML document for application context [" + getDisplayName() + "]", ex);  
  17.     }  
  18. }  
首先,通过createBeanFactory, 创建了DefaultListableBeanFactory类型的beanFactory实例 。分析一下BeanFactory的类图结构,如下:
Spring IoC容器构建过程分析(一)_第2张图片
创建了beanFactory之后,通过调用AbstractXmlApplicationContext.loadBeanDefinitions方法,加载spring的xml配置文件,把用户通过配置文件定义的bean,解析成容器中以特定的数据结构描述的bean定义。AbstractXmlApplicationContext.loadBeanDefinitions方法的实现如下:
[java]  view plain
  1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {  
  2.     // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
  3.     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //1、创建BeanDefinitionReader对象  
  4.   
  5.     // Configure the bean definition reader with this context's  
  6.     // resource loading environment.  
  7.     beanDefinitionReader.setResourceLoader(this);  
  8.     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 2、设置xml文件schema的解析器,获取xsd文件。  
  9.   
  10.     // Allow a subclass to provide custom initialization of the reader,  
  11.     // then proceed with actually loading the bean definitions.  
  12.     initBeanDefinitionReader(beanDefinitionReader);  
  13.     loadBeanDefinitions(beanDefinitionReader); //3、加载spring的xml配置文件,并解析。  
  14. }  
第一步,创建bean的解析类:XmlBeanDefinitionReader,将bean的解析工作,托管给BeanDefinitionReader处理。BeanDefinitionReader的类结构图如下:
Spring IoC容器构建过程分析(一)_第3张图片
第二步, 创建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的注册,方法的实现如下:
[java]  view plain
  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
  2.         Assert.notNull(encodedResource, "EncodedResource must not be null");  
  3.         if (logger.isInfoEnabled()) {  
  4.             logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
  5.         }  
  6.   
  7.         Set currentResources = (Set) this.resourcesCurrentlyBeingLoaded.get();  
  8.         if (currentResources == null) {  
  9.             currentResources = new HashSet(4);  
  10.             this.resourcesCurrentlyBeingLoaded.set(currentResources);  
  11.         }  
  12.         if (!currentResources.add(encodedResource)) {  
  13.             throw new BeanDefinitionStoreException(  
  14.                     "Detected recursive loading of " + encodedResource + " - check your import definitions!");  
  15.         }  
  16.         try {  
  17.             InputStream inputStream = encodedResource.getResource().getInputStream();  
  18.             try {  
  19.                 InputSource inputSource = new InputSource(inputStream);  
  20.                 if (encodedResource.getEncoding() != null) {  
  21.                     inputSource.setEncoding(encodedResource.getEncoding());  
  22.                 }  
  23.                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  
  24.             }  
  25.             finally {  
  26.                 inputStream.close();  
  27.             }  
  28.         }  
  29.         catch (IOException ex) {  
  30.             throw new BeanDefinitionStoreException(  
  31.                     "IOException parsing XML document from " + encodedResource.getResource(), ex);  
  32.         }  
  33.         finally {  
  34.             currentResources.remove(encodedResource);  
  35.             if (currentResources.isEmpty()) {  
  36.                 this.resourcesCurrentlyBeingLoaded.set(null);  
  37.             }  
  38.         }  
  39.     }  
  40. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  
  41.             throws BeanDefinitionStoreException {  
  42.         try {  
  43.             int validationMode = getValidationModeForResource(resource);  
  44.             Document doc = this.documentLoader.loadDocument(  
  45.                     inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());  
  46.             return registerBeanDefinitions(doc, resource);  
  47.         }  
  48.         catch (BeanDefinitionStoreException ex) {  
  49.             throw ex;  
  50.         }  
  51.         catch (SAXParseException ex) {  
  52.             throw new XmlBeanDefinitionStoreException(resource.getDescription(),  
  53.                     "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);  
  54.         }  
  55.         catch (SAXException ex) {  
  56.             throw new XmlBeanDefinitionStoreException(resource.getDescription(),  
  57.                     "XML document from " + resource + " is invalid", ex);  
  58.         }  
  59.         catch (ParserConfigurationException ex) {  
  60.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  61.                     "Parser configuration exception parsing XML from " + resource, ex);  
  62.         }  
  63.         catch (IOException ex) {  
  64.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  65.                     "IOException parsing XML document from " + resource, ex);  
  66.         }  
  67.         catch (Throwable ex) {  
  68.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  69.                     "Unexpected exception parsing XML document from " + resource, ex);  
  70.         }  
  71.     }  
在doLoadBeanDefinitions方法中,调用DefaultDocumentLoader.loadDocument方法,使用JAXP加载xml文档;然后调用registerBeanDefinitions方法,通过DefaultBeanDefinitionDocumentReader.registerBeanDefinitions,注册定义的bean。registerBeanDefinitions方法实现如下:
[java]  view plain
  1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {  
  2.         this.readerContext = readerContext;  
  3.   
  4.         logger.debug("Loading bean definitions");  
  5.         Element root = doc.getDocumentElement();  
  6.   
  7.         BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);  
  8.   
  9.         preProcessXml(root);// 前置处理方法,是一个空实现  
  10.         parseBeanDefinitions(root, delegate);<span><span class="comment">// 解析整个文档,循环处理各个子节点</span></span>  
  11.         postProcessXml(root);// 后置处理方法,也是一个空实现  
  12.     }  
  13.     protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {  
  14.         if (delegate.isDefaultNamespace(root.getNamespaceURI())) {  
  15.             NodeList nl = root.getChildNodes();  
  16.             for (int i = 0; i < nl.getLength(); i++) {  
  17.                 Node node = nl.item(i);  
  18.                 if (node instanceof Element) {  
  19.                     Element ele = (Element) node;  
  20.                     String namespaceUri = ele.getNamespaceURI();  
  21.                     if (delegate.isDefaultNamespace(namespaceUri)) {//<span><span class="comment">如果是默认名字空间(beans),则直接使用解析</span><span></span></span>  
  22.                         parseDefaultElement(ele, delegate);  
  23.                     }  
  24.                     else {  
  25.                         delegate.parseCustomElement(ele);//对于非默认名字空间,如util,使用对应的NamespaceHandlerResolver实现类的parse方法进行解析。  
  26.                     }  
  27.                 }  
  28.             }  
  29.         }  
  30.         else {  
  31.             delegate.parseCustomElement(root);  
  32.         }  
  33.     }  
在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 本身需要的一些工具类:
[java]  view plain
  1. protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {  
  2.         // Tell the internal bean factory to use the context's class loader.  
  3.         beanFactory.setBeanClassLoader(getClassLoader());  
  4.   
  5.         // Populate the bean factory with context-specific resource editors.  
  6.         beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this));  
  7.   
  8.         // Configure the bean factory with context callbacks.  
  9.         beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));  
  10.         beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);  
  11.         beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);  
  12.         beanFactory.ignoreDependencyInterface(MessageSourceAware.class);  
  13.         beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);  
  14.   
  15.         // BeanFactory interface not registered as resolvable type in a plain factory.  
  16.         // MessageSource registered (and found for autowiring) as a bean.  
  17.         beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);  
  18.         beanFactory.registerResolvableDependency(ResourceLoader.classthis);  
  19.         beanFactory.registerResolvableDependency(ApplicationEventPublisher.classthis);  
  20.         beanFactory.registerResolvableDependency(ApplicationContext.classthis);  
  21.   
  22.         // Detect a LoadTimeWeaver and prepare for weaving, if found.  
  23.         if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME) && JdkVersion.isAtLeastJava15()) {  
  24.             // Register the (JDK 1.5 specific) LoadTimeWeaverAwareProcessor.  
  25.             try {  
  26.                 Class ltwapClass = ClassUtils.forName(  
  27.                         "org.springframework.context.weaving.LoadTimeWeaverAwareProcessor",  
  28.                         AbstractApplicationContext.class.getClassLoader());  
  29.                 BeanPostProcessor ltwap = (BeanPostProcessor) BeanUtils.instantiateClass(ltwapClass);  
  30.                 ((BeanFactoryAware) ltwap).setBeanFactory(beanFactory);  
  31.                 beanFactory.addBeanPostProcessor(ltwap);  
  32.             }  
  33.             catch (ClassNotFoundException ex) {  
  34.                 throw new IllegalStateException("Spring's LoadTimeWeaverAwareProcessor class is not available");  
  35.             }  
  36.             // Set a temporary ClassLoader for type matching.  
  37.             beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));  
  38.         }  
  39.     }  
该方法主要分成四步,如下:
第一步
,设置类加载器;
第二步 ,设置属性编辑器注册类,用来注册相关的属性编辑器。ResourceEditorRegistrar类,注册的属性编辑器如下:
[java]  view plain
  1. public void registerCustomEditors(PropertyEditorRegistry registry) {  
  2.         ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader);  
  3.         registry.registerCustomEditor(Resource.class, baseEditor);  
  4.         registry.registerCustomEditor(InputStream.classnew InputStreamEditor(baseEditor));  
  5.         registry.registerCustomEditor(File.classnew FileEditor(baseEditor));  
  6.         registry.registerCustomEditor(URL.classnew URLEditor(baseEditor));  
  7.   
  8.         ClassLoader classLoader = this.resourceLoader.getClassLoader();  
  9.         registry.registerCustomEditor(Class.classnew ClassEditor(classLoader));  
  10.         registry.registerCustomEditor(URI.classnew URIEditor(classLoader));  
  11.   
  12.         if (this.resourceLoader instanceof ResourcePatternResolver) {  
  13.             registry.registerCustomEditor(Resource[].class,  
  14.                     new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader));  
  15.         }  
  16.     }  
例如InputStreamEditor这个属性编辑器,可以将spring配置文件中,用字符串表示的文件路径,转成Resource资源类,注入到bean的对应属性中。同样,我们可以通过自定义的属性编辑器,将spring配置文件中以某种格式定义的字符串,转成对应的Java对象。spring的属性编辑器类图结构如下:
Spring IoC容器构建过程分析(一)_第4张图片
第三步: 设置内置的BeanPostProcessor:ApplicationContextAwareProcessor。该BeanPostProcessor的作用是,为实现特殊接口的bean,注入容器类(例如为实现ApplicationContextAware接口的类,注入ApplicationContext对象实例),如下:
[java]  view plain
  1. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
  2.     if (bean instanceof ResourceLoaderAware) {  
  3.         ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);  
  4.     }  
  5.     if (bean instanceof ApplicationEventPublisherAware) {  
  6.         ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);  
  7.     }  
  8.     if (bean instanceof MessageSourceAware) {  
  9.         ((MessageSourceAware) bean).setMessageSource(this.applicationContext);  
  10.     }  
  11.     if (bean instanceof ApplicationContextAware) {  
  12.         ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);  
  13.     }  
  14.     return bean;  
  15. }  
第四步 :调用ignoreDependencyInterface,设置忽略自动注入的接口(因为这些接口已经通过ApplicationContextAwareProcessor注入了)。
第五步:调用registerResolvableDependency,注入特殊的对象。

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

你可能感兴趣的:(Spring IoC容器构建过程分析(一))