BeanFactory
使用BeanFactory的XML配置方式实现业务对象间的依赖管理
<beans>
<bean id="djNewsProvider" class="..FXNewsProvider">
<constructor-arg index="0">
<ref bean="djNewsListener"/>
</constructor-arg>
<constructor-arg index="1">9
<ref bean="djNewsPersister"/>
</constructor-arg>
</bean>
...
</beans>
具体使用:
BeanFactory container = new XmlBeanFactory(new ClassPathResource("配置文件路径"));
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
// 或者
ApplicationContext container = new ClassPathXmlApplicationContext("配置文件路径");
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
依赖关系管理:对象注册与依赖绑定方式
1. 直接编码
main方法中:
- 构造一个 DefaultListableBeanFactory 作 为 BeanDefinitionRegistry
- 将其交给bindViaCode方法进行具体的对象注册和相关依赖管理
- 通过bindViaCode返回的BeanFactory取得需要的对象,最后执行相应逻辑
bindViaCode方法中:
- 构造业务对象与其相对应的BeanDefinition,使用了 RootBeanDefinition 作 为 BeanDefinition 的实现类。
- 构造完成后,将这些BeanDefinition注册到通过方法参数传进来的BeanDefinitionRegistry中。
- 指定依赖关系:构造方法注入,setter方法注入。
public static void main(String[] args) {
// 构造一个DefaultListableBeanFactory作为BeanDefinitionRegistry
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
// 交给bindViaCode进行具体对象的注册和依赖管理
BeanFactory container = (BeanFactory) bindViaCode(beanRegistry);
// 通过bindViaCode返回的BeanFactory取得需要的对象
FXNewsProvider newsProvider = (FXNewsProvider) container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
}
public static BeanFactory bindViaCode(BeanDefinitionRegistry registry) {
// 构造业务对象对应的BeanDefinition
AbstractBeanDefinition newsProvider = new RootBeanDefinition(FXNewsProvider.class, true);
AbstractBeanDefinition newsListener = new RootBeanDefinition(DowJonesNewsListener.class, true);
AbstractBeanDefinition newsPersister = new RootBeanDefinition(DowJonesNewsPersister.class, true);
// 将bean定义注册到容器中
registry.registerBeanDefinition("djNewsProvider", newsProvider);
registry.registerBeanDefinition("djListener", newsListener);
registry.registerBeanDefinition("djPersister", newsPersister);
/**
* 指定依赖关系:1.构造方法注入 2.setter方法注入
*/
// 1. 可以通过构造方法注入方式
ConstructorArgumentValues argValues = new ConstructorArgumentValues();
argValues.addIndexedArgumentValue(0, newsListener);
argValues.addIndexedArgumentValue(1, newsPersister);
newsProvider.setConstructorArgumentValues(argValues);
// 2. 或者通过setter方法注入方式
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue(new ropertyValue("newsListener", newsListener));
propertyValues.addPropertyValue(new PropertyValue("newPersistener", newsPersister));
newsProvider.setPropertyValues(propertyValues);
// 绑定完成
return (BeanFactory) registry;
}
DefaultListableBeanFactory:BeanFactory的实现类
-
BeanFactory接口只定义如何访问容器内管理的Bean的方法,各个BeanFactory的具体实现类负责具体Bean的注册以及管理工作。
-
BeanDefinitionRegistry接口定义抽象了Bean的注册逻辑。
通常情况下,具体的BeanFactory实现类会实现这个接口来管理Bean的注册。
BeanFactory、BeanDefinitionRegistry以及DefaultListableBeanFactory的关系
BeanDefinitionRegistry 管理 BeanDefinition
每一个受管的对象,在容器中都会有一个BeanDefinition的实例(instance)与之相对应,该 BeanDefinition的实例负责保存对象的所有必要信息,【包括其对应的对象的class类型、是否是抽象 类、构造方法参数以及其他属性等】。
当客户端向BeanFactory请求相应对象的时候,BeanFactory会通过这些信息为客户端返回一个完备可用的对象实例。RootBeanDefinition和ChildBeanDefinition是BeanDefinition的两个主要实现类。
2. 外部配置文件
Spring的IoC容器支持两种配置文件格式:Properties文件格式和XML文件格式。
采用外部配置文件时,Spring的IoC容器有一个统一的处理方式。
- 通常情况下,需要根据不同的外部配置文件格式,给出相应的BeanDefinitionReader实现类
- 由BeanDefinitionReader的相应实现类负责将相应的配置文件内容读取并映射到BeanDefinition:包括解析文件格式、装配BeanDefinition之类的工作,都是由BeanDefinitionReader的相应实现类来做的
- 将映射后的BeanDefinition注册到一个BeanDefinitionRegistry:BeanDefinitionRegistry只不过负责保管而已
- BeanDefinitionRegistry即完成Bean的注册和加载
BeanDefinitionRegistry beanRegistry = <某个BeanDefinitionRegistry实现类,通常为DefaultListableBeanFactory>;
BeanDefinitionReader beanDefinitionReader = new BeanDefinitionReaderImpl(beanRegistry);
beanDefinitionReader.loadBeanDefinitions("配置文件路径");
// 现在我们就取得了一个可用的Bea nDefinitionRegistry实例
1. Properties配置格式的加载
Spring提供了org.springframework.beans.factory.support.PropertiesBeanDefinitionReader类用于Properties格式配置文件的加载,所以,我们不用自己去实现BeanDefinitionReader, 只要根据该类的读取规则,提供相应的配置文件即可。
djNewsProvider.(class)=..FXNewsProvider
# ----------通过构造方法注入的时候-------------
djNewsProvider.$0(ref)=djListener
djNewsProvider.$1(ref)=djPersister
# ----------通过setter方法注入的时候---------
djNewsProvider.newsListener(ref)=djListener
djNewsProvider.newPersistener(ref)=djPersister
djListener.(class)=..impl.DowJonesNewsListener
djPersister.(class)=..impl.DowJonesNewsPersister
这些内容是特定于Spring的PropertiesBeanDefinitionReader的,要了解更多内容,请参照Spring的API参考文档。
- djNewsProvider作为beanName,后面通过.(class)表明对应的实现类是什么。其他两个类的注册,djListener和djPersister,也是相同的道理。
- 通过在表示beanName的名称后添加.$[number]后缀的形式,来表示当前beanName对应的对象需要通过构造方法注入的方式注入相应依赖对象。在这里,我们分别将构造方法的第一个参数和第二个参数对应到djListener和djPersister。需要注意的一点,就是$0和$1后面的 (ref),(ref)用来表示所依赖的是引用对象,而不是普通的类型。如果不加(ref), PropertiesBeanDefinitionReader会将djListener和djPersister作为简单的String类型 进行注入。
- 构造方法注入方式:FXNewsProvider采用的是构造方法注入
- setter方法注入与构造方法注入最大的区别就是,它不使用数字顺序来指定注入的位置,而使用相应的属性名称来指定注入。newsListener和 newPersistener恰好就是我们的FXNewsProvider类中所声明的属性名称。与在 Properties中表达构造方法注入一样,同样需要注意,如果属性名称所依赖的是引用对象,那 么一定不要忘了**(ref)**。
当这些对象之间的注册和依赖注入信息都表达清楚之后,就可以将其加载到BeanFactory而付诸使用了。
public static void main(String[] args) {
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
BeanFactory container = (BeanFactory) bindViaPropertiesFile(beanRegistry);
FXNewsProvider newsProvider = (FXNewsProvider) container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
}
public static BeanFactory bindViaPropertiesFile(BeanDefinitionRegistry registry) {
PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(registry);
reader.loadBeanDefinitions("classpath:../../binding-config.properties");
return (BeanFactory) registry;
}
2. xml配置格式的加载
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="djNewsProvider" class="..FXNewsProvider">
<constructor-arg index="0">
<ref bean="djNewsListener"/>
</constructor-arg>
<constructor-arg index="1">
<ref bean="djNewsPersister"/>
</constructor-arg>
</bean>
<bean id="djNewsListener" class="..impl.DowJonesNewsListener">
</bean>
<bean id="djNewsPersister" class="..impl.DowJonesNewsPersister">
</bean>
</beans>
具体使用
public static void main(String[] args) {
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
BeanFactory container = (BeanFactory) bindViaXMLFile(beanRegistry);
FXNewsProvider newsProvider = (FXNewsProvider) container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
}
public static BeanFactory bindViaXMLFile(BeanDefinitionRegistry registry) {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
reader.loadBeanDefinitions("classpath:../news-config.xml");
return (BeanFactory) registry;
// 或者直接
// return new XmlBeanFactory(new ClassPathResource("../news-config.xml"));
}
- Spring同样为XML 格式的配置文件提供了现成的BeanDefinitionReader实现,即XmlBeanDefinitionReader。
- XmlBeanDefinitionReader负责读取Spring指定格式的XML配置文件并解析
- 将解析后的文件内容映射到相应的BeanDefinition,并加载到相应的BeanDefinitionRegistry中(在这里是DefaultListableBeanFactory)
- 整个BeanFactory就可以放给客户端使用了
Spring除了提供XmlBeanDefinitionReader用于XML格式配置文件的加载,Spring还在DefaultListableBeanFactory的基础上构建了简化XML格式配置加载的XmlBeanFactory实现。从以上代码最后注释掉的一行,你可以看到使用了XmlBeanFactory之后,完成XML的加载和BeanFactory的初始化是多么简单。
3. 注解方式
如果要通过注解标注的方式为FXNewsProvider注入所需要的依赖,现在可以使用**@Autowired以 及@Component**对相关类进行标记。
@Autowired是这里的主角,它的存在将告知Spring容器需要为当前对象注入哪些依赖对象。
@Component则是配合Spring 2.5中新的classpath-scanning功能使用的。该类交给spring管理的标志。现在我们只要再向Spring的配置 文件中增加一个“触发器”,使用@Autowired和@Component标注的类就能获得依赖对象的注入了。