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

来源:互联网 发布:阿尔法淘宝宝贝 编辑:程序博客网 时间:2024/06/03 14:46

注:本文分析的内容,针对的是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,注入特殊的类。

1 0
原创粉丝点击