Spring IoC容器构建过程分析(一)
2014-07-09 22:26
453 查看
注:本文分析的内容,针对的是Spring 2.5.6的版本
ApplicationContext是spring IoC容器的顶级接口,其类结构图如下:
从上面的类图中可以看出, ApplicationContext继承了ResourceLoader接口,便于获取外部资源;也间接继承了 BeanFactory接口,这样可以在Spring容器中创建Bean对象;同时也继承了ApplicationEventPublisher接口,用于发送一些事件消息。
通常我们使用这样的一行代码来创建并启动Spring容器:
1、prepareRefresh()
该方法所做的事情相对比较简单:记录容器启动的时间,并设置容器处于活跃状态。
2、obtainFreshBeanFactory()
该方法的作用:创建BeanFactory实例,并解析Spring的xml配置文件。beanFactory的实现类是:ConfigurableListableBeanFactory。方法的实现如下:
创建了beanFactory之后,通过调用AbstractXmlApplicationContext.loadBeanDefinitions方法,加载spring的xml配置文件,把用户通过配置文件定义的bean,解析成容器中以特定的数据结构描述的bean定义。AbstractXmlApplicationContext.loadBeanDefinitions方法的实现如下:
第二步,创建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的注册,方法的实现如下:
至此,完成了BeanFactory实例的创建和Spring的xml配置文件的解析。
3、prepareBeanFactory
创建好 BeanFactory 后,接着调用prepareBeanFactory方法,添加一些 Spring 本身需要的一些工具类:
第一步,设置类加载器;
第二步,设置属性编辑器注册类,用来注册相关的属性编辑器。ResourceEditorRegistrar类,注册的属性编辑器如下:
第三步:设置内置的BeanPostProcessor:ApplicationContextAwareProcessor。该BeanPostProcessor的作用是,为实现特殊接口的bean,注入容器类(例如为实现ApplicationContextAware接口的类,注入ApplicationContext对象实例),如下:
第五步:调用registerResolvableDependency,注入特殊的对象。
4、postProcessBeanFactory
该方法是spring的一个扩展点之一,是一个空方法,留给子类去扩展。子类可以重写该方法,对已经构建的 BeanFactory 的配置根据需要进行修改。例如调用beanFactory.registerResolvableDependency,注入特殊的类。
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,注入特殊的类。
相关文章推荐
- spring源码学习之路---深度分析IOC容器初始化过程(四)
- Spring原理与源码分析系列(二)- Spring IoC容器启动过程分析(上)
- Spring源码分析----IOC容器的实现(IoC容器的初始化过程(定位、载入解析、注册))
- spring源码学习之路---深度分析IOC容器初始化过程(四)
- Spring原理与源码分析系列(三)- Spring IoC容器启动过程分析(下)
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
- spring源码学习之路---深度分析IOC容器初始化过程(四)
- Spring IOC容器初始化过程分析
- spring源码学习之路---深度分析IOC容器初始化过程(三)
- Spring--IoC容器对非lazy-init的singleton Bean实例化过程
- 体验Spring的“IoC容器”对开发过程中的简化 推荐
- Spring 源码学习(二) IOC容器启动过程
- 【Spring】IOC核心源码学习(二):容器初始化过程
- 一 spring源码分析--- IOC容器
- spring源码研究之IoC容器在web容器中初始化过程
- 【Spring】IOC核心源码学习:容器初始化过程
- SPRING源码分析:IOC容器
- Spring源码分析----IoC容器(一)
- Spring IoC容器构建过程分析(二)(草稿,持续整理中)
- Spring源码分析----IoC容器(二)