Spring的IoC容器就是一个 IoC service provider. 但是它提供的又不仅仅是IoC service provider的功能,还包括:对象生命周期管理,线程管理,企业服务集成,AOP支持,等等。
Spring提供了2种IoC容器:
- BeanFactory:基础类型的IoC容器,能够完成IoC的所有功能。 采用延迟加载(lazy load),只有客户端需要某个对象时才进行构建和依赖注入。 容器自身加载快,一开始需要的资源少。
- ApplicationContext:它是基于BeanFactory的一种高级的IoC容器。除了提供IoC的基本功能外,还提供了消息通知等其他功能。 它在加载时,将初始化所有业务对象,并进行依赖注入。
BeanFactory中,对象注册和依赖绑定的实现方式
之前我们学习到,有3种绑定方式,这3种方式BeanFactory都是支持的,我们一个一个看看。
- 直接编码方式。 实际上,任何一种方式最后都得进行编码,只是编码工作由不同的人做而已。我们这里研究直接编码,只是想学习一下BeanFactory的实现原理
public static void main(String[] args){ //DefaultListableBeanFactory是BeanFactory接口的一个默认实现,它同时实现了BeanFactory和BeanDefinitionRegistry两个接口。 DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory(); BeanFactory container = (BeanFactory) bindViaCode(beanRegistry); FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider"); newsProvider.getAndPersistNews(); } public static BeanFactory bindViaCode(BeanDefinitionRegistry registry){ //RootBeanDefinition是AbstractBeanDefinition的一个实现,用来保存业务类的相关信息,例如类型,初始化参数等。AbstractBeanDefinition实现了BeanDefinition接口。 AbstractBeanDefinition newsProvider = new RootBeanDefinition(FXNewsProvider.class, true); AbstractBeanDefinition newsListener = new RootBeanDefinition(DownJonesNewsListener.class, true); AbstractBeanDefinition newsPersistenter = new RootBeanDefinition(DownJonesPersistenter.class, true); //将bean的定义注册到容器中 register.registerBeanDefinition("djNewsProvider", newsProvider); register.registerBeanDefinition("djNewsListener", newsListener); register.registerBeanDefinition("djNewsPersistenter", newsPersistenter); //进行绑定操作,这里提供的是通过构造函数绑定时需要的信息 ConstructorArgumentsValue argValues = new ConstructorArgumentsValue(); argValues.addIndexedArgumentValue(0, newsListener); argValues.addIndexedArgumentValue(1, newsPersistenter); newsProvider.setConstructorArgumentValues(argValues); //因为同时实现了2个接口,这里才能进行这种强制转换的 return (BeanFactory) registry; }
- 外部配置文件方式。spring支持2种文件,property文件和xml文件。采用外置配置文件时,spring的IoC提供了一个统一的处理流程。 大致流程是:根据配置文件的格式,选择一个BeanDefinitionReader接口的一个实现,负责读取配置文件的内容,并将其映射到BeanDefinition, 然后将这些BeanDefinition注册到BeanDefinitionRegistry中。这些工作的类似于上面的代码。Property文件,使用的是org.springframework.beans.factory.support.PropertysBeanDefinitionReader, 配置文件如下:
#djNewsProvider是bean的名字,.(class)表明对应的实现类是什么 djNewsProvider.(class)=..FXNewsProvider #通过构造函数进行注入,$后面的数字是构造函数参数的位置索引,(ref)表示所依赖的引用对象,如果不写ref会把djListener当做字符串的。 djNewsProvider.$0(ref)=djListener djNewsProvider.$1(ref)=djPersistenter #通过setter注入 #djNewsProvider.newsListener(ref)=djListener #djNewsProvider.newsPersistenter(ref)= djPersistenter djListener.(class)=..impl.DownJonesListener djPersistenter.(class)=..impl.DownJonesPersistenter
Property文件的加载例子:
public static void main(String[] args){ DefaultListableBeanFactory registry = new DefaultListableBeanFactory(); BeanFactory container = (BeanFactory)bindViaPropertiesFile(registry); FXNewsProvider provider = (FXNewsProvider) container.getBean("djNewsProvider"); provider.getAndPersistentNews(); } public static BeanFactory bindViaPropertiesFile(BeanDefinitionRegistry registry){ PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(registry); reader.loadBeanDefinitions("classpath:../../xxx.properties"); return (BeanFactory)registry; }
-
-
XmlBeanDefinitionReader则是用来读取xml文件的,过程代码与property的例子相似。另外,由于xml非常常用,所以spring又提供了一个累来简化加载配置文件的过程:return XmlBeanFactory(new ClassPathResource("../xxx.xml");
-
下面我们研究下xml文件编写过程可能遇到的问题:
-
使用构造函数时,如果类有2个构造函数,而且参数个数相同,但类型不同,这时我们需要使用type来确定调用哪个构造函数。但是如果有2个参数,怎么指定类型呢??
-
<bean id="Mock" class="MockImpl"> <constructor-arg type="int"> <value>1111</value> </constructor-arg> </bean> <bean id="Mock" class="MockImpl"> <constructor-arg type="int" value=1111/> <constructor-arg type="float" value=12.34/> </bean>
如果有1个构造函数,它有2个参数,我们书写的时候,一定要注意安装参数的定义的顺序。如果不按照顺序,那怎么处理呢? 使用index,可以解决这个问题。
-
<bean id="Mock" class="MockImpl"> <constructor-arg index="0" value=111/> <constructor-arg index="1" value=222/> </bean>
-
使用setter注入时,spring提供了property,使用方式如下。我们需要为属性djNews和account生产setter方法。 实际上setter注入和构造注入是可以并存的,先构造,再调用setter注入。
-
<bean id="Mock" class="MockImpl"> <property name="djNews" ref="djNewsImpl"/> <property name="account" value=222/> </bean> <bean id="djNewsImpl" class="NewsImpl"> </bean>
-
对于construtor-arg和property,除了我们上边用到过的ref和value,我们还可以使用其他元素来指定注入类型:bean,ref, idref,value,null,list,set,map,props。这些元素对于construtor-arg和property是通用的。
-
1. value:可以通过它注入基本数据类型,但是也可以指定String类型和基本类型的包装器类型。但是value是最底层元素,不能在嵌入任何东西了
-
2. ref:用来引用容器中的实例,它有三个属性,local parent 和 bean。其中local是说,我们要引用的实例就在当前配置文件所生成的容器中。 parent是指我们要引用的实例在当前容器的父容器中。bean呢,如果local找不到会去父容器找,所以这个选项比较常用。
<bean id="theTargetBean" class="..."/> <bean id="theClientBean" class="..."> <property name="targetName"> <ref bean="theTargetBean" /> </property> </bean>
BeanFactory parentBeanFactory = new XmlBeanFactory(new ClassPathResource('parent.xml')); BeanFactory childBeanFactory = new XmlBeanFactory(new ClassPathResource('child.xml'), parentBeanFactory);
-
3. idref: 注入的是所依赖的对象的名称,是个字符串,不是对象实例哦! 它的效果与用value是一样的,不同的地方在与,使用idref的话,在解析xml会进行检查是不是有对象的名字叫这个。 如果使用value那么只有到运行时才会发现没有叫这个名字的对象。 idref也支持三个属性,local parent 和 bean
-
4. bean:这个bean与外面的bean支持的元素都是相同的,特殊的地方在与我们用它定义一个内部的,局域的bean,其他地方是看不到它的。 内部bean的id可以省略,因为没人会用它。
<bean id="theClientBean" class="..."> <property name="targetName"> <bean id="theTargetBean" class="..."/> </property> </bean>
-
-
-
- 注解方式,是java5和spring2.5之后才有的。 以后会详细介绍。