- 浏览: 401906 次
- 性别:
- 来自: 南京
最新评论
-
x981114171:
不知大家有没发现,国产书,国人写的书就没几本是给力的,以后国人 ...
《Spring技术内幕》的读者问题交流 -
x981114171:
买了这本书,感觉很不值。来吐槽下,也许你自己是专家,不过写的书 ...
《Spring技术内幕》的读者问题交流 -
851228082:
作者,写的书很好,我觉得幸亏有源码,所以我才能看懂。一边看,一 ...
《Spring技术内幕》的读者问题交流 -
yueshang520:
说的真不错。。学习了
Spring技术内幕——深入解析Spring架构与设计原理(一)IOC实现原理 -
faith789510:
TransactionProxyFactoryBean 什么情 ...
Spring源代码解析(六):Spring声明式事务处理
在认真学习Rod.Johnson的三部曲之一:<<Professional Java Development with the spring framework>>,顺便也看了看源代码想知道个究竟,抛砖引玉,有兴趣的同志一起讨论研究吧!
以下内容引自博客:http://jiwenke-spring.blogspot.com/,欢迎指导:)
在Spring中,IOC容器的重要地位我们就不多说了,对于Spring的使用者而言,IOC容器实际上是什么呢?我们可以说BeanFactory就是我们看到的IoC容器,当然了Spring为我们准备了许多种IoC容器来使用,这样可以方便我们从不同的层面,不同的资源位置,不同的形式的定义信息来建立我们需要的IoC容器。
在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定 - 不管怎么着,作为IOC容器,这些接口你必须要满足应用程序的最基本要求:
在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的bean是怎样定义怎样加载的 - 就像我们只关心从这个工厂里我们得到到什么产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心这些。如果要关心工厂是怎样产生对象的,应用程序需要使用具体的IOC容器实现- 当然你可以自己根据这个BeanFactory来实现自己的IOC容器,但这个没有必要,因为Spring已经为我们准备好了一系列工厂来让我们使用。比如XmlBeanFactory就是针对最基础的BeanFactory的IOC容器的实现 - 这个实现使用xml来定义IOC容器中的bean。
Spring提供了一个BeanFactory的基本实现,XmlBeanFactory同样的通过使用模板模式来得到对IOC容器的抽象- AbstractBeanFactory,DefaultListableBeanFactory这些抽象类为其提供模板服务。其中通过resource 接口来抽象bean定义数据,对Xml定义文件的解析通过委托给XmlBeanDefinitionReader来完成。下面我们根据书上的例子,简单的演示IOC容器的创建过程:
这些代码演示了以下几个步骤:
1. 创建IOC配置文件的抽象资源
2. 创建一个BeanFactory
3. 把读取配置信息的BeanDefinitionReader,这里是XmlBeanDefinitionReader配置给BeanFactory
4. 从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成,这样完成整个载入bean定义的过程。我们的IoC容器就建立起来了。在BeanFactory的源代码中我们可以看到:
我们在后面会看到读取器读取资源和注册bean定义信息的整个过程,基本上是和上下文的处理是一样的,从这里我们可以看到上下文和 XmlBeanFactory这两种IOC容器的区别,BeanFactory往往不具备对资源定义的能力,而上下文可以自己完成资源定义,从这个角度上看上下文更好用一些。
仔细分析Spring BeanFactory的结构,我们来看看在BeanFactory基础上扩展出的ApplicationContext - 我们最常使用的上下文。除了具备BeanFactory的全部能力,上下文为应用程序又增添了许多便利:
* 可以支持不同的信息源,我们看到ApplicationContext扩展了MessageSource
* 访问资源 , 体现在对ResourceLoader和Resource的支持上面,这样我们可以从不同地方得到bean定义资源
* 支持应用事件,继承了接口ApplicationEventPublisher,这样在上下文中引入了事件机制而BeanFactory是没有的。
ApplicationContext允许上下文嵌套 - 通过保持父上下文可以维持一个上下文体系 - 这个体系我们在以后对Web容器中的上下文环境的分析中可以清楚地看到。对于bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的bean定义环境。这个我们在分析Web容器中的上下文环境时也能看到。
ApplicationContext提供IoC容器的主要接口,在其体系中有许多抽象子类比如AbstractApplicationContext为具体的BeanFactory的实现,比如FileSystemXmlApplicationContext和 ClassPathXmlApplicationContext提供上下文的模板,使得他们只需要关心具体的资源定位问题。当应用程序代码实例化 FileSystemXmlApplicationContext的时候,得到IoC容器的一种具体表现 - ApplicationContext,从而应用程序通过ApplicationContext来管理对bean的操作。
BeanFactory 是一个接口,在实际应用中我们一般使用ApplicationContext来使用IOC容器,它们也是IOC容器展现给应用开发者的使用接口。对应用程序开发者来说,可以认为BeanFactory和ApplicationFactory在不同的使用层面上代表了SPRING提供的IOC容器服务。
下面我们具体看看通过FileSystemXmlApplicationContext是怎样建立起IOC容器的, 显而易见我们可以通过new来得到IoC容器:
调用的是它初始化代码:
refresh的模板在AbstractApplicationContext:
这个方法包含了整个BeanFactory初始化的过程,对于特定的FileSystemXmlBeanFactory,我们看到定位资源位置由refreshBeanFactory()来实现:
在AbstractXmlApplicationContext中定义了对资源的读取过程,默认由XmlBeanDefinitionReader来读取:
转到beanDefinitionReader中进行处理:
而在作为其抽象父类的AbstractBeanDefinitionReader中来定义载入过程:
当我们通过ResourceLoader来载入资源,别忘了了我们的GenericApplicationContext也实现了ResourceLoader接口:
而我们的FileSystemXmlApplicationContext就是一个DefaultResourceLoader - GenericApplicationContext()通过DefaultResourceLoader:
我们的FileSystemXmlApplicationContext本身就是是DefaultResourceLoader的实现类,他实现了以下的接口:
这样代码就回到了FileSystemXmlApplicationContext中来,他提供了FileSystemResource来完成从文件系统得到配置文件的资源定义。这样,就可以从文件系统路径上对IOC配置文件进行加载 - 当然我们可以按照这个逻辑从任何地方加载,在Spring中我们看到它提供的各种资源抽象,比如ClassPathResource, URLResource,FileSystemResource等来供我们使用。上面我们看到的是定位Resource的一个过程,而这只是加载过程的一部分 - 我们回到AbstractBeanDefinitionReaderz中的loadDefinitions(resource)来看看得到代表bean文件的资源定义以后的载入过程,默认的我们使用XmlBeanDefinitionReader:
我们看到先把定义文件解析为DOM对象,然后进行具体的注册过程:
具体的在BeanDefinitionDocumentReader中完成对,下面是一个简要的注册过程来完成bean定义文件的解析和IOC容器中bean的初始化
我们看到在parseBeanDefinition中对具体bean元素的解析式交给BeanDefinitionParserDelegate来完成的,下面我们看看解析完的bean是怎样在IOC容器中注册的:
在BeanDefinitionReaderUtils调用的是:
我们看看XmlBeanFactory中的注册实现:
这样就完成了Bean定义在IOC容器中的注册,就可被IOC容器进行管理和使用了。
从上面的代码来看,我们总结一下IOC容器初始化的基本步骤:
* 初始化的入口在容器实现中的refresh()调用来完成
* 对bean 定义载入IOC容器使用的方法是loadBeanDefinition,其中的大致过程如下:通过ResourceLoader来完成资源文件位置的定位,DefaultResourceLoader是默认的实现,同时上下文本身就给出了ResourceLoader的实现,可以从类路径,文件系统, URL等方式来定为资源位置。如果是XmlBeanFactory作为IOC容器,那么需要为它指定bean定义的资源,也就是说bean定义文件时通过抽象成Resource来被IOC容器处理的,容器通过BeanDefinitionReader来完成定义信息的解析和Bean信息的注册,往往使用的是XmlBeanDefinitionReader来解析bean的xml定义文件 - 实际的处理过程是委托给BeanDefinitionParserDelegate来完成的,从而得到bean的定义信息,这些信息在Spring中使用BeanDefinition对象来表示 - 这个名字可以让我们想到loadBeanDefinition,RegisterBeanDefinition这些相关的方法 - 他们都是为处理BeanDefinitin服务的,IoC容器解析得到BeanDefinition以后,需要把它在IOC容器中注册,这由IOC实现 BeanDefinitionRegistry接口来实现。注册过程就是在IOC容器内部维护的一个HashMap来保存得到的 BeanDefinition的过程。这个HashMap是IoC容器持有bean信息的场所,以后对bean的操作都是围绕这个HashMap来实现的。
* 然后我们就可以通过BeanFactory和ApplicationContext来享受到Spring IOC的服务了.
在使用IOC容器的时候,我们注意到除了少量粘合代码,绝大多数以正确IoC风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。 Spring本身提供了对声明式载入web应用程序用法的应用程序上下文,并将其存储在ServletContext中的框架实现。具体可以参见以后的文章。
在使用Spring IOC容器的时候我们还需要区别两个概念:
Beanfactory 和Factory bean,其中BeanFactory指的是IOC容器的编程抽象,比如ApplicationContext, XmlBeanFactory等,这些都是IOC容器的具体表现,需要使用什么样的容器由客户决定但Spring为我们提供了丰富的选择。而 FactoryBean只是一个可以在IOC容器中被管理的一个bean,是对各种处理过程和资源使用的抽象,Factory bean在需要时产生另一个对象,而不返回FactoryBean本省,我们可以把它看成是一个抽象工厂,对它的调用返回的是工厂生产的产品。所有的 Factory bean都实现特殊的org.springframework.beans.factory.FactoryBean接口,当使用容器中factory bean的时候,该容器不会返回factory bean本身,而是返回其生成的对象。Spring包括了大部分的通用资源和服务访问抽象的Factory bean的实现,其中包括:
对JNDI查询的处理,对代理对象的处理,对事务性代理的处理,对RMI代理的处理等,这些我们都可以看成是具体的工厂,看成是SPRING为我们建立好的工厂。也就是说Spring通过使用抽象工厂模式为我们准备了一系列工厂来生产一些特定的对象,免除我们手工重复的工作,我们要使用时只需要在IOC容器里配置好就能很方便的使用了。
现在我们来看看在Spring的事件机制,Spring中有3个标准事件,ContextRefreshEvent, ContextCloseEvent,RequestHandledEvent他们通过ApplicationEvent接口,同样的如果需要自定义时间也只需要实现ApplicationEvent接口,参照ContextCloseEvent的实现可以定制自己的事件实现:
可以通过显现ApplicationEventPublishAware接口,将事件发布器耦合到ApplicationContext这样可以使用 ApplicationContext框架来传递和消费消息,然后在ApplicationContext中配置好bean就可以了,在消费消息的过程中,接受者通过实现ApplicationListener接收消息。
比如可以直接使用Spring的ScheduleTimerTask和TimerFactoryBean作为定时器定时产生消息,具体可以参见《Spring框架高级编程》。
TimerFactoryBean是一个工厂bean,对其中的ScheduleTimerTask进行处理后输出,参考ScheduleTimerTask的实现发现它最后调用的是jre的TimerTask:
在书中给出了一个定时发送消息的例子,当然可以可以通过定时器作其他的动作,有两种方法:
1.定义MethodInvokingTimerTaskFactoryBean定义要执行的特定bean的特定方法,对需要做什么进行封装定义;
2.定义TimerTask类,通过extends TimerTask来得到,同时对需要做什么进行自定义
然后需要定义具体的定时器参数,通过配置ScheduledTimerTask中的参数和timerTask来完成,以下是它需要定义的具体属性,timerTask是在前面已经定义好的bean
最后,需要在ApplicationContext中注册,需要把ScheduledTimerTask配置到FactoryBean - TimerFactoryBean,这样就由IOC容器来管理定时器了。参照
TimerFactoryBean的属性,可以定制一组定时器。
如果要发送时间我们只需要在定义好的ScheduledTimerTasks中publish定义好的事件就可以了。具体可以参考书中例子的实现,这里只是结合FactoryBean的原理做一些解释。如果结合事件和定时器机制,我们可以很方便的实现heartbeat(看门狗),书中给出了这个例子,这个例子实际上结合了Spring事件和定时机制的使用两个方面的知识 - 当然了还有IOC容器的知识(任何Spring应用我想都逃不掉IOC的魔爪:)
完全同意!所以loadBeanDefinition和createBean是两个不同的过程,一个由容器启动的时候触发,一个由客户程序请求服务的时候触发!那大致可以看到启动的时候只是定位 - 读入 - 解析 - 注册,而最后的依赖注入还是要等到客户第一次请求bean的时候触发,如果有的bean不被请求,就不会被依赖注入。
看到了,谢谢!!! 在AbstractFactoryBean中:
在AbstractFactoryBean中的getBean调用createBean
那看来bean的依赖注入是在第一次向容器请求bean的时候完成的,loadBeanDefinition只是把定义的bean和依赖解析了以后存到容器里。这个createBean和getBean要好好看看。
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory中
没有找到这个createBean()是在什么时候别调用的 ..... ??? 有点奇怪
应该在loadBeanDefinition之后,但是没有找到调用它的地方......
我想这里是处理对Property的依赖关系解析,比如如果是一个"bean ref",就会生成一个RunTimeReferenceBean,在代码里可以看到:
在这里对定义的依赖关系已经在BeanDefinition里面持有了,但是还没有对依赖进行注入。看来依赖解析和具体的依赖注入Spring是分成了两个过程来做的。
哪位一起来研究看看 ?
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory中
当中有些步骤没有完全看明白,不过这里有个populateBean方法
我想这里才是解析BeanDefinition对象,并且根据依赖关系以及自动装配方式进行property装载的实现.
然后这里有个类org.springframework.beans.factory.support.BeanDefinitionValueResolver
这里的
可能根据property value的不同进行不同的解析
最后
将真正的property values绑定到BeanWrapper对象.
以上就是bean依赖装配的大致过程吧
哪位一起来研究看看 ?
但是上述的这些过程只是加载资源然后将每1个bean定义封装到BeanDefiniton中,最后将所有BeanDefiniton存放到map中,如:
可以说这里的map才是容器的核心部分.但是这里的map仅仅存放了定义级别的bean,而要获取bean的实例对象,还有个复杂的过程,这个过spring如何来处理的?
同意!
这里应该涉及到Spring IOC容器的核心了,它把用户定义的bean封装成BeanDefinition,然后放到这个BeanDefinitionMap中去 - 一个bean定义对应一个BeanDefintion,但是这些bean之间的依赖关系是怎样管理和怎样实现注入的? - 这是个很好的问题,要再去研究研究代码啦:)
具体要到BeanDefinitionParserDelegate中去看看,这个BeanDefinitionParserDelegate是根据Spring的bean定义规则生成BeanDefinition的主要地方:
至于具体的bean的JAVA对象,只是在BeanDefinition中的一个属性,这个对象的生成是在生成BeanDefinition的时候生成的:
这个BeanDefnition持有了和bean相关的所有配置信息,对于依赖检查和DI的实现, 现在还搞不太明白,也就是说Spring IOC容器是怎样管理bean之间的依赖关系的? - 这个应该是IOC的核心功能。
- 这个要好好看看代码才行。
BeanFactory和ApplicationContext的差别体现在对bean的加载处理上,因为它们的具体实现不尽相同,但是不管是 BeanFactory还是ApplicationContext,他们的加载处理都是由BeanDefinitionReader来完成,这个 BeanDefinitionReader完成信息的读取,解析后,最后一步向IOC容器的注册是通过向持有的BeanFactory回调来完成的 - 向IOC容器注册需要实现BeanDefinitionRegistry接口。这个接口在DefaultListBeanFactory里被实现,也就是说ApplicationContext是通过DefaultListBeanFactory来完成向自己持有的IOC容器注册的 - 当然这个IOC容器就是DefaultListBeanFactory。
下面再看加载过程,对ApplicationContext中而言,往往在初始化的时候就加载bean,比如在refresh的时候AbstractApplicationContext:
这里就为ApplicationContext指定了XmlBeanDefinitionReader,然后是加载过程 - 我们看到这个XmlBeanDefinitionReader持有一个beanFactory, 这个beanFactory是在refresh中得到的:
而这些步骤往往在XmlBeanFactory中是这样实现的:
这些步骤我们在DefaultListableBeanFactory中是看不到的,所以如果要使用
DefaultListableBeanFactory的话,需要编程式的指明BeanDefinitionReader和调用:
如果比较XmlBeanFactory和ApplicationContext,除了XmlBeanFactory不具备那些ResourceLoader和ApplicationEvent这些特定的上下文能力之外,他们持有的IOC容器和加载过程是一样的。
所以BeanDefinitionReader是一个比较重要的类,BeanFactory只维护自己持有的IOC容器,其他都不管,不管你的定义从哪里来,怎么来。
但是上述的这些过程只是加载资源然后将每1个bean定义封装到BeanDefiniton中,最后将所有BeanDefiniton存放到map中,如:
可以说这里的map才是容器的核心部分.但是这里的map仅仅存放了定义级别的bean,而要获取bean的实例对象,还有个复杂的过程,这个过spring如何来处理的?
BeanFactory和ApplicationContext的差别体现在对bean的加载处理上,因为它们的具体实现不尽相同,但是不管是 BeanFactory还是ApplicationContext,他们的加载处理都是由BeanDefinitionReader来完成,这个 BeanDefinitionReader完成信息的读取,解析后,最后一步向IOC容器的注册是通过向持有的BeanFactory回调来完成的 - 向IOC容器注册需要实现BeanDefinitionRegistry接口。这个接口在DefaultListBeanFactory里被实现,也就是说ApplicationContext是通过DefaultListBeanFactory来完成向自己持有的IOC容器注册的 - 当然这个IOC容器就是DefaultListBeanFactory。
下面再看加载过程,对ApplicationContext中而言,往往在初始化的时候就加载bean,比如在refresh的时候AbstractApplicationContext:
这里就为ApplicationContext指定了XmlBeanDefinitionReader,然后是加载过程 - 我们看到这个XmlBeanDefinitionReader持有一个beanFactory, 这个beanFactory是在refresh中得到的:
而这些步骤往往在XmlBeanFactory中是这样实现的:
这些步骤我们在DefaultListableBeanFactory中是看不到的,所以如果要使用
DefaultListableBeanFactory的话,需要编程式的指明BeanDefinitionReader和调用:
如果比较XmlBeanFactory和ApplicationContext,除了XmlBeanFactory不具备那些ResourceLoader和ApplicationEvent这些特定的上下文能力之外,他们持有的IOC容器和加载过程是一样的。
所以BeanDefinitionReader是一个比较重要的类,BeanFactory只维护自己持有的IOC容器,其他都不管,不管你的定义从哪里来,怎么来。
以下内容引自博客:http://jiwenke-spring.blogspot.com/,欢迎指导:)
在Spring中,IOC容器的重要地位我们就不多说了,对于Spring的使用者而言,IOC容器实际上是什么呢?我们可以说BeanFactory就是我们看到的IoC容器,当然了Spring为我们准备了许多种IoC容器来使用,这样可以方便我们从不同的层面,不同的资源位置,不同的形式的定义信息来建立我们需要的IoC容器。
在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定 - 不管怎么着,作为IOC容器,这些接口你必须要满足应用程序的最基本要求:
public interface BeanFactory { //这里是对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象, //如果需要得到工厂本身,需要转义 String FACTORY_BEAN_PREFIX = "&"; //这里根据bean的名字,在IOC容器中得到bean实例,这个IOC容器就是一个大的抽象工厂。 Object getBean(String name) throws BeansException; //这里根据bean的名字和Class类型来得到bean实例,和上面的方法不同在于它会抛出异常:如果根据名字取得的bean实例的Class类型和需要的不同的话。 Object getBean(String name, Class requiredType) throws BeansException; //这里提供对bean的检索,看看是否在IOC容器有这个名字的bean boolean containsBean(String name); //这里根据bean名字得到bean实例,并同时判断这个bean是不是单件 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; //这里对得到bean实例的Class类型 Class getType(String name) throws NoSuchBeanDefinitionException; //这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来 String[] getAliases(String name); }
在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的bean是怎样定义怎样加载的 - 就像我们只关心从这个工厂里我们得到到什么产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心这些。如果要关心工厂是怎样产生对象的,应用程序需要使用具体的IOC容器实现- 当然你可以自己根据这个BeanFactory来实现自己的IOC容器,但这个没有必要,因为Spring已经为我们准备好了一系列工厂来让我们使用。比如XmlBeanFactory就是针对最基础的BeanFactory的IOC容器的实现 - 这个实现使用xml来定义IOC容器中的bean。
Spring提供了一个BeanFactory的基本实现,XmlBeanFactory同样的通过使用模板模式来得到对IOC容器的抽象- AbstractBeanFactory,DefaultListableBeanFactory这些抽象类为其提供模板服务。其中通过resource 接口来抽象bean定义数据,对Xml定义文件的解析通过委托给XmlBeanDefinitionReader来完成。下面我们根据书上的例子,简单的演示IOC容器的创建过程:
ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res);
这些代码演示了以下几个步骤:
1. 创建IOC配置文件的抽象资源
2. 创建一个BeanFactory
3. 把读取配置信息的BeanDefinitionReader,这里是XmlBeanDefinitionReader配置给BeanFactory
4. 从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成,这样完成整个载入bean定义的过程。我们的IoC容器就建立起来了。在BeanFactory的源代码中我们可以看到:
public class XmlBeanFactory extends DefaultListableBeanFactory { //这里为容器定义了一个默认使用的bean定义读取器 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } //在初始化函数中使用读取器来对资源进行读取,得到bean定义信息。 public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); }
我们在后面会看到读取器读取资源和注册bean定义信息的整个过程,基本上是和上下文的处理是一样的,从这里我们可以看到上下文和 XmlBeanFactory这两种IOC容器的区别,BeanFactory往往不具备对资源定义的能力,而上下文可以自己完成资源定义,从这个角度上看上下文更好用一些。
仔细分析Spring BeanFactory的结构,我们来看看在BeanFactory基础上扩展出的ApplicationContext - 我们最常使用的上下文。除了具备BeanFactory的全部能力,上下文为应用程序又增添了许多便利:
* 可以支持不同的信息源,我们看到ApplicationContext扩展了MessageSource
* 访问资源 , 体现在对ResourceLoader和Resource的支持上面,这样我们可以从不同地方得到bean定义资源
* 支持应用事件,继承了接口ApplicationEventPublisher,这样在上下文中引入了事件机制而BeanFactory是没有的。
ApplicationContext允许上下文嵌套 - 通过保持父上下文可以维持一个上下文体系 - 这个体系我们在以后对Web容器中的上下文环境的分析中可以清楚地看到。对于bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的bean定义环境。这个我们在分析Web容器中的上下文环境时也能看到。
ApplicationContext提供IoC容器的主要接口,在其体系中有许多抽象子类比如AbstractApplicationContext为具体的BeanFactory的实现,比如FileSystemXmlApplicationContext和 ClassPathXmlApplicationContext提供上下文的模板,使得他们只需要关心具体的资源定位问题。当应用程序代码实例化 FileSystemXmlApplicationContext的时候,得到IoC容器的一种具体表现 - ApplicationContext,从而应用程序通过ApplicationContext来管理对bean的操作。
BeanFactory 是一个接口,在实际应用中我们一般使用ApplicationContext来使用IOC容器,它们也是IOC容器展现给应用开发者的使用接口。对应用程序开发者来说,可以认为BeanFactory和ApplicationFactory在不同的使用层面上代表了SPRING提供的IOC容器服务。
下面我们具体看看通过FileSystemXmlApplicationContext是怎样建立起IOC容器的, 显而易见我们可以通过new来得到IoC容器:
ApplicationContext = new FileSystemXmlApplicationContext(xmlPath);
调用的是它初始化代码:
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); this.configLocations = configLocations; if (refresh) { //这里是IoC容器的初始化过程,其初始化过程的大致步骤由AbstractApplicationContext来定义 refresh(); } }
refresh的模板在AbstractApplicationContext:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { synchronized (this.activeMonitor) { this.active = true; } // 这里需要子类来协助完成资源位置定义,bean载入和向IOC容器注册的过程 refreshBeanFactory(); ............ }
这个方法包含了整个BeanFactory初始化的过程,对于特定的FileSystemXmlBeanFactory,我们看到定位资源位置由refreshBeanFactory()来实现:
在AbstractXmlApplicationContext中定义了对资源的读取过程,默认由XmlBeanDefinitionReader来读取:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { // 这里使用XMLBeanDefinitionReader来载入bean定义信息的XML文件 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //这里配置reader的环境,其中ResourceLoader是我们用来定位bean定义信息资源位置的 ///因为上下文本身实现了ResourceLoader接口,所以可以直接把上下文作为ResourceLoader传递给XmlBeanDefinitionReader beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); //这里转到定义好的XmlBeanDefinitionReader中对载入bean信息进行处理 loadBeanDefinitions(beanDefinitionReader); }
转到beanDefinitionReader中进行处理:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { //调用XmlBeanDefinitionReader来载入bean定义信息。 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
而在作为其抽象父类的AbstractBeanDefinitionReader中来定义载入过程:
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { //这里得到当前定义的ResourceLoader,默认的我们使用DefaultResourceLoader ResourceLoader resourceLoader = getResourceLoader(); .........//如果没有找到我们需要的ResourceLoader,直接抛出异常 if (resourceLoader instanceof ResourcePatternResolver) { // 这里处理我们在定义位置时使用的各种pattern,需要ResourcePatternResolver来完成 try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); return loadCount; } ........ } else { // 这里通过ResourceLoader来完成位置定位 Resource resource = resourceLoader.getResource(location); // 这里已经把一个位置定义转化为Resource接口,可以供XmlBeanDefinitionReader来使用了 int loadCount = loadBeanDefinitions(resource); return loadCount; } }
当我们通过ResourceLoader来载入资源,别忘了了我们的GenericApplicationContext也实现了ResourceLoader接口:
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { public Resource getResource(String location) { //这里调用当前的loader也就是DefaultResourceLoader来完成载入 if (this.resourceLoader != null) { return this.resourceLoader.getResource(location); } return super.getResource(location); } ....... }
而我们的FileSystemXmlApplicationContext就是一个DefaultResourceLoader - GenericApplicationContext()通过DefaultResourceLoader:
public Resource getResource(String location) { //如果是类路径的方式,那需要使用ClassPathResource来得到bean文件的资源对象 if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // 如果是URL方式,使用UrlResource作为bean文件的资源对象 URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { // 如果都不是,那我们只能委托给子类由子类来决定使用什么样的资源对象了 return getResourceByPath(location); } } }
我们的FileSystemXmlApplicationContext本身就是是DefaultResourceLoader的实现类,他实现了以下的接口:
protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } //这里使用文件系统资源对象来定义bean文件 return new FileSystemResource(path); }
这样代码就回到了FileSystemXmlApplicationContext中来,他提供了FileSystemResource来完成从文件系统得到配置文件的资源定义。这样,就可以从文件系统路径上对IOC配置文件进行加载 - 当然我们可以按照这个逻辑从任何地方加载,在Spring中我们看到它提供的各种资源抽象,比如ClassPathResource, URLResource,FileSystemResource等来供我们使用。上面我们看到的是定位Resource的一个过程,而这只是加载过程的一部分 - 我们回到AbstractBeanDefinitionReaderz中的loadDefinitions(resource)来看看得到代表bean文件的资源定义以后的载入过程,默认的我们使用XmlBeanDefinitionReader:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { ....... try { //这里通过Resource得到InputStream的IO流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //从InputStream中得到XML的解析源 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //这里是具体的解析和注册过程 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { //关闭从Resource中得到的IO流 inputStream.close(); } } ......... } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); //通过解析得到DOM,然后完成bean在IOC容器中的注册 Document doc = this.documentLoader.loadDocument( inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware); return registerBeanDefinitions(doc, resource); } ....... }
我们看到先把定义文件解析为DOM对象,然后进行具体的注册过程:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 这里定义解析器,使用XmlBeanDefinitionParser来解析xml方式的bean定义文件 - 现在的版本不用这个解析器了,使用的是XmlBeanDefinitionReader if (this.parserClass != null) { XmlBeanDefinitionParser parser = (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass); return parser.registerBeanDefinitions(this, doc, resource); } // 具体的注册过程,首先得到XmlBeanDefinitionReader,来处理xml的bean定义文件 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getBeanFactory().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getBeanFactory().getBeanDefinitionCount() - countBefore; }
具体的在BeanDefinitionDocumentReader中完成对,下面是一个简要的注册过程来完成bean定义文件的解析和IOC容器中bean的初始化
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); postProcessXml(root); } protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root.getNamespaceURI())) { //这里得到xml文件的子节点,比如各个bean节点 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)) { //这里是解析过程的调用,对缺省的元素进行分析比如bean元素 parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //这里对元素Import进行处理 if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) { String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); getReaderContext().getReader().getBeanFactory().registerAlias(name, alias); getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } //这里对我们最熟悉的bean元素进行处理 else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) { //委托给BeanDefinitionParserDelegate来完成对bean元素的处理,这个类包含了具体的bean解析的过程。 // 把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要载体,也是IOC容器的管理对象。 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); // 这里是向IOC容器注册,实际上是放到IOC容器的一个map里 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); // 这里向IOC容器发送事件,表示解析和注册完成。 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } } }
我们看到在parseBeanDefinition中对具体bean元素的解析式交给BeanDefinitionParserDelegate来完成的,下面我们看看解析完的bean是怎样在IOC容器中注册的:
在BeanDefinitionReaderUtils调用的是:
public static void registerBeanDefinition( BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory) throws BeansException { // 这里得到需要注册bean的名字; String beanName = bdHolder.getBeanName(); //这是调用IOC来注册的bean的过程,需要得到BeanDefinition beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()); // 别名也是可以通过IOC容器和bean联系起来的进行注册 String[] aliases = bdHolder.getAliases(); if (aliases != null) { for (int i = 0; i < aliases.length; i++) { beanFactory.registerAlias(beanName, aliases[i]); } } }
我们看看XmlBeanFactory中的注册实现:
//--------------------------------------------------------------------- // 这里是IOC容器对BeanDefinitionRegistry接口的实现 //--------------------------------------------------------------------- public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { .....//这里省略了对BeanDefinition的验证过程 //先看看在容器里是不是已经有了同名的bean,如果有抛出异常。 Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { ........... } else { //把bean的名字加到IOC容器中去 this.beanDefinitionNames.add(beanName); } //这里把bean的名字和Bean定义联系起来放到一个HashMap中去,IOC容器通过这个Map来维护容器里的Bean定义信息。 this.beanDefinitionMap.put(beanName, beanDefinition); removeSingleton(beanName); }
这样就完成了Bean定义在IOC容器中的注册,就可被IOC容器进行管理和使用了。
从上面的代码来看,我们总结一下IOC容器初始化的基本步骤:
* 初始化的入口在容器实现中的refresh()调用来完成
* 对bean 定义载入IOC容器使用的方法是loadBeanDefinition,其中的大致过程如下:通过ResourceLoader来完成资源文件位置的定位,DefaultResourceLoader是默认的实现,同时上下文本身就给出了ResourceLoader的实现,可以从类路径,文件系统, URL等方式来定为资源位置。如果是XmlBeanFactory作为IOC容器,那么需要为它指定bean定义的资源,也就是说bean定义文件时通过抽象成Resource来被IOC容器处理的,容器通过BeanDefinitionReader来完成定义信息的解析和Bean信息的注册,往往使用的是XmlBeanDefinitionReader来解析bean的xml定义文件 - 实际的处理过程是委托给BeanDefinitionParserDelegate来完成的,从而得到bean的定义信息,这些信息在Spring中使用BeanDefinition对象来表示 - 这个名字可以让我们想到loadBeanDefinition,RegisterBeanDefinition这些相关的方法 - 他们都是为处理BeanDefinitin服务的,IoC容器解析得到BeanDefinition以后,需要把它在IOC容器中注册,这由IOC实现 BeanDefinitionRegistry接口来实现。注册过程就是在IOC容器内部维护的一个HashMap来保存得到的 BeanDefinition的过程。这个HashMap是IoC容器持有bean信息的场所,以后对bean的操作都是围绕这个HashMap来实现的。
* 然后我们就可以通过BeanFactory和ApplicationContext来享受到Spring IOC的服务了.
在使用IOC容器的时候,我们注意到除了少量粘合代码,绝大多数以正确IoC风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。 Spring本身提供了对声明式载入web应用程序用法的应用程序上下文,并将其存储在ServletContext中的框架实现。具体可以参见以后的文章。
在使用Spring IOC容器的时候我们还需要区别两个概念:
Beanfactory 和Factory bean,其中BeanFactory指的是IOC容器的编程抽象,比如ApplicationContext, XmlBeanFactory等,这些都是IOC容器的具体表现,需要使用什么样的容器由客户决定但Spring为我们提供了丰富的选择。而 FactoryBean只是一个可以在IOC容器中被管理的一个bean,是对各种处理过程和资源使用的抽象,Factory bean在需要时产生另一个对象,而不返回FactoryBean本省,我们可以把它看成是一个抽象工厂,对它的调用返回的是工厂生产的产品。所有的 Factory bean都实现特殊的org.springframework.beans.factory.FactoryBean接口,当使用容器中factory bean的时候,该容器不会返回factory bean本身,而是返回其生成的对象。Spring包括了大部分的通用资源和服务访问抽象的Factory bean的实现,其中包括:
对JNDI查询的处理,对代理对象的处理,对事务性代理的处理,对RMI代理的处理等,这些我们都可以看成是具体的工厂,看成是SPRING为我们建立好的工厂。也就是说Spring通过使用抽象工厂模式为我们准备了一系列工厂来生产一些特定的对象,免除我们手工重复的工作,我们要使用时只需要在IOC容器里配置好就能很方便的使用了。
现在我们来看看在Spring的事件机制,Spring中有3个标准事件,ContextRefreshEvent, ContextCloseEvent,RequestHandledEvent他们通过ApplicationEvent接口,同样的如果需要自定义时间也只需要实现ApplicationEvent接口,参照ContextCloseEvent的实现可以定制自己的事件实现:
public class ContextClosedEvent extends ApplicationEvent { public ContextClosedEvent(ApplicationContext source) { super(source); } public ApplicationContext getApplicationContext() { return (ApplicationContext) getSource(); } }
可以通过显现ApplicationEventPublishAware接口,将事件发布器耦合到ApplicationContext这样可以使用 ApplicationContext框架来传递和消费消息,然后在ApplicationContext中配置好bean就可以了,在消费消息的过程中,接受者通过实现ApplicationListener接收消息。
比如可以直接使用Spring的ScheduleTimerTask和TimerFactoryBean作为定时器定时产生消息,具体可以参见《Spring框架高级编程》。
TimerFactoryBean是一个工厂bean,对其中的ScheduleTimerTask进行处理后输出,参考ScheduleTimerTask的实现发现它最后调用的是jre的TimerTask:
public void setRunnable(Runnable timerTask) { this.timerTask = new DelegatingTimerTask(timerTask); }
在书中给出了一个定时发送消息的例子,当然可以可以通过定时器作其他的动作,有两种方法:
1.定义MethodInvokingTimerTaskFactoryBean定义要执行的特定bean的特定方法,对需要做什么进行封装定义;
2.定义TimerTask类,通过extends TimerTask来得到,同时对需要做什么进行自定义
然后需要定义具体的定时器参数,通过配置ScheduledTimerTask中的参数和timerTask来完成,以下是它需要定义的具体属性,timerTask是在前面已经定义好的bean
private TimerTask timerTask; private long delay = 0; private long period = 0; private boolean fixedRate = false;
最后,需要在ApplicationContext中注册,需要把ScheduledTimerTask配置到FactoryBean - TimerFactoryBean,这样就由IOC容器来管理定时器了。参照
TimerFactoryBean的属性,可以定制一组定时器。
public class TimerFactoryBean implements FactoryBean, InitializingBean, DisposableBean { protected final Log logger = LogFactory.getLog(getClass()); private ScheduledTimerTask[] scheduledTimerTasks; private boolean daemon = false; private Timer timer; ........... }
如果要发送时间我们只需要在定义好的ScheduledTimerTasks中publish定义好的事件就可以了。具体可以参考书中例子的实现,这里只是结合FactoryBean的原理做一些解释。如果结合事件和定时器机制,我们可以很方便的实现heartbeat(看门狗),书中给出了这个例子,这个例子实际上结合了Spring事件和定时机制的使用两个方面的知识 - 当然了还有IOC容器的知识(任何Spring应用我想都逃不掉IOC的魔爪:)
评论
34 楼
bennyparlo
2007-07-01
最后来总结下吧,对于普通ioc容器启动到被客户端使用的步骤大致如下:
定义->定位->装载->注册->创建->注入
定义:类似application.xml资源文件,所有的bean定义信息都在这个文件中描述.且依赖于dtd/xsd的规则.
定位:对于资源文件信息的封装需要依赖Resource对象.当然这里的Resource仅仅存放了资源文件的简单信息,比如文件(名)和路径等等.所以这就需要ResourceLoader来用于对资源的辅助加载.其中以DefaultResourceLodaer为例,如果通过ClassPathResource方式来加载,就需要指定ClassLoader,这也就是为什么以classpath方式加载资源文件,通常将把资源文件放置于source path下而以file system方式加载则以工程相对路径.比如/WEB-INF/application.xml.这里值得说明的是ApplicationContext实现了ResourceLoader接口,因此他本身就是个资源解析器,通过构造方法就能直接加载资源文件.此外spring自带了常用的资源加载绑定类,当然也可以自己来实现这个Resource接口,比如通过网络获取2进制数据对象反序列化的方式.
装载:BeanDefinitonReader的loadBeanDefine方法用于对Resource的解析处理,另外还对资源文件定义的合法性进行校验.同大多资源文件解析相同,通过w3c开源框架对所有element做迭代处理.但是这里的DeanDefinitonReader只是对BeanDefinitonParserDelegate的套壳处理.也就是说真正对资源文件定义做每个标签和属性做读取解析的最终类,就是BeanDefinitonParserDelegate.因此从命名不难发现,对定义进行解析后最终得到的数据模型,就是BeanDefiniton对象.每个BeanDefiniton对象封装了对应bean定义的所有信息.这个有些类似于o-r(x) mapping实现思想.需要重点说明的是,这里仅仅作为bean信息个定义封装,而并没有产生任何与bean实例以及相关属性实例.
注册:这个过程比较简单,以DefaultListableBeanFactory为例,或常用的AbstractXmlApplicationContext及其实现的子类,都无非将装载时获取的所有BeanDefinition对象存放到beanDefinitionMap对象中,这个beanDefinitionMap是个标准的Map对象.当然作者处于对BeanDefinition的顺序检索处理,还特地定义了beanDefinitionNames,这个beanDefinitionNames是个List对象.这2个对象就是存放bean(定义)的场所,或许也是真正意义的容器概念.因为今后所有客户端对容器的请求发起,作为容器方都将从该Map中取得相关bean信息.而不再持久资源文件中做任何读取操作.
创建:当客户端对已经初始化的容器发起请求时,容器首先会以bean name为key从beanDefinitionMap中获取BeanDefiniton定义对象.接着对singleton bean进行预实例化的处理.随后会从BeanDefiniton定义对象中对当前定义的bean对象做实例化操作,这里调用了AbstractAutowireCapableBeanFactory的createBean方法,注意目前到这个层面仅仅对bean本身的实例化(以prototype为例也就相当于进行了new操作).这其中首先会对实例化方式进行判断,比如工厂模式或者原型模式(包括带参数构造),如遇到带参数的构造定义,则需要进步解析构造参数,这个解析过程类似与当前bean的属性注入的过程.在下文中将做详细介绍.最后当前给请求的bean通过反射来实例化对象.
注入:这个过程是所有容器工作步骤中最为复杂的部分.首先这里有个对依赖的检查过程.虽然通过调用getBean的方式处理,但仅仅为了做依赖检查而不返回被依赖的对象.这样的目的在于将校验提前进行,而不要等到对于开销较大的依赖注入时.接下来createBean开始调用populateBean方法.首先进行自动装配模式的处理,也就是说对BeanDefiniton中定义自动装配模式的属性进行调整.比如定义了AUTOWIRE_BY_NAME的属性会相应找到匹配该命名的bean.并且将该被检索到的bean实例值给property,当然这里也肯定通过getBean方法来获取这个需要自动装配的bean实例,并且在BeanWrapper中包装.其实下文要讲到的注入过程也在BeanWrapper中完成.说道这里,可以明显看出,1个普通bean(既1个bean,包含1个或多个简单的属性)的属性注入过程,也需要依赖BeanWrapper来完成.或者说当得到1个bean的所有property定义时,容器首先将根据property的不同类型,如prop,map,list,set当然还有ref(在BeanDefinitionValueResolver的resolveValueIfNecessary方法完成),进行不同方式的对象实例化,当得知了属性的类型后就可以通过该类中相关resolve方法来完成该属性需要注入的对象的实例化操作,比如1个prop类型的属性可能就通过简单的new操作外加setProperty操作来完成了.而之所以可以通过类型检查来进行判断,是因为在BeanDefiniton定义中已经对这些property value做了封装处理,这可能也是最好用于区分property类型的最佳方式.具体的封装过程可以参考BeanDefinitonParserDelegate的parsePropertyValue方法.这里最最最值得提及的是,在该类resolveReference方法中,容器依旧通过getBean来获取需要被注入的bean对象,因此这里还需要进行1次依赖检查.如果该被注入的bean对象又依赖其他bean对象,那将重复上述过程.因此说创建与注入是个递归的过程.而最后完成对bean注入,也就是通常我们所谓的setter方法的,又需要回到BeanWrapper类,这里的setPropertyValue方法比较复杂,但我们可以暂且不必关心他的实现,因为写到这里我们已经知道了属性需要被注入的对象,在setPropertyValue只是将该对象进行赋值的操作.
定义->定位->装载->注册->创建->注入
定义:类似application.xml资源文件,所有的bean定义信息都在这个文件中描述.且依赖于dtd/xsd的规则.
定位:对于资源文件信息的封装需要依赖Resource对象.当然这里的Resource仅仅存放了资源文件的简单信息,比如文件(名)和路径等等.所以这就需要ResourceLoader来用于对资源的辅助加载.其中以DefaultResourceLodaer为例,如果通过ClassPathResource方式来加载,就需要指定ClassLoader,这也就是为什么以classpath方式加载资源文件,通常将把资源文件放置于source path下而以file system方式加载则以工程相对路径.比如/WEB-INF/application.xml.这里值得说明的是ApplicationContext实现了ResourceLoader接口,因此他本身就是个资源解析器,通过构造方法就能直接加载资源文件.此外spring自带了常用的资源加载绑定类,当然也可以自己来实现这个Resource接口,比如通过网络获取2进制数据对象反序列化的方式.
装载:BeanDefinitonReader的loadBeanDefine方法用于对Resource的解析处理,另外还对资源文件定义的合法性进行校验.同大多资源文件解析相同,通过w3c开源框架对所有element做迭代处理.但是这里的DeanDefinitonReader只是对BeanDefinitonParserDelegate的套壳处理.也就是说真正对资源文件定义做每个标签和属性做读取解析的最终类,就是BeanDefinitonParserDelegate.因此从命名不难发现,对定义进行解析后最终得到的数据模型,就是BeanDefiniton对象.每个BeanDefiniton对象封装了对应bean定义的所有信息.这个有些类似于o-r(x) mapping实现思想.需要重点说明的是,这里仅仅作为bean信息个定义封装,而并没有产生任何与bean实例以及相关属性实例.
注册:这个过程比较简单,以DefaultListableBeanFactory为例,或常用的AbstractXmlApplicationContext及其实现的子类,都无非将装载时获取的所有BeanDefinition对象存放到beanDefinitionMap对象中,这个beanDefinitionMap是个标准的Map对象.当然作者处于对BeanDefinition的顺序检索处理,还特地定义了beanDefinitionNames,这个beanDefinitionNames是个List对象.这2个对象就是存放bean(定义)的场所,或许也是真正意义的容器概念.因为今后所有客户端对容器的请求发起,作为容器方都将从该Map中取得相关bean信息.而不再持久资源文件中做任何读取操作.
创建:当客户端对已经初始化的容器发起请求时,容器首先会以bean name为key从beanDefinitionMap中获取BeanDefiniton定义对象.接着对singleton bean进行预实例化的处理.随后会从BeanDefiniton定义对象中对当前定义的bean对象做实例化操作,这里调用了AbstractAutowireCapableBeanFactory的createBean方法,注意目前到这个层面仅仅对bean本身的实例化(以prototype为例也就相当于进行了new操作).这其中首先会对实例化方式进行判断,比如工厂模式或者原型模式(包括带参数构造),如遇到带参数的构造定义,则需要进步解析构造参数,这个解析过程类似与当前bean的属性注入的过程.在下文中将做详细介绍.最后当前给请求的bean通过反射来实例化对象.
注入:这个过程是所有容器工作步骤中最为复杂的部分.首先这里有个对依赖的检查过程.虽然通过调用getBean的方式处理,但仅仅为了做依赖检查而不返回被依赖的对象.这样的目的在于将校验提前进行,而不要等到对于开销较大的依赖注入时.接下来createBean开始调用populateBean方法.首先进行自动装配模式的处理,也就是说对BeanDefiniton中定义自动装配模式的属性进行调整.比如定义了AUTOWIRE_BY_NAME的属性会相应找到匹配该命名的bean.并且将该被检索到的bean实例值给property,当然这里也肯定通过getBean方法来获取这个需要自动装配的bean实例,并且在BeanWrapper中包装.其实下文要讲到的注入过程也在BeanWrapper中完成.说道这里,可以明显看出,1个普通bean(既1个bean,包含1个或多个简单的属性)的属性注入过程,也需要依赖BeanWrapper来完成.或者说当得到1个bean的所有property定义时,容器首先将根据property的不同类型,如prop,map,list,set当然还有ref(在BeanDefinitionValueResolver的resolveValueIfNecessary方法完成),进行不同方式的对象实例化,当得知了属性的类型后就可以通过该类中相关resolve方法来完成该属性需要注入的对象的实例化操作,比如1个prop类型的属性可能就通过简单的new操作外加setProperty操作来完成了.而之所以可以通过类型检查来进行判断,是因为在BeanDefiniton定义中已经对这些property value做了封装处理,这可能也是最好用于区分property类型的最佳方式.具体的封装过程可以参考BeanDefinitonParserDelegate的parsePropertyValue方法.这里最最最值得提及的是,在该类resolveReference方法中,容器依旧通过getBean来获取需要被注入的bean对象,因此这里还需要进行1次依赖检查.如果该被注入的bean对象又依赖其他bean对象,那将重复上述过程.因此说创建与注入是个递归的过程.而最后完成对bean注入,也就是通常我们所谓的setter方法的,又需要回到BeanWrapper类,这里的setPropertyValue方法比较复杂,但我们可以暂且不必关心他的实现,因为写到这里我们已经知道了属性需要被注入的对象,在setPropertyValue只是将该对象进行赋值的操作.
33 楼
jiwenke
2007-06-30
回到AbstractAutowireCapableBeanFactory中的populate方法,这里是处理bean的依赖注入的地方:
这里有一个迭代的解析和bean依赖的创建,注入:
在BeanDefinitionValueResolver中是这样实现这个resolve的:
这里可以看到对各种依赖类型的resolve,我们看看怎样解析reference bean的,非常清楚的向IOC容器去请求 - 也许会触发下一层依赖的bean的创建和依赖注入过程:
假设我们经过穷推,已经到最后一层的bean的依赖创建和注入,这个具体的注入过程,也就是依赖注入过程要依靠BeanWrapperImp的实现我们回到applyPropertyValues中来,这里是已经迭代对依赖进行完解析的地方,也就是需要对依赖进行注入的地方 - 注意这个token是在wrapper中已经对属性做过处理了:
这里比较重要的是propValue的取得,我们看看getPropertyValue的实现:
这就是整个依赖注入的处理过程,在这个过程中起主要作用的是WrapperImp ,这个Wrapper不是一个简单的对bean对象的封装,因为它需要处理在beanDefinition中的信息来迭代的处理依赖注入。
从上面可以看到两个明显的迭代过程,一个是迭代的在上下文体系中查找需要的bean和创建没有被创建的bean - 根据依赖关系为线索,另一个迭代实在依赖注入的时候,如果依赖没有创建,因为是一个向容器取得bean的过程 - 其中的IOC工厂的getbean方法被迭代的调用,中间又迭代的对需要创建的bean进行了创建和依赖注入,这样根据依赖关系,一层一层的创建和注入直至顶层被要求的bean创建和依赖注入完成 - 这样最后得到一个依赖创建和注入完成的最顶层bean被用来交给客户程序使用。
protected void populateBean(String beanName, RootBeanDefinition mergedBeanDefinition, BeanWrapper bw) throws BeansException { //首先取得我们在loadBeanDefinition中取得的依赖定义propertyValues PropertyValues pvs = mergedBeanDefinition.getPropertyValues(); ...... checkDependencies(beanName, mergedBeanDefinition, filteredPds, pvs); //主要地依赖注入处理在这里 applyPropertyValues(beanName, mergedBeanDefinition, bw, pvs); } private void applyPropertyValues( String beanName, RootBeanDefinition mergedBeanDefinition, BeanWrapper bw, PropertyValues pvs) throws BeansException { if (pvs == null) { return; } BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mergedBeanDefinition); // Create a deep copy, resolving any references for values. // 这里把那些相关的有依赖关系的property内容copy过来 MutablePropertyValues deepCopy = new MutablePropertyValues(); PropertyValue[] pvArray = pvs.getPropertyValues(); for (int i = 0; i < pvArray.length; i++) { PropertyValue pv = pvArray[i]; //这个队property的resolve过程包含了一个对依赖bean的迭代解析和创建 Object resolvedValue = valueResolver.resolveValueIfNecessary("bean property '" + pv.getName() + "'", pv.getValue()); deepCopy.addPropertyValue(pvArray[i].getName(), resolvedValue); } // 这里把copy过来的propertyValue置入到BeanWrapper中去,这个set其实并不简单,它通过wrapper完成了实际的依赖注入 try { // Synchronize if custom editors are registered. // Necessary because PropertyEditors are not thread-safe. if (!getCustomEditors().isEmpty()) { synchronized (this) { bw.setPropertyValues(deepCopy); } } else { bw.setPropertyValues(deepCopy); } } ......... }
这里有一个迭代的解析和bean依赖的创建,注入:
Object resolvedValue = valueResolver.resolveValueIfNecessary("bean property '" + pv.getName() + "'", pv.getValue());
在BeanDefinitionValueResolver中是这样实现这个resolve的:
public Object resolveValueIfNecessary(String argName, Object value) throws BeansException { if (value instanceof BeanDefinitionHolder) { // Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases. BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value; return resolveInnerBeanDefinition(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition()); } else if (value instanceof BeanDefinition) { // Resolve plain BeanDefinition, without contained name: use dummy name. BeanDefinition bd = (BeanDefinition) value; return resolveInnerBeanDefinition(argName, "(inner bean)", bd); } else if (value instanceof RuntimeBeanNameReference) { String ref = ((RuntimeBeanNameReference) value).getBeanName(); if (!this.beanFactory.containsBean(ref)) { throw new BeanDefinitionStoreException( "Invalid bean name '" + ref + "' in bean reference for " + argName); } return ref; } else if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; return resolveReference(argName, ref); } else if (value instanceof ManagedList) { // May need to resolve contained runtime references. return resolveManagedList(argName, (List) value); } else if (value instanceof ManagedSet) { // May need to resolve contained runtime references. return resolveManagedSet(argName, (Set) value); } else if (value instanceof ManagedMap) { // May need to resolve contained runtime references. return resolveManagedMap(argName, (Map) value); } else if (value instanceof ManagedProperties) { Properties copy = new Properties(); copy.putAll((Properties) value); return copy; } else if (value instanceof TypedStringValue) { // Convert value to target type here. TypedStringValue typedStringValue = (TypedStringValue) value; try { Class resolvedTargetType = resolveTargetType(typedStringValue); return this.beanFactory.doTypeConversionIfNecessary( this.typeConverter, typedStringValue.getValue(), resolvedTargetType, null); } catch (Throwable ex) { // Improve the message by showing the context. throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Error converting typed String value for " + argName, ex); } } else { // No need to resolve value... return value; } }
这里可以看到对各种依赖类型的resolve,我们看看怎样解析reference bean的,非常清楚的向IOC容器去请求 - 也许会触发下一层依赖的bean的创建和依赖注入过程:
private Object resolveReference(String argName, RuntimeBeanReference ref) throws BeansException { .......... try { //向父工厂请求bean return this.beanFactory.getParentBeanFactory().getBean(ref.getBeanName()); } else { //向自己所在的工厂请求bean Object bean = this.beanFactory.getBean(ref.getBeanName()); if (this.beanDefinition.isSingleton()) { this.beanFactory.registerDependentBean(ref.getBeanName(), this.beanName); } return bean; } } ......... }
假设我们经过穷推,已经到最后一层的bean的依赖创建和注入,这个具体的注入过程,也就是依赖注入过程要依靠BeanWrapperImp的实现我们回到applyPropertyValues中来,这里是已经迭代对依赖进行完解析的地方,也就是需要对依赖进行注入的地方 - 注意这个token是在wrapper中已经对属性做过处理了:
private void setPropertyValue(PropertyTokenHolder tokens, Object newValue) throws BeansException { String propertyName = tokens.canonicalName; if (tokens.keys != null) { // Apply indexes and map keys: fetch value for all keys but the last one. PropertyTokenHolder getterTokens = new PropertyTokenHolder(); getterTokens.canonicalName = tokens.canonicalName; getterTokens.actualName = tokens.actualName; getterTokens.keys = new String[tokens.keys.length - 1]; System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1); //这里取得需要的propertyValue, 这个getPropertyValue同样不简单,这个getProperty实际上已经取出了bean对象中的属性引用 //所以下面可以直接把依赖对象注入过去 Object propValue = null; try { propValue = getPropertyValue(getterTokens); } ........ // 如果根据token取不到propertyValue,直接抛出异常 String key = tokens.keys[tokens.keys.length - 1]; if (propValue == null) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + "in indexed property path '" + propertyName + "': returned null"); } //这里处理属性是List的注入 else if (propValue.getClass().isArray()) { ..... Array.set(propValue, Integer.parseInt(key), convertedValue); ....... } //这里处理对List的注入 else if (propValue instanceof List) { ........ List list = (List) propValue; ........ if (index < list.size()) { list.set(index, convertedValue); } else if (index >= list.size()) { for (int i = list.size(); i < index; i++) { try { list.add(null); } } list.add(convertedValue); ....... } //这里处理对Map的注入 else if (propValue instanceof Map) { ...... Map map = (Map) propValue; ...... map.put(convertedMapKey, convertedMapValue); } ........ } //这里是通过对一般属性进行注入的地方 else { PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName); if (pd == null || pd.getWriteMethod() == null) { PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass()); throw new NotWritablePropertyException( getRootClass(), this.nestedPath + propertyName, matches.buildErrorMessage(), matches.getPossibleMatches()); } //得到需要的set/get方法 Method readMethod = pd.getReadMethod(); Method writeMethod = pd.getWriteMethod(); Object oldValue = null; ........ try { Object convertedValue = this.typeConverterDelegate.convertIfNecessary(oldValue, newValue, pd); //千辛万苦,这里是通过set方法对bean对象的依赖属性进行注入 if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } writeMethod.invoke(this.object, new Object[] {convertedValue}); ........... }
这里比较重要的是propValue的取得,我们看看getPropertyValue的实现:
private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException { .... //这里取得对property的读取方法,然后取得在bean对象中的属性引用 Method readMethod = pd.getReadMethod(); try { if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(this.object, (Object[]) null); if (tokens.keys != null) { ...... }//这里处理Array属性 else if (value.getClass().isArray()) { value = Array.get(value, Integer.parseInt(key)); }//这里处理List属性 else if (value instanceof List) { List list = (List) value; value = list.get(Integer.parseInt(key)); }//这里处理Set属性 else if (value instanceof Set) { // Apply index to Iterator in case of a Set. Set set = (Set) value; int index = Integer.parseInt(key); } ...... }//这里处理Map属性 else if (value instanceof Map) { Map map = (Map) value; Class mapKeyType = null; if (JdkVersion.isAtLeastJava15()) { mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType( pd.getReadMethod(), tokens.keys.length); } ..... Object convertedMapKey = this.typeConverterDelegate.convertIfNecessary( null, null, key, mapKeyType); // Pass full property name and old value in here, since we want full // conversion ability for map values. value = map.get(convertedMapKey); } ........ return value; }
这就是整个依赖注入的处理过程,在这个过程中起主要作用的是WrapperImp ,这个Wrapper不是一个简单的对bean对象的封装,因为它需要处理在beanDefinition中的信息来迭代的处理依赖注入。
从上面可以看到两个明显的迭代过程,一个是迭代的在上下文体系中查找需要的bean和创建没有被创建的bean - 根据依赖关系为线索,另一个迭代实在依赖注入的时候,如果依赖没有创建,因为是一个向容器取得bean的过程 - 其中的IOC工厂的getbean方法被迭代的调用,中间又迭代的对需要创建的bean进行了创建和依赖注入,这样根据依赖关系,一层一层的创建和注入直至顶层被要求的bean创建和依赖注入完成 - 这样最后得到一个依赖创建和注入完成的最顶层bean被用来交给客户程序使用。
32 楼
jiwenke
2007-06-30
我们看看是用的wrapper类是怎样被创建的,这个对象被创建的时候已经为我们的bean创建了JAVA对象:
我们注意到在这里定义的实例化的策略是
一般而言可以直接实例化也可以通过cglib来完成bean对象的重新实例化,在
这里我们看到对bean的JAVA对象的创建过程,如果没有什么依赖关系的话,那主要的bean创建过程已经完成了,但是如果存在依赖关系的话,这些依赖关系还要进行注入,
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args) throws BeansException { BeanWrapper instanceWrapper = null; //这里使用BeanWrapper的不同创建方法 if (mergedBeanDefinition.getFactoryMethodName() != null) { instanceWrapper = instantiateUsingFactoryMethod(beanName, mergedBeanDefinition, args); } else if (mergedBeanDefinition.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mergedBeanDefinition.hasConstructorArgumentValues() ) { instanceWrapper = autowireConstructor(beanName, mergedBeanDefinition); } else { // No special handling: simply use no-arg constructor. // 这是最正常的创建,使用Spring默认的BeanWrapper实现BeanWrapperImp instanceWrapper = instantiateBean(beanName, mergedBeanDefinition); } return instanceWrapper; } protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mergedBeanDefinition) throws BeansException { //这里是创建bean对象的地方,同时把这个bean对象放到BeanWrapper中去 Object beanInstance = getInstantiationStrategy().instantiate(mergedBeanDefinition, beanName, this); BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; }
我们注意到在这里定义的实例化的策略是
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
一般而言可以直接实例化也可以通过cglib来完成bean对象的重新实例化,在
CglibSubclassingInstantiationStrategy中: public Object instantiate( RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) { // Don't override the class with CGLIB if no overrides. // 这里是重新实例化bean对象的地方,返回后放到BeanWrapper对象当中去 if (beanDefinition.getMethodOverrides().isEmpty()) { return BeanUtils.instantiateClass(beanDefinition.getBeanClass()); } else { // Must generate CGLIB subclass. return instantiateWithMethodInjection(beanDefinition, beanName, owner); } }
这里我们看到对bean的JAVA对象的创建过程,如果没有什么依赖关系的话,那主要的bean创建过程已经完成了,但是如果存在依赖关系的话,这些依赖关系还要进行注入,
31 楼
jiwenke
2007-06-30
有了上面的准备,我们可以看看依赖关系是怎样被注入的,我们知道IOC容器在loadBeanDefinition的时候只是完成了bean定义的解析和向IOC容器的注册,这个时候并没有对依赖的实现注入,具体的注入过程是在客户程序向IOC容器要求bean的时候完成的,具体的过程我们在代码中可以看到 - 在AbstractFactoryBean中:
具体的bean创建过程和依赖关系的注入在createBean中,这个方法在
AbstractAutowireCapableBeanFactory中给出了实现:
public Object getBean(String name, Class requiredType, final Object[] args) throws BeansException { final String beanName = transformedBeanName(name); Object bean = null; // Eagerly check singleton cache for manually registered singletons. // 这里先从缓存中去取,处理那些已经被创建过的单件模式的bean,对这种bean的请求不需要重复的去创建 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null) { if (isSingletonCurrentlyInCreation(beanName)) { ....... } else { ....... } if (containsBeanDefinition(beanName)) { RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false); bean = getObjectForBeanInstance(sharedInstance, name, mergedBeanDefinition); } else { bean = getObjectForBeanInstance(sharedInstance, name, null); } } else { // Fail if we're already creating this singleton instance: // We're assumably within a circular reference. if (isSingletonCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. //这里检查是否能在当前的工厂中取到我们需要的bean,如果在当前的工厂中取不到,则到父工厂取,如果一直取不到 //那就顺着工厂链一直向上查找 BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); // 这里调用父工厂的getbean取需要的bean // 这里有一个迭代,在父工厂中也会重复这么一个getbean的过程。 if (parentBeanFactory instanceof AbstractBeanFactory) { // Delegation to parent with args only possible for AbstractBeanFactory. return ((AbstractBeanFactory) parentBeanFactory).getBean(nameToLookup, requiredType, args); } else if (args == null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { throw new NoSuchBeanDefinitionException(beanName, "Cannot delegate to parent BeanFactory because it does not supported passed-in arguments"); } } //把这个已经被要求过的bean记录下来,因为第一次要求bean的时候往往就是依赖被容器对bean进行注入的时候。 this.alreadyCreated.add(beanName); final RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false); checkMergedBeanDefinition(mergedBeanDefinition, beanName, args); // Create bean instance. //这里是根据beandefinition来创建bean和完成依赖注入的地方。 if (mergedBeanDefinition.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory() { public Object getObject() throws BeansException { try { //注意这个createBean,是创建bean同时完成依赖注入的地方。 return createBean(beanName, mergedBeanDefinition, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, mergedBeanDefinition); } //这里是处理prototype类型的bean请求的地方 else if (mergedBeanDefinition.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); //每次请求,直接通过createBean来创建 prototypeInstance = createBean(beanName, mergedBeanDefinition, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, mergedBeanDefinition); } ........ return bean; }
具体的bean创建过程和依赖关系的注入在createBean中,这个方法在
AbstractAutowireCapableBeanFactory中给出了实现:
protected Object createBean(String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args) throws BeanCreationException { // Guarantee initialization of beans that the current one depends on. // 这里对取得当前bean的所有依赖bean,确定能够取得这些已经被确定的bean,如果没有被创建,那么这个createBean会被这些IOC // getbean时创建这些bean if (mergedBeanDefinition.getDependsOn() != null) { for (int i = 0; i < mergedBeanDefinition.getDependsOn().length; i++) { getBean(mergedBeanDefinition.getDependsOn()[i]); } } ........ // 这里是实例化bean对象的地方,注意这个BeanWrapper类,是对bean操作的主要封装类 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mergedBeanDefinition, args); } Object bean = instanceWrapper.getWrappedInstance(); ...... //这个populate方法,是对已经创建的bean实例进行依赖注入的地方,会使用到在loadBeanDefinition的时候得到的那些propertyValue来对bean进行注入。 if (continueWithPropertyPopulation) { populateBean(beanName, mergedBeanDefinition, instanceWrapper); } //这里完成客户自定义的对bean的一些初始化动作 Object originalBean = bean; bean = initializeBean(beanName, bean, mergedBeanDefinition); // Register bean as disposable, and also as dependent on specified "dependsOn" beans. registerDisposableBeanIfNecessary(beanName, originalBean, mergedBeanDefinition); return bean; } ......... }
30 楼
jiwenke
2007-06-30
我们大致可以看到Spring把loadBean和依赖注入分成两个基本的过程,一个是在启动容器的时候完成,建立起一系列 BeanDefinition,这些定义里面同时都包含了对bean依赖关系的描述,不过这里并没有对bean进行实例化,真正实例化的时候是在客户通过容器使用这些bean的时候 - 也就是getbean的时候。这个时候IOC容器根据需要会建立起一系列bean的实例和完成依赖注入。所以下面我们会详细的分析这个依赖注入的过程。
让我们先简单回顾IOC容器启动时候载入bean定义文件的过程,这个过程是通过BeanDefinitionReader来完成的,其中通过 loadBeanDefinition()来对定义文件进行解析和根据Spring定义的bean规则进行处理 - 事实上和Spring定义的bean规则相关的处理是在BeanDefinitionParserDelegate中完成的,完成这个处理需要得到用户定义的bean定义信息,这是在前面已经通过解析已经完成了的,这个处理过程的结果就是得到了一系列的BeanDefinition,这里不但包含了对 bean定义信息解析后的表示,同时还把和这个bean相关的依赖信息也保存了下来。那以后这些信息怎样被取得和被使用呢?在完成了这一系列的解析和处理之后,接着需要对对BeanFactoryRegistry的接口做一个回调,把这些BeanDefinition都放到一个HashMap - beanDefinitionMap里面,以后对这些信息的操作通过这个HashMap来完成。这样就完成了IOC启动过程中最重要的部分 - 依赖关系的解析,处理和建立,下面就是使用IOC容器的时候完成的依赖注入的行为。在AbstractBeanDefinition中我们看到有这些和依赖关系处理紧密相关的属性:
很显然这些值都是在BeanDefinitionParserDelegate中设置的:
在辅助类BeanDefinitionReaderUtils中对bean对象做了原始的创建:
下面我们分析parsePropertyElements(ele, bd)看看那些属性依赖关系是怎样处理的:
我们看看具体的对各种属性定义的解析,在parsePropertyValue函数中:
比如我们举个例子看看对List属性的解析,也是生成一个对应的List对象,里面记录了相关的定义信息,
这样就把相应的属性对象都解析完,而且都放到了beandefinition中去,这个beandefinition是以后容器进行依赖注入和依赖管理的最主要的数据结构。
让我们先简单回顾IOC容器启动时候载入bean定义文件的过程,这个过程是通过BeanDefinitionReader来完成的,其中通过 loadBeanDefinition()来对定义文件进行解析和根据Spring定义的bean规则进行处理 - 事实上和Spring定义的bean规则相关的处理是在BeanDefinitionParserDelegate中完成的,完成这个处理需要得到用户定义的bean定义信息,这是在前面已经通过解析已经完成了的,这个处理过程的结果就是得到了一系列的BeanDefinition,这里不但包含了对 bean定义信息解析后的表示,同时还把和这个bean相关的依赖信息也保存了下来。那以后这些信息怎样被取得和被使用呢?在完成了这一系列的解析和处理之后,接着需要对对BeanFactoryRegistry的接口做一个回调,把这些BeanDefinition都放到一个HashMap - beanDefinitionMap里面,以后对这些信息的操作通过这个HashMap来完成。这样就完成了IOC启动过程中最重要的部分 - 依赖关系的解析,处理和建立,下面就是使用IOC容器的时候完成的依赖注入的行为。在AbstractBeanDefinition中我们看到有这些和依赖关系处理紧密相关的属性:
public abstract class AbstractBeanDefinition extends AttributeAccessorSupport implements BeanDefinition { ....... private Object beanClass; ...... //这个属性持有的这个bean的依赖属性,比如各种property的定义信息 private MutablePropertyValues propertyValues; ...... }
很显然这些值都是在BeanDefinitionParserDelegate中设置的:
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { //这里得到定义的类的名字 - 通过属性Class String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE); } ......... try { this.parseState.push(new BeanEntry(beanName)); //这里是生成BeanDefinition的地方,也是生成根据定义的class属性来生成bean JAVA对象的地方 //以前我们已经分析过了,这里生成一个BeanDefinition , 它里面已经持有了生成的bean Java对象。 AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition( parent, className, getReaderContext().getReader().getBeanClassLoader()); //下面是一系列对beandefinition的属性设置,依据是用户定义的bean定义信息,类似于用户定义的bean定义信息的读入 if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { // Spring 2.0 "scope" attribute bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Specify either 'scope' or 'singleton', not both", ele); } } else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { // Spring 1.x "singleton" attribute bd.setSingleton(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE))); } .......... parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd); //注意这里是对依赖关系,也就是bean对象属性的处理! parsePropertyElements(ele, bd); bd.setResourceDescription(getReaderContext().getResource().getDescription()); bd.setSource(extractSource(ele)); return bd; ................ }
在辅助类BeanDefinitionReaderUtils中对bean对象做了原始的创建:
public static AbstractBeanDefinition createBeanDefinition( String parent, String className, ClassLoader classLoader) throws ClassNotFoundException { AbstractBeanDefinition bd = null; if (parent != null) { bd = new ChildBeanDefinition(parent); } else { bd = new RootBeanDefinition(); } //这里创建了bean的JAVA对象,同时在beandefinition中持有 if (className != null) { if (classLoader != null) { bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { bd.setBeanClassName(className); } } return bd; }
下面我们分析parsePropertyElements(ele, bd)看看那些属性依赖关系是怎样处理的:
public void parsePropertyElements(Element beanEle, BeanDefinition bd) { //这里取得所有定义的属性定义 NodeList nl = beanEle.getChildNodes(); //很显然,需要一个一个的处理 for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) { parsePropertyElement((Element) node, bd); } } public void parsePropertyElement(Element ele, BeanDefinition bd) { //这里得到属性定义的名字 String propertyName = ele.getAttribute(NAME_ATTRIBUTE); if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try { //判断是不是已经有重名的属性定义 if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } //这里根据属性的名字对属性的定义进行具体的解析,结果放在PropertyValue对象里面 //这里包含了对各种属性类型的解析,比如List,Map,Set,当然还有reference bean等等, //这个propertyValue是连接和表示各个bean之间依赖关系的桥梁 Object val = parsePropertyValue(ele, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); //最后把这个解析的结果,也就是生成的Property对象放到beanDefinition里面去 bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } }
我们看看具体的对各种属性定义的解析,在parsePropertyValue函数中:
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = (propertyName != null) ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"; // Should only have one child element: ref, value, list, etc. //这里根据DOM取得需要分析的属性定义 NodeList nl = ele.getChildNodes(); Element subElement = null; for (int i = 0; i < nl.getLength(); i++) { if (nl.item(i) instanceof Element) { Element candidateEle = (Element) nl.item(i); if (DESCRIPTION_ELEMENT.equals(candidateEle.getTagName())) { // Keep going: we don't use this value for now. } else { // Child element is what we're looking for. if (subElement != null && !META_ELEMENT.equals(subElement.getTagName())) { error(elementName + " must not contain more than one sub-element", ele); } subElement = candidateEle; } } } boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute)) && subElement != null) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } if (hasRefAttribute) { //这里是对ref属性的解析,称称一个RuntimeBeanReference对象, //这个RuntimeBeanReference很简单,只是保持beanName,toParent,source三个属性信息,对记录依赖关系已经足够了 String refName = ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } else if (hasValueAttribute) { return ele.getAttribute(VALUE_ATTRIBUTE); } if (subElement == null) { // Neither child element nor "ref" or "value" attribute found. error(elementName + " must specify a ref or value", ele); } //这里对各种其他属性值进行处理,比如List,Map,Set等等 return parsePropertySubElement(subElement, bd); }
比如我们举个例子看看对List属性的解析,也是生成一个对应的List对象,里面记录了相关的定义信息,
public List parseListElement(Element collectionEle, BeanDefinition bd) { String defaultTypeClassName = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); NodeList nl = collectionEle.getChildNodes(); ManagedList list = new ManagedList(nl.getLength()); list.setSource(extractSource(collectionEle)); list.setMergeEnabled(parseMergeAttribute(collectionEle)); //这里对List的各个子属性进行处理,结果放到这个List,然后返回到beandefinition中以后可以使用。 for (int i = 0; i < nl.getLength(); i++) { if (nl.item(i) instanceof Element) { Element ele = (Element) nl.item(i); list.add(parsePropertySubElement(ele, bd, defaultTypeClassName)); } } return list; }
这样就把相应的属性对象都解析完,而且都放到了beandefinition中去,这个beandefinition是以后容器进行依赖注入和依赖管理的最主要的数据结构。
29 楼
jiwenke
2007-06-29
经过和bennyparlo同学的讨论和MSN,大致把IOC容器完成依赖注入的过程梳理了一遍。让我代笔结合源代码在下面给出分析,望大家多提意见!- 很多细节没法顾及到,慢慢来吧!先发一个广告 :)
我们大致可以看到Spring把loadBean和依赖注入分成两个基本的过程,一个是在启动容器的时候完成,建立起一系列BeanDefinition,这些定义里面同时都包含了对bean依赖关系的描述,不过这里并没有对bean进行实例化,真正实例化的时候是在客户通过容器使用这些bean的时候 - 也就是getbean的时候。这个时候IOC容器根据需要会建立起一系列bean的实例和完成依赖注入。所以下面我们会详细的分析这个依赖注入的过程。
我们大致可以看到Spring把loadBean和依赖注入分成两个基本的过程,一个是在启动容器的时候完成,建立起一系列BeanDefinition,这些定义里面同时都包含了对bean依赖关系的描述,不过这里并没有对bean进行实例化,真正实例化的时候是在客户通过容器使用这些bean的时候 - 也就是getbean的时候。这个时候IOC容器根据需要会建立起一系列bean的实例和完成依赖注入。所以下面我们会详细的分析这个依赖注入的过程。
28 楼
jiwenke
2007-06-28
bennyparlo 写道
也就是说,当调用loadBeanDefinition后,或者说调用了BeanDefinitionRegistry.registerBeanDefinition()以后,所有的resource定义被封装成BeanDefiniton并且存放到beanDefinitionMap后,被客户端使用的时候,去做了这些个bean注入的操作.按我的理解,spring的ioc容器在启动的时候,加载资源定义,而bean依赖的被注入,还要等到具体被客户端使用的时候.
完全同意!所以loadBeanDefinition和createBean是两个不同的过程,一个由容器启动的时候触发,一个由客户程序请求服务的时候触发!那大致可以看到启动的时候只是定位 - 读入 - 解析 - 注册,而最后的依赖注入还是要等到客户第一次请求bean的时候触发,如果有的bean不被请求,就不会被依赖注入。
27 楼
jiwenke
2007-06-28
bennyparlo 写道
这个createBean被getBean方法调用了
public Object getBean(String name, Class requiredType, final Object[] args) throws BeansException中调用了这个方法
public Object getBean(String name, Class requiredType, final Object[] args) throws BeansException中调用了这个方法
看到了,谢谢!!! 在AbstractFactoryBean中:
在AbstractFactoryBean中的getBean调用createBean
public Object getBean(String name, Class requiredType, final Object[] args) throws BeansException { final String beanName = transformedBeanName(name); Object bean = null; ........ // 这里检查是不是在父工厂里这个bean BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { //这里调用父工厂的getBean return ((AbstractBeanFactory) parentBeanFactory).getBean(nameToLookup, requiredType, args); } else if (args == null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { throw new NoSuchBeanDefinitionException(beanName, "Cannot delegate to parent BeanFactory because it does not supported passed-in arguments"); } } this.alreadyCreated.add(beanName); final RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false); checkMergedBeanDefinition(mergedBeanDefinition, beanName, args); //这里调用bean的创建过程,也是依赖注入的地方,依赖解析已经完成放在BeanDefinition里面了 if (mergedBeanDefinition.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory() { public Object getObject() throws BeansException { try { return createBean(beanName, mergedBeanDefinition, args); } ......... }
那看来bean的依赖注入是在第一次向容器请求bean的时候完成的,loadBeanDefinition只是把定义的bean和依赖解析了以后存到容器里。这个createBean和getBean要好好看看。
26 楼
bennyparlo
2007-06-28
也就是说,当调用loadBeanDefinition后,或者说调用了BeanDefinitionRegistry.registerBeanDefinition()以后,所有的resource定义被封装成BeanDefiniton并且存放到beanDefinitionMap后,被客户端使用的时候,去做了这些个bean注入的操作.按我的理解,spring的ioc容器在启动的时候,加载资源定义,而bean依赖的被注入,还要等到具体被客户端使用的时候.
25 楼
bennyparlo
2007-06-28
这个createBean被getBean方法调用了
public Object getBean(String name, Class requiredType, final Object[] args) throws BeansException中调用了这个方法
public Object getBean(String name, Class requiredType, final Object[] args) throws BeansException中调用了这个方法
24 楼
jiwenke
2007-06-28
bennyparlo 写道
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory中
/** * Central method of this class: creates a bean instance, * populates the bean instance, applies post-processors, etc. * <p>Differentiates between default bean instantiation, use of a * factory method, and autowiring a constructor. * @see #instantiateBean * @see #instantiateUsingFactoryMethod * @see #autowireConstructor */ protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { // 初始化被依赖的bean实例 if (mbd.getDependsOn() != null) { for (int i = 0; i < mbd.getDependsOn().length; i++) { getBean(mbd.getDependsOn()[i]); } } if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean '" + beanName + "' with merged definition [" + mbd + "]"); } // 判断当前bean的对象是否已经存在 Class beanClass = resolveBeanClass(mbd, beanName); // Prepare method overrides. try { mbd.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } String errorMessage = null; try { // Instantiate the bean. errorMessage = "BeanPostProcessor before instantiation of bean failed"; // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. if (beanClass != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Object bean = applyBeanPostProcessorsBeforeInstantiation(beanClass, beanName); if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); return bean; } } // Instantiate the bean. errorMessage = "Instantiation of bean failed"; BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { synchronized (getSingletonMutex()) { instanceWrapper = (BeanWrapper) this.factoryBeanInstanceCache.remove(beanName); } } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. if (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingleton(beanName, bean); } // Initialize the bean instance. errorMessage = "Initialization of bean failed"; // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the // state of the bean before properties are set. This can be used, for example, // to support styles of field injection. boolean continueWithPropertyPopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (Iterator it = getBeanPostProcessors().iterator(); it.hasNext(); ) { BeanPostProcessor beanProcessor = (BeanPostProcessor) it.next(); if (beanProcessor instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) beanProcessor; if (!ibp.postProcessAfterInstantiation(bean, beanName)) { continueWithPropertyPopulation = false; break; } } } } if (continueWithPropertyPopulation) { populateBean(beanName, mbd, instanceWrapper); } Object originalBean = bean; bean = initializeBean(beanName, bean, mbd); if (!this.allowRawInjectionDespiteWrapping && originalBean != bean && mbd.isSingleton() && hasDependentBean(beanName)) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans " + getDependentBeans(beanName) + " in its raw version as part of a circular reference, " + "but has eventually been wrapped (for example as part of auto-proxy creation). " + "This means that said other beans do not use the final version of the bean. " + "This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } // Register bean as disposable, and also as dependent on specified "dependsOn" beans. registerDisposableBeanIfNecessary(beanName, originalBean, mbd); return bean; } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, errorMessage, ex); } }
没有找到这个createBean()是在什么时候别调用的 ..... ??? 有点奇怪
应该在loadBeanDefinition之后,但是没有找到调用它的地方......
23 楼
jiwenke
2007-06-28
bennyparlo 写道
不过对于上述的过程中也有问题.
就是说在org.springframework.beans.factory.xml.BeanDefinitonParserDelegate中
这里的property value只是个从xml中解析来的Element(字符流的封装),具体到了装配的时候,确可以被识别不同类型的对象(),这个包装的过程,在哪里实现的呢?
就是说在org.springframework.beans.factory.xml.BeanDefinitonParserDelegate中
public void parsePropertyElement(Element ele, BeanDefinition bd) { String propertyName = ele.getAttribute(NAME_ATTRIBUTE); if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try { if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } Object val = parsePropertyValue(ele, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } }
这里的property value只是个从xml中解析来的Element(字符流的封装),具体到了装配的时候,确可以被识别不同类型的对象(),这个包装的过程,在哪里实现的呢?
我想这里是处理对Property的依赖关系解析,比如如果是一个"bean ref",就会生成一个RunTimeReferenceBean,在代码里可以看到:
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = (propertyName != null) ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"; //这里取得这个Element的子元素值 NodeList nl = ele.getChildNodes(); Element subElement = null; for (int i = 0; i < nl.getLength(); i++) { if (nl.item(i) instanceof Element) { Element candidateEle = (Element) nl.item(i); //下面是对取得的子元素作的具体处理,取得subElement if (DESCRIPTION_ELEMENT.equals(candidateEle.getTagName())) { // Keep going: we don't use this value for now. } else { // Child element is what we're looking for. if (subElement != null && !META_ELEMENT.equals(subElement.getTagName())) { error(elementName + " must not contain more than one sub-element", ele); } subElement = candidateEle; } } } boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute)) && subElement != null) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } //如果这里取得的subElement是Ref bean,生成一个RuntimeBeanReference持有这个bean的名字,但是并不持有ref bean对象。 if (hasRefAttribute) { String refName = ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } else if (hasValueAttribute) { return ele.getAttribute(VALUE_ATTRIBUTE); } if (subElement == null) { // Neither child element nor "ref" or "value" attribute found. error(elementName + " must specify a ref or value", ele); } //这里处理其他类型的属性,比如我们看到的List,Map,Set等等 return parsePropertySubElement(subElement, bd); }
在这里对定义的依赖关系已经在BeanDefinition里面持有了,但是还没有对依赖进行注入。看来依赖解析和具体的依赖注入Spring是分成了两个过程来做的。
22 楼
bennyparlo
2007-06-28
另外包含property value对象的BeanWrapperImpl怎么和容器中需要依赖他的bean相绑定呢?,上述的代码好像没有讲述到BeanDefinition与BeanWrapper的关系...
21 楼
bennyparlo
2007-06-28
不过对于上述的过程中也有问题.
就是说在org.springframework.beans.factory.xml.BeanDefinitonParserDelegate中
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
这里的property value只是个从xml中解析来的Element(字符流的封装),具体到了装配的时候,确可以被识别不同类型的对象(BeanDefinitionValueResolver.resolveValueIfNecessary()),这个包装的过程,在哪里实现的呢?
就是说在org.springframework.beans.factory.xml.BeanDefinitonParserDelegate中
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
这里的property value只是个从xml中解析来的Element(字符流的封装),具体到了装配的时候,确可以被识别不同类型的对象(BeanDefinitionValueResolver.resolveValueIfNecessary()),这个包装的过程,在哪里实现的呢?
20 楼
bennyparlo
2007-06-28
jiwenke 写道
bennyparlo 写道
BeanDefinitionParserDelegate对所做的,是将xml中的定义封装到BeanDefinition实例对象.那对bean的具体初始化操作以及具体的依赖检查等操作,在哪里做了处理?
我在loadBeanDefinitions中没有找到依赖注入的地方,只看到生成BeanDefinition的时候会对一些属性进行设置,比如setPropertyValue - 这个PropertyValue里面会有持有RuntimeReferenceBean - 应该持有的是在IOC容器里面已经有的bean的信息-因为是单例所以在IOC容器里应该只有一个BeanDefinition和一个对象实例,但这个只是索引,没有看到怎样在BeanDefinition中的source对象中注入需要的Property.
哪位一起来研究看看 ?
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory中
/** * Central method of this class: creates a bean instance, * populates the bean instance, applies post-processors, etc. * <p>Differentiates between default bean instantiation, use of a * factory method, and autowiring a constructor. * @see #instantiateBean * @see #instantiateUsingFactoryMethod * @see #autowireConstructor */ protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { // 初始化被依赖的bean实例 if (mbd.getDependsOn() != null) { for (int i = 0; i < mbd.getDependsOn().length; i++) { getBean(mbd.getDependsOn()[i]); } } if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean '" + beanName + "' with merged definition [" + mbd + "]"); } // 判断当前bean的对象是否已经存在 Class beanClass = resolveBeanClass(mbd, beanName); // Prepare method overrides. try { mbd.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } String errorMessage = null; try { // Instantiate the bean. errorMessage = "BeanPostProcessor before instantiation of bean failed"; // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. if (beanClass != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Object bean = applyBeanPostProcessorsBeforeInstantiation(beanClass, beanName); if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); return bean; } } // Instantiate the bean. errorMessage = "Instantiation of bean failed"; BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { synchronized (getSingletonMutex()) { instanceWrapper = (BeanWrapper) this.factoryBeanInstanceCache.remove(beanName); } } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. if (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingleton(beanName, bean); } // Initialize the bean instance. errorMessage = "Initialization of bean failed"; // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the // state of the bean before properties are set. This can be used, for example, // to support styles of field injection. boolean continueWithPropertyPopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (Iterator it = getBeanPostProcessors().iterator(); it.hasNext(); ) { BeanPostProcessor beanProcessor = (BeanPostProcessor) it.next(); if (beanProcessor instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) beanProcessor; if (!ibp.postProcessAfterInstantiation(bean, beanName)) { continueWithPropertyPopulation = false; break; } } } } if (continueWithPropertyPopulation) { populateBean(beanName, mbd, instanceWrapper); } Object originalBean = bean; bean = initializeBean(beanName, bean, mbd); if (!this.allowRawInjectionDespiteWrapping && originalBean != bean && mbd.isSingleton() && hasDependentBean(beanName)) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans " + getDependentBeans(beanName) + " in its raw version as part of a circular reference, " + "but has eventually been wrapped (for example as part of auto-proxy creation). " + "This means that said other beans do not use the final version of the bean. " + "This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } // Register bean as disposable, and also as dependent on specified "dependsOn" beans. registerDisposableBeanIfNecessary(beanName, originalBean, mbd); return bean; } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, errorMessage, ex); } }
当中有些步骤没有完全看明白,不过这里有个populateBean方法
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { PropertyValues pvs = mbd.getPropertyValues(); if (bw == null) { if (!pvs.isEmpty()) { throw new BeanCreationException(beanName, "Cannot apply property values to null instance"); } else { // Skip property population phase for null instance. return; } } if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE); if (hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw); if (hasInstAwareBpps) { for (Iterator it = getBeanPostProcessors().iterator(); it.hasNext(); ) { BeanPostProcessor beanProcessor = (BeanPostProcessor) it.next(); if (beanProcessor instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) beanProcessor; pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); } } applyPropertyValues(beanName, mbd, bw, pvs); }
我想这里才是解析BeanDefinition对象,并且根据依赖关系以及自动装配方式进行property装载的实现.
然后这里有个类org.springframework.beans.factory.support.BeanDefinitionValueResolver
这里的
public Object resolveValueIfNecessary(String argName, Object value) { // We must check each value to see whether it requires a runtime reference // to another bean to be resolved. if (value instanceof BeanDefinitionHolder) { // Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases. BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value; return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition()); } else if (value instanceof BeanDefinition) { // Resolve plain BeanDefinition, without contained name: use dummy name. BeanDefinition bd = (BeanDefinition) value; return resolveInnerBean(argName, "(inner bean)", bd); } else if (value instanceof RuntimeBeanNameReference) { String ref = ((RuntimeBeanNameReference) value).getBeanName(); if (!this.beanFactory.containsBean(ref)) { throw new BeanDefinitionStoreException( "Invalid bean name '" + ref + "' in bean reference for " + argName); } return ref; } else if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; return resolveReference(argName, ref); } else if (value instanceof ManagedList) { // May need to resolve contained runtime references. return resolveManagedList(argName, (List) value); } else if (value instanceof ManagedSet) { // May need to resolve contained runtime references. return resolveManagedSet(argName, (Set) value); } else if (value instanceof ManagedMap) { // May need to resolve contained runtime references. return resolveManagedMap(argName, (Map) value); } else if (value instanceof ManagedProperties) { Properties original = (Properties) value; Properties copy = new Properties(); for (Iterator it = original.entrySet().iterator(); it.hasNext();) { Map.Entry propEntry = (Map.Entry) it.next(); Object propKey = propEntry.getKey(); Object propValue = propEntry.getValue(); if (propKey instanceof TypedStringValue) { propKey = ((TypedStringValue) propKey).getValue(); } if (propValue instanceof TypedStringValue) { propValue = ((TypedStringValue) propValue).getValue(); } copy.put(propKey, propValue); } return copy; } else if (value instanceof TypedStringValue) { // Convert value to target type here. TypedStringValue typedStringValue = (TypedStringValue) value; try { Class resolvedTargetType = resolveTargetType(typedStringValue); if (resolvedTargetType != null) { return this.beanFactory.doTypeConversionIfNecessary( this.typeConverter, typedStringValue.getValue(), resolvedTargetType, null); } else { // No target type specified - no conversion necessary... return typedStringValue.getValue(); } } catch (Throwable ex) { // Improve the message by showing the context. throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Error converting typed String value for " + argName, ex); } } else { // No need to resolve value... return value; } }
可能根据property value的不同进行不同的解析
最后
protected void applyPropertyValues(BeanWrapper bw, PropertyValues pv) { // Synchronize if custom editors are registered. // Necessary because PropertyEditors are not thread-safe. if (!this.customEditors.isEmpty()) { synchronized (this.customEditors) { bw.setPropertyValues(pv); } } else { bw.setPropertyValues(pv); } }
将真正的property values绑定到BeanWrapper对象.
以上就是bean依赖装配的大致过程吧
19 楼
jiwenke
2007-06-28
bennyparlo 写道
BeanDefinitionParserDelegate对所做的,是将xml中的定义封装到BeanDefinition实例对象.那对bean的具体初始化操作以及具体的依赖检查等操作,在哪里做了处理?
我在loadBeanDefinitions中没有找到依赖注入的地方,只看到生成BeanDefinition的时候会对一些属性进行设置,比如setPropertyValue - 这个PropertyValue里面会有持有RuntimeReferenceBean - 应该持有的是在IOC容器里面已经有的bean的信息-因为是单例所以在IOC容器里应该只有一个BeanDefinition和一个对象实例,但这个只是索引,没有看到怎样在BeanDefinition中的source对象中注入需要的Property.
哪位一起来研究看看 ?
18 楼
jiwenke
2007-06-28
bennyparlo 写道
但是上述的这些过程只是加载资源然后将每1个bean定义封装到BeanDefiniton中,最后将所有BeanDefiniton存放到map中,如:
/** Map of bean definition objects, keyed by bean name */ private final Map beanDefinitionMap = new HashMap(); /** List of bean definition names, in registration order */ private final List beanDefinitionNames = new ArrayList();
可以说这里的map才是容器的核心部分.但是这里的map仅仅存放了定义级别的bean,而要获取bean的实例对象,还有个复杂的过程,这个过spring如何来处理的?
同意!
这里应该涉及到Spring IOC容器的核心了,它把用户定义的bean封装成BeanDefinition,然后放到这个BeanDefinitionMap中去 - 一个bean定义对应一个BeanDefintion,但是这些bean之间的依赖关系是怎样管理和怎样实现注入的? - 这是个很好的问题,要再去研究研究代码啦:)
17 楼
jiwenke
2007-06-27
bennyparlo 写道
BeanDefinitionParserDelegate对所做的,是将xml中的定义封装到BeanDefinition实例对象.那对bean的具体初始化操作以及具体的依赖检查等操作,在哪里做了处理?
具体要到BeanDefinitionParserDelegate中去看看,这个BeanDefinitionParserDelegate是根据Spring的bean定义规则生成BeanDefinition的主要地方:
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { //这里从已经解析好的DOM中取得element的属性,确定bean实例化使用的类名。 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE); } String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { this.parseState.push(new BeanEntry(beanName)); //这里是根据得到的类名,去实例化bean对象的地方,注意bean对象不等于BeanDefinition,bean对象只是使用简单的反射机制去得到的一个普通JAVA对象,而BeanDefinition还持有了许多定义信息,bean对象是BeanDefinition持有信息的一部分。 AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition( parent, className, getReaderContext().getReader().getBeanClassLoader()); //下面是许多对Spring定义的bean定义规则的处理,处理结果会反映在BeanDefinition对象中,比如Singleton等各种配置属性 // 最后返回一个BeanDefinition,这是IOC容器操作bean的基本单元,也就是bean在IOC容器中的基本表现。 if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { // Spring 2.0 "scope" attribute bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Specify either 'scope' or 'singleton', not both", ele); } } else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { // Spring 1.x "singleton" attribute bd.setSingleton(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE))); } ......... return bd; ......... }
至于具体的bean的JAVA对象,只是在BeanDefinition中的一个属性,这个对象的生成是在生成BeanDefinition的时候生成的:
public static AbstractBeanDefinition createBeanDefinition( String parent, String className, ClassLoader classLoader) throws ClassNotFoundException { //这里确定BeanDefinition的两种类型,RootBeanDefinition或者是ChildBeanDefinition AbstractBeanDefinition bd = null; if (parent != null) { bd = new ChildBeanDefinition(parent); } else { bd = new RootBeanDefinition(); } if (className != null) { if (classLoader != null) { //这里通过定义的class名字和类装载器通过反射机制生成需要的JAVA对象。 //把这个对象保存到BeanDefinition中去,然后返回生成的BeanDefinition bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { bd.setBeanClassName(className); } } return bd; }
这个BeanDefnition持有了和bean相关的所有配置信息,对于依赖检查和DI的实现, 现在还搞不太明白,也就是说Spring IOC容器是怎样管理bean之间的依赖关系的? - 这个应该是IOC的核心功能。
- 这个要好好看看代码才行。
16 楼
bennyparlo
2007-06-27
jiwenke 写道
bennyparlo 写道
同意上述的说法
不过还有1点,可能也是BeanFactory实现与ApplicationContext实现比较明显的区别,BeanFactory在容器启动过程中不会加载所有定义的bean,而ApplicationContext则将加载所有定义的bean和bean所依赖的bean以及他父context.
这个区别在实现上来看,可以从那些代码中体现??
不过还有1点,可能也是BeanFactory实现与ApplicationContext实现比较明显的区别,BeanFactory在容器启动过程中不会加载所有定义的bean,而ApplicationContext则将加载所有定义的bean和bean所依赖的bean以及他父context.
这个区别在实现上来看,可以从那些代码中体现??
BeanFactory和ApplicationContext的差别体现在对bean的加载处理上,因为它们的具体实现不尽相同,但是不管是 BeanFactory还是ApplicationContext,他们的加载处理都是由BeanDefinitionReader来完成,这个 BeanDefinitionReader完成信息的读取,解析后,最后一步向IOC容器的注册是通过向持有的BeanFactory回调来完成的 - 向IOC容器注册需要实现BeanDefinitionRegistry接口。这个接口在DefaultListBeanFactory里被实现,也就是说ApplicationContext是通过DefaultListBeanFactory来完成向自己持有的IOC容器注册的 - 当然这个IOC容器就是DefaultListBeanFactory。
下面再看加载过程,对ApplicationContext中而言,往往在初始化的时候就加载bean,比如在refresh的时候AbstractApplicationContext:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { // 这里使用XMLBeanDefinitionReader来载入bean定义信息的XML文件 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //这里配置reader的环境,其中ResourceLoader是我们用来定位bean定义信息资源位置的 ///因为上下文本身实现了ResourceLoader接口,所以可以直接把上下文作为ResourceLoader传递给XmlBeanDefinitionReader beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); //这里转到定义好的XmlBeanDefinitionReader中对载入bean信息进行处理 loadBeanDefinitions(beanDefinitionReader); }
这里就为ApplicationContext指定了XmlBeanDefinitionReader,然后是加载过程 - 我们看到这个XmlBeanDefinitionReader持有一个beanFactory, 这个beanFactory是在refresh中得到的:
protected final void refreshBeanFactory() throws BeansException { ......... // 这里初始化一个DefaultListBeanFactory并持有,然后把这个beanFactory传到BeanDefinitionReader中让它回调对BeanDefinition在IOC中进行注册。 try { DefaultListableBeanFactory beanFactory = createBeanFactory(); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } ....... }
而这些步骤往往在XmlBeanFactory中是这样实现的:
public class XmlBeanFactory extends DefaultListableBeanFactory { //这里定义需要的BeanDefinitionReader,我们看到的初始化函数中的this,就是把一个DefaultListableBeanFactory放到XmlBeanDefinitionReader中让它回调。 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } //在初始化函数中调用BeanDefinitionReader来进行加载。 public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } }
这些步骤我们在DefaultListableBeanFactory中是看不到的,所以如果要使用
DefaultListableBeanFactory的话,需要编程式的指明BeanDefinitionReader和调用:
ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res);
如果比较XmlBeanFactory和ApplicationContext,除了XmlBeanFactory不具备那些ResourceLoader和ApplicationEvent这些特定的上下文能力之外,他们持有的IOC容器和加载过程是一样的。
所以BeanDefinitionReader是一个比较重要的类,BeanFactory只维护自己持有的IOC容器,其他都不管,不管你的定义从哪里来,怎么来。
但是上述的这些过程只是加载资源然后将每1个bean定义封装到BeanDefiniton中,最后将所有BeanDefiniton存放到map中,如:
/** Map of bean definition objects, keyed by bean name */ private final Map beanDefinitionMap = new HashMap(); /** List of bean definition names, in registration order */ private final List beanDefinitionNames = new ArrayList();
可以说这里的map才是容器的核心部分.但是这里的map仅仅存放了定义级别的bean,而要获取bean的实例对象,还有个复杂的过程,这个过spring如何来处理的?
15 楼
jiwenke
2007-06-27
bennyparlo 写道
同意上述的说法
不过还有1点,可能也是BeanFactory实现与ApplicationContext实现比较明显的区别,BeanFactory在容器启动过程中不会加载所有定义的bean,而ApplicationContext则将加载所有定义的bean和bean所依赖的bean以及他父context.
这个区别在实现上来看,可以从那些代码中体现??
不过还有1点,可能也是BeanFactory实现与ApplicationContext实现比较明显的区别,BeanFactory在容器启动过程中不会加载所有定义的bean,而ApplicationContext则将加载所有定义的bean和bean所依赖的bean以及他父context.
这个区别在实现上来看,可以从那些代码中体现??
BeanFactory和ApplicationContext的差别体现在对bean的加载处理上,因为它们的具体实现不尽相同,但是不管是 BeanFactory还是ApplicationContext,他们的加载处理都是由BeanDefinitionReader来完成,这个 BeanDefinitionReader完成信息的读取,解析后,最后一步向IOC容器的注册是通过向持有的BeanFactory回调来完成的 - 向IOC容器注册需要实现BeanDefinitionRegistry接口。这个接口在DefaultListBeanFactory里被实现,也就是说ApplicationContext是通过DefaultListBeanFactory来完成向自己持有的IOC容器注册的 - 当然这个IOC容器就是DefaultListBeanFactory。
下面再看加载过程,对ApplicationContext中而言,往往在初始化的时候就加载bean,比如在refresh的时候AbstractApplicationContext:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { // 这里使用XMLBeanDefinitionReader来载入bean定义信息的XML文件 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //这里配置reader的环境,其中ResourceLoader是我们用来定位bean定义信息资源位置的 ///因为上下文本身实现了ResourceLoader接口,所以可以直接把上下文作为ResourceLoader传递给XmlBeanDefinitionReader beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); //这里转到定义好的XmlBeanDefinitionReader中对载入bean信息进行处理 loadBeanDefinitions(beanDefinitionReader); }
这里就为ApplicationContext指定了XmlBeanDefinitionReader,然后是加载过程 - 我们看到这个XmlBeanDefinitionReader持有一个beanFactory, 这个beanFactory是在refresh中得到的:
protected final void refreshBeanFactory() throws BeansException { ......... // 这里初始化一个DefaultListBeanFactory并持有,然后把这个beanFactory传到BeanDefinitionReader中让它回调对BeanDefinition在IOC中进行注册。 try { DefaultListableBeanFactory beanFactory = createBeanFactory(); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } ....... }
而这些步骤往往在XmlBeanFactory中是这样实现的:
public class XmlBeanFactory extends DefaultListableBeanFactory { //这里定义需要的BeanDefinitionReader,我们看到的初始化函数中的this,就是把一个DefaultListableBeanFactory放到XmlBeanDefinitionReader中让它回调。 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } //在初始化函数中调用BeanDefinitionReader来进行加载。 public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } }
这些步骤我们在DefaultListableBeanFactory中是看不到的,所以如果要使用
DefaultListableBeanFactory的话,需要编程式的指明BeanDefinitionReader和调用:
ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res);
如果比较XmlBeanFactory和ApplicationContext,除了XmlBeanFactory不具备那些ResourceLoader和ApplicationEvent这些特定的上下文能力之外,他们持有的IOC容器和加载过程是一样的。
所以BeanDefinitionReader是一个比较重要的类,BeanFactory只维护自己持有的IOC容器,其他都不管,不管你的定义从哪里来,怎么来。
发表评论
-
开放注册了!我们的任务推客 - 群组任务和流程协同SaaS工具
2010-06-30 20:31 621经过这几天团队的努力,为软件增加了开放注册功能,大家可以自己动 ... -
我们团队研发的SaaS软件产品:应用于群组任务协同和流程管理
2010-06-21 15:39 846任务协同及流程管理SaaS软件:任务推客上线,欢迎大家免费体验 ... -
随笔:Spring与云计算(六)
2009-12-17 18:49 3817这样,就说到国内了, ... -
随笔:Spring与云计算(五)
2009-12-16 19:58 3432那其他呢,我们看看还 ... -
随笔:Spring与云计算(四)
2009-12-15 16:26 4196前面我们提到,Spring被VMWare收购而进入云计算领域, ... -
随笔:Spring与云计算(三)
2009-12-14 14:05 3770在前面的那张图中,可以看到SpringSource产品和云计算 ... -
随笔:Spring与云计算(二)
2009-12-11 14:57 4344这么大的范围的模式转 ... -
随笔:Spring与云计算(一)
2009-12-10 15:19 7040对Spring和云计算的关注 ... -
《Spring技术内幕 - 深入解析Spring架构与设计原理》上市了!
2009-12-09 15:17 9158详细的书本目录和章节节选请见附件!欢迎下载指正。 可以购买到 ... -
Spring技术内幕——深入解析Spring架构与设计原理(六)Spring ACEGI
2009-11-20 12:27 14585Spring ACEGI 作为Spring丰富生态系统中的一个 ... -
Spring技术内幕——深入解析Spring架构与设计原理(五)Spring与远端调用
2009-11-16 20:23 11227在应用开发中,常常涉及服务器系统中各种不同进程之间的通信与计算 ... -
Spring技术内幕——深入解析Spring架构与设计原理(四)Web MVC的实现
2009-11-08 08:55 21287以前的欠账,现在补上,欢迎指正和讨论。 Spring Web ... -
Spring技术内幕——深入解析Spring架构与设计原理(三)数据库的操作实现
2009-11-02 17:34 13850最近事情实在是比较多,没有及时更新帖子,还望大家见谅啊。今天, ... -
Spring技术内幕——深入解析Spring架构与设计原理(二)AOP
2009-10-20 08:30 23133关于AOP的个人理解 AOP联盟定义的AOP体系结构把与AO ... -
Spring技术内幕——深入解析Spring架构与设计原理(一)IOC实现原理
2009-10-19 10:47 99000内容较多,新开一贴, ... -
Spring技术内幕——深入解析Spring架构与设计原理(一)引子
2009-10-17 20:31 11335缘起 已经很久没有写 ... -
Hadoop的mapred JobTracker端源码概览
2009-02-18 19:40 5211上一节看到TaskTracker启动新任务的过程,这里接着看看 ... -
Hadoop的mapred TaskTracker端源码概览
2009-02-17 14:39 4119花了许多功夫把Hadoop的mapreduce实现过了一遍,基 ... -
Hadoop的mapred TaskTracker端源码概览
2009-02-17 14:37 1665这篇文章和博客的另一篇重复,删掉了。对不起,请参阅博客的另一篇 ... -
发布用javaeye生成的博客版本 - Spring源代码解析
2008-11-20 08:49 3655呵呵,试试javaeye的新功能。帮助大家阅读,文中的很多错误 ...
相关推荐
在Spring源代码解析的第一部分,我们将聚焦于IOC容器,特别是BeanFactory接口,它是所有Spring容器的基础。 BeanFactory接口是Spring的基石,它定义了基本的容器操作,如获取Bean、检查Bean是否存在、确定Bean的...
Spring源代码解析(一):IOC容器 Spring源代码解析(二):IoC容器在Web容器中的启动 Spring源代码解析(三):Spring JDBC Spring源代码解析(四):Spring MVC Spring源代码解析(五):Spring AOP获取Proxy Spring源...
Spring源代码解析1:IOC容器.doc Spring源代码解析2:IoC容器在Web容器中的启动.doc Spring源代码解析3:Spring JDBC .doc Spring源代码解析4:Spring MVC .doc Spring源代码解析5:Spring AOP获取Proxy .doc Spring...
Spring源代码解析1:IOC容器;Spring源代码解析2:IoC容器在Web容器中的启动;Spring源代码解析3:Spring JDBC ; Spring源代码解析4:Spring MVC ;Spring源代码解析5:Spring AOP获取Proxy;Spring源代码解析6:...
当我们在Web环境中运行Spring应用时,IoC容器需要在Web容器(如Tomcat、Jetty等)中启动并运行。这个过程涉及到一系列的初始化步骤,确保Spring能够正确地与Web容器集成。 首先,`WebApplicationContext`是`...
Spring源代码解析2:IoC容器在Web容器中的启动;Spring源代码解析3:Spring JDBC ; Spring源代码解析4:Spring MVC ;Spring源代码解析5:Spring AOP获取Proxy;Spring源代码解析6:Spring声明式事务处理 ; ...
本文将通过分析Spring源代码来揭示其IoC容器的工作原理。 首先,我们要理解IoC的概念。IoC是一种设计模式,它将对象的创建和管理从应用逻辑中解耦出来,由一个中心组件(如Spring的ApplicationContext)负责。在...
在"spring源代码解析(一):IOC容器.doc"中,讲解了如何通过XML配置或注解方式定义bean,以及容器如何解析配置并构建对象的依赖关系。同时,也探讨了容器如何通过接口查找和管理bean。 2. **Web环境下的IOC容器...
Spring源代码解析(二):ioc容器在Web容器中的启动.doc Spring源代码分析(三):Spring JDBC.doc Spring源代码解析(四):Spring MVC.doc Spring源代码解析(五):Spring AOP获取Proxy.doc Spring源代码解析(六):...
《Spring源代码解析》 Spring框架作为Java领域最流行的开源框架之一,它的设计思想和实现方式一直是广大开发者关注的焦点。深入理解Spring的源代码,能够帮助我们更好地掌握其工作原理,提高我们的开发效率和代码...
Spring框架的源代码解析主要集中在它的核心特性——依赖注入(Dependency Injection,简称DI),这是通过Inversion of Control(IOC)容器实现的。IOC容器在Spring中扮演着至关重要的角色,负责管理对象(bean)的...
Spring框架是Java开发中最常用的轻...总的来说,"Spring源代码解析"将涵盖Spring的核心组件、设计理念、内部实现以及与其他技术的交互,对于希望提升技术水平、深入理解框架运作机制的开发者来说,这是一个宝贵的资源。
《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》从源代码的角度对Spring的内核和各个主要功能模块的架构、设计和实现原理进行了深入剖析。你不仅能从本书中参透Spring框架的出色架构和设计思想,还能从...
在整个源代码分析中,我们可以看到 Spring 实现声明式事务管理有三个部分: 1. 对在上下文中配置的属性的处理,这里涉及的类是 TransactionAttributeSourceAdvisor,这是一个通知器,用它来对属性值进行处理,属性...
第一部分详细分析了Spring的核心:IoC容器和AOP的实现,能帮助读者了解Spring的运行机制;第二部分深入阐述了各种基于IoC容器和AOP的Java EE组件在Spring中的实现原理;第三部分讲述了ACEGI安全框架、DM模块以及Flex...