Spring源码分析----IOC容器的实现(IoC容器的初始化过程(定位、载入解析、注册))
来源:互联网 发布:知乎 恩牛网络 编辑:程序博客网 时间:2024/04/29 17:38
本文参考《Spring技术内幕第2版》,spring4
1.BeanFactory的应用场景
BeanFactory提供的是最基本的IOC容器的功能。BeanFactory接口定义了IOC容器最基本的形式,并且提供了IOC容器所应该遵守的最基本的服务契约,同是,这也是我们使用IOC容器所遵守的最底层和最基本的编程规划,这些接口定义勾画了IOC的基本轮廓。在Spring的代码实现中,BeanFactory只是一个接口类,并没有给出容器的具体实现,而XmlBeanFactory、ApplicationContext等类都可以看成是容器附加了某种功能的具体实现,也就是容器体系中的具体容器产品。用户使用容器时,可以使用转义符"&"来得到FactoryBean本身,用于区分通过容器来获取FactoryBean产生的对象和获取FactoryBean本身。如myJndiObject是一个FactoryBean,那么使用&myJndiObject得到的是FactoryBean,而不是myJndiObject这个FactoryBean产生出来 的对象。
注意:FactoryBean和BeanFactory这两个类的区别:
1).BeanFactory是一个Factory,也就是IOC容器或对象工厂。
2).FactoryBean是一个Bean,在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但FactoryBean这个Bean不是简单的Bean,它是一个能产生或修饰对象生成的工厂Bean,它的实现与设计模式中的工大模式和修饰器模式类似。
BeanFactory接口设计了getBean方法,这个方法是使用IoC容器API的主要方法,通过这个方法,可以取得IoC容器中管理的Bean, Been的取得是通过指定名字来进行索引的。如果需要在获取Bean时对Bean的类型进行检查,BeanFactory接口定义了带有参数的getBean方法,这个方法的使用与无参的getBean方法类似,不同的是增加了对Bean检索的类型的要求。
用户可以通过BeanFactory接口方法getBean来使用Bean名字,从而当获取Bean时,如果需要获取的Bean是prototype类型的,用户还可以为这个prototype类型的Bean生成指定构造函数的对应参数。这使得在一定程度上可以控制生成prototype类型的Bean。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver { String getId(); String getApplicationName(); String getDisplayName(); long getStartupDate(); ApplicationContext getParent(); AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;}
public interface BeanFactory { String FACTORY_BEAN_PREFIX = "&"; Object getBean(String var1) throws BeansException; <T> T getBean(String var1, Class<T> var2) throws BeansException; <T> T getBean(Class<T> var1) throws BeansException; Object getBean(String var1, Object... var2) throws BeansException; <T> T getBean(Class<T> var1, Object... var2) throws BeansException; // 判断容器是否含有指定名字的Bean boolean containsBean(String var1); // 查询指定名字的Bean是否是Singleton类型的Bean。对于Singleton类型,用户可以在BeanDefinition中指定 boolean isSingleton(String var1) throws NoSuchBeanDefinitionException; // 查询指定名字的Bean是否是prototype类型的。该属性也可以在BeanDefinition中指定 boolean isPrototype(String var1) throws NoSuchBeanDefinitionException; // 查询指定了名字的Bean的Class类型是否是特定的Class类型,这个Class类型可以由用户来指定 boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException; // 查询指定名字的Bean的Class类型 Class<?> getType(String var1) throws NoSuchBeanDefinitionException; // 查询指定了名字的Bean的所有别名,这些别名由用户在BeanDefinition中定义 String[] getAliases(String var1);}
// 编程式使用IoC容器 ClassPathResource res = new ClassPathResource("bean.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res);在使用IOC容器时,需要如下几个步骤:
1).创建IOC配置文件的抽象资源,这个抽象包含了BeanDefinition的定义信息
2).创建一个BeanFactory。如DefaultListableBeanFactory。
3).创建一个载入BeanDefinition的读取器。如使用XmlBeanDefinitionReader来载入XMl文件形式的BeanDefinition,通过一个回调配置给BeanFactory。
4).从定义好的资源位置读入配置信息,如由XmlBeanDefinitionReader完成。再完成整个载入和注册Bean定义之后,需要的IOC容器就建立起来了,这个时候就可能直接使用IOC容器了。
2.ApplicationContext
我们了解了IoC容器建立的基本步骤。现在可以很方便地通过编程的方式来一手工控制这些配置和容器的建立过程了 。但是,在Spring中,系统已经为用户提供了许多已经定义好的容器实现,而不需要开发人员事必躬亲。相比那些简单拓展BeanFactory的基本IoC容器,开发人员常用的ApplicationContext除了能够提供前面看到的容器的基木功能外,还为用户提供了以下的附加服务,可以更方便地使用。ApplicationContext是一个高级形态意义的IoC容器,如下是BeanFactory没有的新功能:作用1:责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期。
作用2:提供更完整的框架功能:
a. 国际化支持,支持不能的信息源。
b. 资源访问:体现在对ResourceLoader和Resources的支持上
c. 事件传递:通过实现ApplicationContextAware接口
二、IoC容器的初始化过程
简单来说,IoC容器的初始化过程是在refresh()(AbstractApplicationContext类具体实现)方法中启动的,这个方法标志着IoC容器的正式启动。包括BeanDefinition的Resource定位,载入和注册三个过程,Spring把这三个过程分开,并使用不同的模块来城。1.Resource的定位
这个Resource指的是BeanDefinition的资源定位,由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。对于BeanDefination的存在形式,文件系统中的Bean定义信息可以使用FileSystemResource来进行抽象;类路径中的Bean定义信息可以使用ClassPathResource来抽象。这个定位过程类似于容器寻找数据的过程,就像用水桶装水先要把水找到一样。
2.BeanDefinition的载入
该载入过程把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。下面介绍这个数据结构的详细定义。具体来说,这个BeanDefinition实际上就是POJO对象在IoC容器中的抽象,通过这个BeanDefinition定义的数据结构,使得IoC容器能够方便地对POJO对象也就是Bean进行管理。
3.向IoC容器注册这些BeanDefinition
这个过程是通过 调用BeanDefinitionRegistry接口的实现来完成的。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过分析,在IoC容器内部将BeanDefinition注入到一个HashMap中去,IoC容器就是通过向这个HashMap来持有这个BeanDefinition数据的。
注意,这里说的是IoC容器初始化过程,在这个过程,一般不包括Bean依赖注入的实现。在Spring IoC的设计中,Bean定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候。但有一个例外需要注意,在使用IoC容器时有一个预实例化的配置,通过这个预实例化的配置(具体来说,可以通过为Bean定义信息中的layinit属性),用户可以对容器初始化过程作一个微小的控制,从而改变这个被设置了lazyinit属性的Bean的依赖注入过程。举例来说,如果我们对某个Bean设置了lazyinit属性,那么这个Bean的依赖注入在IoC容器初始化时就预先完成了,而不需要等到整个初始化完成以后,第一次使用getBean时才会触发。
2.1.BeanDefinition的Resource定位
以编程的方式使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition。这时使用的是ClassPathResource,这意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition信息。
ClassPathResource res = new ClassPathResource("bean.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res);这里定义的Resource并不能直接由DefaultListableBeanFactory直接使用,Spring通过BeanSefinitionReader来对这些信息进行处理。而使用ApplicationContext的好处就是,已经为我们提供了一系列加载不同Resource的记取器的实现,而DefaultListableBeanFactory只是一个纯粹的IoC容器,需要为它配置特定的读取器才能完成这些功能,但它的好处就是这种更底层的容器,能提高定制IoC容器的灵活性。
//1. 创建 Spring 的 IOC 容器(ApplicationContext代表Spring 的 IOC 容器) //创建的时候会先调用无参构造器,同时会调用 setter方法对属性赋值!! ApplicationContext ctx = new ClassPathXmlApplicationContext("Spring4_IOC/applicationContext.xml"); //2. 从 IOC 容器中的id获取 bean 的实例 HelloWorldBean helloWorldBean = (HelloWorldBean) ctx.getBean("helloWorld");我们经常使用的ApplicationContext,通过类的名字分析就可能清楚地看到它们可以提供哪些不同的Resource读入功能,如FileSystemXmlApplicationContext可以从文件系统载入Resource,ClassPathXmlApplicationContext可以从ClassPath载入Resource,XmlWebApplicationContext可以在Web容器中载入Resource。
下面以ClassPathXmlApplicationContext为例,通过分析 这个ApplicationContext的实现来看它是怎样完成这个Resources定位过程的。先看看继承体系
从上面的继承体系可以看到,这个ClassPathXmlApplicationContext已经通过继承AbstractApplicationContext具备了ResourceLoader读入以Resource定义的BeanDefinition的能力,因为AbstractApplicationContext的基类是DefaultResourceLoader。
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { private Resource[] configResources; …… //省去其他的构造方法 public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, ApplicationContext parent) throws BeansException { super(parent); Assert.notNull(paths, "Path array must not be null"); Assert.notNull(clazz, "Class argument must not be null"); this.configResources = new Resource[paths.length]; for(int i = 0; i < paths.length; ++i) { this.configResources[i] = new ClassPathResource(paths[i], clazz); } this.refresh(); } protected Resource[] getConfigResources() { return this.configResources; }}-----------------------------------------------------------------------------------------------------------------------
下面为refresh的实现,它详细的描述了整个ApplicationContext的初始化过程。
//AbstractApplicationContext // IoC容器执行refresh的过程 public void refresh() throws BeansException, IllegalStateException { Object var1 = this.startupShutdownMonitor; synchronized(this.startupShutdownMonitor) { this.prepareRefresh(); // 在子类中启动refreshBeanFactory()的地方,也是IOC初始化过程(定位,载入解析,注册) ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); this.prepareBeanFactory(beanFactory); try {// 设置BeanFactory的后置处理 this.postProcessBeanFactory(beanFactory);// 调用BeanFactory的后处理器,这些后处理器在Bean定义中向容器注册的 this.invokeBeanFactoryPostProcessors(beanFactory);// 注册Bean的后处理器,在Bean创建过程中调用 this.registerBeanPostProcessors(beanFactory);// 对上下文中的消息源进行初始化 this.initMessageSource();// 初始化上下文中的事件机制 this.initApplicationEventMulticaster();// 初始化其他 的特殊Bean this.onRefresh();// 实例化所有的(non-lazy-init)单件 this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory);// 发布容器事件,结束Refresh过程 this.finishRefresh(); } catch (BeansException var9) { if(this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); }// 为防止Bean资源被占用,在异常处理中,销毁已经在前面过程中生成的单件Bean this.destroyBeans();// 重置 'active'标志 this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); } } } protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { this.refreshBeanFactory(); // ------------> ConfigurableListableBeanFactory beanFactory = this.getBeanFactory(); if(this.logger.isDebugEnabled()) { this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory); } return beanFactory; } protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;重点看AbstractRefreshableApplicationContext的refreshBeanFactory方法的实现
// AbstractRefreshableApplicationContext类的方法,该方法与前面的编程式的方法使用IoC容器的过程非常类似 protected final void refreshBeanFactory() throws BeansException { // 在创建IoC容器前,如果已经有容器存在,那么需要把已有的容器销毁和关闭, // 保存refresh以后使用的是新建立起来的IoC容器 // 这个refresh类似重启计算机一样 if(this.hasBeanFactory()) { this.destroyBeans(); this.closeBeanFactory(); } try { // 构建一个IoC容器供ApplicationContext使用 DefaultListableBeanFactory ex = this.createBeanFactory(); ex.setSerializationId(this.getId()); this.customizeBeanFactory(ex); this.loadBeanDefinitions(ex); // 启动BeanDefinition的载入 Object var2 = this.beanFactoryMonitor; synchronized(this.beanFactoryMonitor) { this.beanFactory = ex; } } catch (IOException var5) { throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5); } } // 这就是在上下文中创建DefaultListableBeanFactory的地方,而getInternalParentBeanFactory()具体实现可以 // 参考父类AbstractApplicationContext中的实现,会根据容器已有的双亲IoC容器的信息 // 来生成DefaultListableBeanFactory 的双亲容器 protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(this.getInternalParentBeanFactory()); } // 这里是使用BeanDefinitionReader载入Bean定义的地方,因为允许有多种载入方式,虽然用得最多的是XMl定义的形式, // 这里通过一个抽象函数把具体的实现委托给子类来完成 protected abstract void loadBeanDefinitions(DefaultListableBeanFactory var1) throws BeansException, IOException;
//XmlApplicationContext里的方法 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// 1.初始化读取器XmlBeanDefinitionReader,// 创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中去, // 创建BeanFactory的过程可以参考上文对编程式使用IoC容器的相关分析,使用的也是DefaultListableBeanFactory XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.setEnvironment(this.getEnvironment());// 2.把这个读取器在IoC容器中设置好(过程和编程式类似) // 这里设置XmlBeanDefinitionReader,为XmlBeanDefinitionReader 配置ResourceLoader,// 因为DafaultResourceLoader是父类,所以this可以直接被使用 beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); this.initBeanDefinitionReader(beanDefinitionReader); // 这里启动读取器来完成BeanDefinition在IoC容器中的载入 this.loadBeanDefinitions(beanDefinitionReader); } protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) { reader.setValidating(this.validating); } protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { // 得到BeanDefinition信息的Resource定位 // 以String的形式获得配置文件的位置 String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); --> } } }
// AbstractBeanDefinitionReader public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {return loadBeanDefinitions(location, null); } // 主要看这个方法 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {// 这里取得ResourceLoader,使用的DefaultResourceLoaderResourceLoader resourceLoader = getResourceLoader();if (resourceLoader == null) {throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");}// 这里对Resource路径模式进行解析,比如我们设定的各种Ant格式的路径定义,得到需要的Resource集合, // 这些Resource集合指向我们已经定义好的BeanDefinition信息,可以是多个文件if (resourceLoader instanceof ResourcePatternResolver) {// Resource pattern matching available.try {Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);int loadCount = loadBeanDefinitions(resources);if (actualResources != null) {for (Resource resource : resources) {actualResources.add(resource);}}if (logger.isDebugEnabled()) {logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");}return loadCount;}catch (IOException ex) {throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", ex);}}else {// 调用DefaultResrouceLoader的getResource完成具体的Resource定位!!!!!!!!!!!!!! -------->Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); /// 载入!!!!!!!if (actualResources != null) {actualResources.add(resource);}if (logger.isDebugEnabled()) {logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");}return loadCount;}}---------------------------------------------------------// DefaultResourceLoader // 取得Resource的具体过程public Resource getResource(String location) {Assert.notNull(location, "Location must not be null");for (ProtocolResolver protocolResolver : this.protocolResolvers) {Resource resource = protocolResolver.resolve(location, this);if (resource != null) {return resource;}}if (location.startsWith("/")) {// 该方法由FileSystemXmlApplicationContext类里覆盖的方法实return getResourceByPath(location);}else if (location.startsWith(CLASSPATH_URL_PREFIX)) {// ClassPathXmlApplicationContextreturn new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());}else {try {URL url = new URL(location);return new UrlResource(url);} catch (MalformedURLException ex) {return getResourceByPath(location);}}}在BeanDefinition定位完成以后的基础上,就可以通过返回Resource对象来进行BeanDefinition的载入了。在定位过程完成以后,为BeanDefinition的载入创造了I/O操作的条件,但是具体的数据还没有开始读入。
------------------------------------------------------------------------------------------------------------
2.2.BeanDefinition的载入和解析
对IoC容器来说,这个载入过程,相当于把定义的BeanDefinition在IoC容器中转化成一个Spring内部表示的数据结构的过程。IoC容器对Bean的管理的依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关操作来完成。这些BeanDefinition数据在IoC窗口中通过 一个HashMap来保持维护,这只是一种比较简单的维护方式,如果需要提高IoC容器的性能和容量,完成可以自己做一些扩展。从IoC容器的初始化入口,也就是refresh方法。
//AbstractBeanDefinitionReader类载入BeanDefinition@Overridepublic int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {Assert.notNull(resources, "Resource array must not be null");int counter = 0;for (Resource resource : resources) {// 启动载入BeanDefinition的过程,这个过程会遍历 // 整个Resource集合所包含的BeanDefinition信息 // 这个方法是一个接口方法counter += loadBeanDefinitions(resource);}return counter;}————————————————————————————————public interface BeanDefinitionReader { BeanDefinitionRegistry getRegistry(); ResourceLoader getResourceLoader(); ClassLoader getBeanClassLoader(); BeanNameGenerator getBeanNameGenerator(); int loadBeanDefinitions(Resource var1) throws BeanDefinitionStoreException; // int loadBeanDefinitions(Resource... var1) throws BeanDefinitionStoreException; int loadBeanDefinitions(String var1) throws BeanDefinitionStoreException; int loadBeanDefinitions(String... var1) throws BeanDefinitionStoreException;}//————————————————————— /*在loadBeanDefinitions中,在读取器中,需要得到代表XML文件的Resource,因为这个Resource对象封装了对XML文件的I/O操作,所以读取器可以在打开I/O流后得到XML的文件对象。有了这个文件对象以后,应该可以按照Spring的Bean定义规则来对这个XML的文档进行解析了,这个解析是交给BeanDefinitionParserDelegate来完成的,具体代码如下:*/// XmlBeanDefinitionReader // 这里是调用的入口:对BeanDefinition的载入实现 public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return this.loadBeanDefinitions(new EncodedResource(resource)); } // 这里是载入XML形式的BeanDefinition的地方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<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet<EncodedResource>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {// 这里得到XML文件,并得到IO的InputSource准备进行读取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.remove();}}} // 具体的读取过程可以在该方法中找到 // 这是从特定的XML文件中实际载入BeanDefinition的地方 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document ex = this.doLoadDocument(inputSource, resource); // 这个方法有时间分析 // 这里启动的是对BeanDefinition解析的详细过程,这个解析会使用到Spring的Bean的配置规则,下面会详解 // Spring的BeanDefinition是怎么按照Spring的Bank语义要求进行解析并转化为容器内部数据结构的, // 这个过程是在这个方法中完成的 return this.registerBeanDefinitions(ex, resource); // } catch (BeanDefinitionStoreException var4) { throw var4; } catch (SAXParseException var5) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5); } catch (SAXException var6) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6); } catch (ParserConfigurationException var7) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7); } catch (IOException var8) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8); } catch (Throwable var9) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9); } } public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {// 这里得到BeanDefinitionDocumentReader 来对XML的BeanDefinition进行解析 BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader(); int countBefore = this.getRegistry().getBeanDefinitionCount();// 具体的解析过程在这里完成,下面接口的方法 documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource)); return this.getRegistry().getBeanDefinitionCount() - countBefore; } protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return (BeanDefinitionDocumentReader)BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass)); }----------------------------------------------public interface BeanDefinitionDocumentReader { void registerBeanDefinitions(Document var1, XmlReaderContext var2) throws BeanDefinitionStoreException;}------------------------------------------------通过以上对实现原理的分析,可以看到,调用IoC容器的refresh来启动整个BeanDefinition的载入过程的,这个初始化是通过定义的XmlBeanDefinitionReader来完成的。同时,实际使用的IoC容器是DefaultListableBeanFactroy,具体的Resource载入在XmlBeanDefinitionReader读入BeanDefinition时实现。注意,因为Spring可以对应不同形式的BeanDefinition,由于这里使用的是XML方式的定义,所以需要使用XmlBeanDefinitionReader。在XmlBeanDefinitionReader的实现中可以看到,是在reader.loadBeanDefinition中开始进行BeanDefinition的载入的,而这时其父类AbstractBeanDefinitionReader已经为BeanDefinition的载入做好了准备,代码如下(上面代码resource加载方法的调用):
--BeanDefinition的载入分两部分,1.首先通过调用XML的解析器得到document对象,但这些document对象并没有按照Spring的Bean规则进行解析。2.在完成通用的XML解析以后,才是按照Spring的Bean规则进行解析的地方,这个按照Spring的Bean规则进行解析的过程是在documentReader中实现的。这里使用的documentReader是默认设置好的DefaultBeanDefinitionDocumentReader。该类D的创建是在后面的方法中完成的,然后再完成BeanDefinition的处理,处理的结果由BeanDefinitionHolder对象来持有。这个BeanDefinitionHolder对象除了持有BeanDefinition对象外,还持有其他与BeanDefinition的使用相关的信息,比如Bean的名字、别名集等。这个BeanDefinitionHolder的生成是通过 对Document文档树的内容进行解析来完成的。可以看到这个解析过程是由BeanDefinitionParserDelegate来实现的(具体在processBeanDefinition方法中实现),同时这个解析是与Spring对BeanDefinition配置规则紧密相关的。代码如下 :
//createBeanDefinitionDocumentReader()方法的代码在上面// DefaultBeanDefinitionDocumentReader public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { ...... this.doRegisterBeanDefinitions(root); } protected void doRegisterBeanDefinitions(Element root) {...... this.parseBeanDefinitions(root, this.delegate); } protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { ...... this.parseDefaultElement(ele, delegate); } private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if(delegate.nodeNameEquals(ele, "import")) { this.importBeanDefinitionResource(ele); } else if(delegate.nodeNameEquals(ele, "alias")) { this.processAliasRegistration(ele); } else if(delegate.nodeNameEquals(ele, "bean")) { this.processBeanDefinition(ele, delegate); // ——> } else if(delegate.nodeNameEquals(ele, "beans")) { this.doRegisterBeanDefinitions(ele); } } /*createBeanDefinitionDocumentReader方法执行后得到documentReader以后,为具体的Spring Bean的解析过程准备好了数据 这里是处理BeanDefinition的地方,具体的处理委托给BeanDefinitionParserDelegate 来完成 Element对应在Spring BeanDefinition中定义的XML元素*/ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {/*BeanDefinitionHolder 是BeanDefinition对象的封装类, 封装了BeanDefinition,Bean的名字和别名,用它来完成向IoC容器注册。得到这个类就意味着BeanDifinition是通过BeanDefinitionParserDelegate对XML元素的信息按照Spring的Bean规则进行解析得到的 */ BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if(bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try {// !!!!!这里是向IoC容器注册解析得到BeanDefinition的地方!!!!!!!----> BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException var5) { this.getReaderContext().error("Failed to register bean definition with name \'" + bdHolder.getBeanName() + "\'", ele, var5); } // 在BeanDefinition向IoC容器注册后,发送消息 this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }具体的Spring BeanDefinition的解析是在BeanDefinitionParserDelegate中完成的。这个类包含了对各种Spring Bean定义规则的处理,感兴趣可以自己研究下,比如我们最熟悉的对Bean元素的处理是怎样完成的,也就是怎么处理在XML定义文件中出现的<bean></bean>这个常见的元素信息。在这里会看到对那些熟悉的BeanDefinition定义的处理,比如id,name,aliase等属性元素。
把这些元素值从XML文件相应的元素的属性中读取出来后,设置到生成的BeanDefinitionHolder中去。这些属性的解析还是比较简单的。对于其他元素配置的解析,比如各种Bean的属性配置,通过 一个较为复杂的解析过程,这个过程由parseBeanDefinitionElement来完成的。解析完成以后,会把解析结果放到BeanDefinition对象中并设置到BeanDefinitionHolder中去,如下代码
// BeanDefinitionParserDelegate public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return this.parseBeanDefinitionElement(ele, (BeanDefinition)null); } public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {// 这里取得在<bean>元素中定义的id,name和aliase属性的值 String id = ele.getAttribute("id"); String nameAttr = ele.getAttribute("name"); ArrayList aliases = new ArrayList(); if(StringUtils.hasLength(nameAttr)) { String[] beanName = StringUtils.tokenizeToStringArray(nameAttr, ",; "); aliases.addAll(Arrays.asList(beanName)); } String beanName1 = id; if(!StringUtils.hasText(id) && !aliases.isEmpty()) { beanName1 = (String)aliases.remove(0); if(this.logger.isDebugEnabled()) { this.logger.debug("No XML \'id\' specified - using \'" + beanName1 + "\' as bean name and " + aliases + " as aliases"); } } if(containingBean == null) { this.checkNameUniqueness(beanName1, aliases, ele); }// 这个方法会引发对Bean元素的详细解析 AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName1, containingBean); if(beanDefinition != null) { if(!StringUtils.hasText(beanName1)) { try { if(containingBean != null) { beanName1 = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true); } else { beanName1 = this.readerContext.generateBeanName(beanDefinition); String aliasesArray = beanDefinition.getBeanClassName(); if(aliasesArray != null && beanName1.startsWith(aliasesArray) && beanName1.length() > aliasesArray.length() && !this.readerContext.getRegistry().isBeanNameInUse(aliasesArray)) { aliases.add(aliasesArray); } } if(this.logger.isDebugEnabled()) { this.logger.debug("Neither XML \'id\' nor \'name\' specified - using generated bean name [" + beanName1 + "]"); } } catch (Exception var9) { this.error(var9.getMessage(), ele); return null; } } String[] aliasesArray1 = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName1, aliasesArray1); } else { return null; }
上面介绍了对Bean元素进行解析地过程,也就是BeanDefinition依据XML的<bean>定义被创建的过程。这个BeanDefinition可以看到是对<bean>定义的抽象,如下图。这个数据对象中封闭的数据大多数都是与<bean>定义相关的,也有很多是我们在定义Bean时看到的那些Spring标记,比如常见的init-method,destory-method,factory-method,等等,这个BeanDefinition数据类型是非常重要的,它封装了很多基本数据,这些基本数据都IoC容器需要的。有了这些基本数据,IoC容器才能对Bean配置进行处理,才能实现相应的容器特性。
beanClass,decription,lazyInit这些属性都是在配置bean时经常遇到的,都集中在这里。这个BeanDefinition是IoC容器体系中非常重要的核心数据结构。通过解析以后,这些数据已经做好在IoC容器中大显身手的准备了。对BeanDefinition元素的处理如下代码:
// BeanDefinitionParserDelegate // 对BeanDefinition定义元素的处理。也是具体生成BeanDefinition的地方 public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); /* 这里只是读取定义的<bean>中设置的class名字,然后载入到BeanDefinition中去,只是做个记录, 并不涉及对象的实例化过程,对象的实例化实际上是在依赖注入时完成的*/ String className = null; if(ele.hasAttribute("class")) { className = ele.getAttribute("class").trim(); } try { String ex = null; if(ele.hasAttribute("parent")) { ex = ele.getAttribute("parent"); } // 这里生成需要的BeanDefinition对象,为Bean定义信息的载入做准备 AbstractBeanDefinition bd = this.createBeanDefinition(className, ex); // 这里对当前的Bean元素进行属性解析,并设置description信息 this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description")); // 从名字可以清楚地看到,这里是对各种<bean>元素的信息进行解析的地方 this.parseMetaElements(ele, bd); this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // 解析<bean>的构造函数设置 this.parseConstructorArgElements(ele, bd); // 解析<bean>的property设置 this.parsePropertyElements(ele, bd); this.parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(this.extractSource(ele)); AbstractBeanDefinition var7 = bd; return var7;/* 下面这些异常常是配置Bean出现问题时经常看到的,原来是在这里抛出的这些检查是在 createBeanDefinition时进行的,会检查Bean的class设置是否正确,比如这个类是否能找到*/ } catch (ClassNotFoundException var13) { this.error("Bean class [" + className + "] not found", ele, var13); } catch (NoClassDefFoundError var14) { this.error("Class that bean class [" + className + "] depends on not found", ele, var14); } catch (Throwable var15) { this.error("Unexpected failure during bean definition parsing", ele, var15); } finally { this.parseState.pop(); } return null; }-----------------------------------------------------------------------------------------
2.3.BeanDefinition在IoC容器中的注册
BeanDefinition在IoC容器载入和解析完成后,用户定义的BeanDefinition信息已经在IoC容器内建立起了自己的数据结构以及相应的数据表示,但此时这些数据还不能供IoC容器直接使用,需要在IoC容器中对这些BeanDefinition进行注册。这个注册为IoC容器提供了更友好的使用方式,在DefaultListableBeanFactory中,是通过一个HashMap来持有载入的BeanDefinition的,这个HashMap的定义在DefaultListableBeanFactory中可以看到,如下:private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);
将解析得到的BeanDefinition向IoC容器中的beanDefinitionMap注册的过程是在载入BeanDefinition完成后进行的,注册的调用过程如下:
//这个方法在上面的载入里已经分析过了,这里就不详解了 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if(bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try {// !!!!!这里是向IoC容器注册解析得到BeanDefinition的地方!!!!!!! BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException var5) { this.getReaderContext().error("Failed to register bean definition with name \'" + bdHolder.getBeanName() + "\'", ele, var5); } // 在BeanDefinition向IoC容器注册后,发送消息 this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }// BeanDefinitionReaderUtils public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // String[] aliases = definitionHolder.getAliases(); if(aliases != null) { String[] var4 = aliases; int var5 = aliases.length; for(int var6 = 0; var6 < var5; ++var6) { String alias = var4[var6]; registry.registerAlias(beanName, alias); } } }public interface BeanDefinitionRegistry extends AliasRegistry { void registerBeanDefinition(String var1, BeanDefinition var2) throws BeanDefinitionStoreException; // void removeBeanDefinition(String var1) throws NoSuchBeanDefinitionException; BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException; boolean containsBeanDefinition(String var1); String[] getBeanDefinitionNames(); int getBeanDefinitionCount(); boolean isBeanNameInUse(String var1);}//DefaultListableBeanFactory public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if(beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition)beanDefinition).validate(); } catch (BeanDefinitionValidationException var9) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9); } }/* 这里检查是不是有相同名字的BeanDefinition已经在IoC容器中注册了, 如果有相同名字的BeanDefinition,但又不允许覆盖,那么会抛出异常*/ BeanDefinition oldBeanDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName); if(oldBeanDefinition != null) { if(!this.isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean \'" + beanName + "\': There is already [" + oldBeanDefinition + "] bound."); } if(oldBeanDefinition.getRole() < beanDefinition.getRole()) { if(this.logger.isWarnEnabled()) { this.logger.warn("Overriding user-defined bean definition for bean \'" + beanName + "\' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else if(!beanDefinition.equals(oldBeanDefinition)) { if(this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean \'" + beanName + "\' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else if(this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean \'" + beanName + "\' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } this.beanDefinitionMap.put(beanName, beanDefinition); } else { if(this.hasBeanCreationStarted()) { Map var4 = this.beanDefinitionMap; synchronized(this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); ArrayList updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if(this.manualSingletonNames.contains(beanName)) { LinkedHashSet updatedSingletons = new LinkedHashSet(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else {/* 这是正常注册BeanDefinition的过程,把Bean的名字存入到beanDefinitionNames的同时, 把beanName作为map的key,把beanDefinition作为value存入到IoC容器持有的beanDefinitionMap中去*/ this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if(oldBeanDefinition != null || this.containsSingleton(beanName)) { this.resetBeanDefinition(beanName); } }完成了BeanDefinition的注册,就完成了IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,它们都在beanDefinitionMap里被检索和使用。容器的使作用就是对这些信息进行处理和维护。这些信息是容器建立依赖反转的基础,有了这些基本数据,下面将分析在IoC容器中依赖注入是怎样完成的。
- Spring源码分析----IOC容器的实现(IoC容器的初始化过程(定位、载入解析、注册))
- Spring IOC容器的初始化过程--资源定位
- 【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【I】
- 【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【II】
- Spring 容器IOC的初始化过程
- Spring中IOC容器的初始化过程
- Spring IOC容器的初始化过程
- Spring IoC容器的初始化过程
- Spring技术内幕之IOC容器的实现(01)-IOC容器初始化过程
- Spring IOC学习心得之IOC容器的初始化过程
- IOC容器的初始化过程
- IoC容器的初始化过程
- IoC容器的初始化过程
- Spring源码分析--Ioc容器定位解析资源文件并注册BeanDefinition
- Spring源码解析之IoC容器系列的设计实现(IoC容器系列概况)
- Spring IOC容器初始化过程解析
- Spring IOC 源码分析:容器的启动
- Spring IOC容器初始化过程分析
- SourceTree安装问题
- 日常小错
- hive2.0版本整合hbase1.2.1
- RandomAccessFile
- Laravel框架中常用PHP语法-匿名函数
- Spring源码分析----IOC容器的实现(IoC容器的初始化过程(定位、载入解析、注册))
- Python cmd模块
- 如何在snap中利用socket.io及websocket来进行实时数据更新
- ubuntu制作本地源
- Android 源代码整编失败快速查询
- Android 日期计算类
- KDE 5.9 发布,全局菜单回来了
- 抽象工厂模式(Abstract Factory)- 最易懂的设计模式解析
- 复习