spring揭秘 读书笔记 二 BeanFactory的对象注册与依赖绑定

本文是王福强所著<<spring揭秘>>一书的读书笔记

我们前面就说过,Spring的IoC容器时一个IoC Service Provider,而且IoC Service Provider提供两个功能对象的创建,依赖关系的管理。

不过,IoC容器这个词中,我们还得关注容器二字。它还包含了一些别的功能,如下图



Spring提供了两种类型的容器,分别是BeanFactory与ApplicationContext。
它们的区别在于:
BeanFactory:对于它所管理的bean,采取的是延迟加载模式,也就是说等用户使用某个bean的时候,采取加载它;另外,它启动所需要的资源也比较少。
ApplciationContext:除了拥有BeanFctory的全部功能外,它还提供了一些高级特性。对应它所管理的对象,在容器启动的时候就把所有的bean都加载了,而且启动时需要的资源也比较多。
两个容器的关系如下:



我们看看BeanFactory的接口说明,都是些跟查询相关的方法。


拥有BeanFactory之后的生活

FXNewsProvider与DowJonesNewsListener等等的代码,我就不写了。
在采用IoC模式之前,我们是这么写代码的
FXNewsProvider newsProvider = new FXNewsProvider(); 
newsProvider.getAndPersistNews(); 
在之前的章节里,我们就说了IoC模式的好处就是用户不用处理各个组件间的依赖问题了。
那谁来处理呢?丢给BeanFactory。
首先,我们用xml来说明依赖关系
<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> 
  ... 
</beans> 
看看客户端的代码  
BeanFactory container = new XmlBeanFactory(new ClassPathResource("配置文件路径")); 
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider"); 
newsProvider.getAndPersistNews(); 
至于XmlBeanFactory是个什么东西,大家看名字猜也能猜出来,就是一个实现了BeanFactoy且能从xml中读取依赖关系的对象嘛。
或者
ApplicationContext container = new ClassPathXmlApplicationContext("配置文件路径"); 
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider"); 
newsProvider.getAndPersistNews(); 
OK,这次使用了ClassPathXmlApplicationContext,就是一个实现了ApplicationContext且能从xml中读取依赖关系的对象嘛。
再或者
ApplicationContext container = new FileSystemXmlApplicationContext("配置文件路径"); 
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider"); 
newsProvider.getAndPersistNews();  
后两者有什么区别?
1、ClassPathXmlApplicationContext
这个方法是从classpath下加载配置文件(适合于相对路径方式加载),例如:
ApplicationContext ctx = new ClassPathXmlApplicationContext( "/applicationcontext.xml ");
该方法参数中classpath: 前缀是不需要的,默认就是指项目的classpath路径下面;这也就是说用ClassPathXmlApplicationContext时默认的根目录是在WEB-INF/classes下面,而不是项目根目录。这个需要注意!


2、FileSystemXmlApplicationContext
这个方法是从文件绝对路径加载配置文件,例如:
ApplicationContext ctx = new FileSystemXmlApplicationContext( "G:/Test/applicationcontext.xml ");
如果在参数中写的不是绝对路径,那么方法调用的时候也会默认用绝对路径来找,我测试的时候发现默认的绝对路径是eclipse所在的路径。
采用绝对路径的话,程序的灵活性就很差了,所以这个方法一般不推荐。
(如果要使用classpath路径,需要加入前缀classpath:   )
 

BeanFactory的对象注册与依赖绑定方式

在上一章,我们就提到spring绑定依赖关系的方式有三种:直接编码,通过配置文件,通过注解方式。
我们一个一个来看

直接编码

public static void main(String[] args)  {  
  DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory(); 
  BeanFactory container = (BeanFactory)bindViaCode(beanRegistry); 
  FXNewsProvider newsProvider = 
  (FXNewsProvider)container.getBean("djNewsProvider"); 
  newsProvider.getAndPersistNews(); 
} 
 
public static BeanFactory bindViaCode(BeanDefinitionRegistry registry) { 
    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. 可以通过构造方法注入方式 
    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; 
}  

我们看看上面几个对象的uml类图。


一个一个说,BeanDefinition,见名知意,是一个对bean的描述对象。RootBeanDefinition与ChildBeanDefinition都是BeanDefinition的实现类。
RootBeanDefinition与ChildBeanDefinition有什么区别?
java中的类是有继承关系,在xml中,bean包含一个parent属性。
RootBeanDefinition:一个bean就是一个顶级对象(没有parent)
ChildBeanDefinition:对应于一个子Bean定义,他是从一个已有的Bean继承而来
ChildBeanDefinitionl里面有一个私有变量parentName。


下来就是BeanDefinitionRegistry
BeanDefinitionRegistry也是一个接口,其方法如下:



registerBeanDefinition干的事情就是把bean与name对应起来,并且把bean的实例与name都存储起来,以后再用。
DefaultListableBeanFactory实现了BeanDefinitionRegistry接口:
里面有这个两个私有属性,看看名字就知道它是干什么的了
        /** Map of bean definition objects, keyed by bean name */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);


	/** List of bean definition names, in registration order */
	private final List<String> beanDefinitionNames = new ArrayList<String>();
说实话,我不明白为什么还需要有一个beanDefinitionNames,beanDefinitionMap的key不就是beanname吗?


解释了这么多了,我想上面使用硬代码绑定依赖关系的例子,我就不解释了,大家都能看懂。

外部配置文件方式

通过xml或者Properties文件加载依赖信息的情况还是比通过硬代码加载依赖关系普遍(或者说通过硬代码加载,本身就是为了说明问题,实际中倒是不会这么用)。
加载外部文件,那么首先就得介绍一个接口。
BeanDefinitionReader。
看名字就知道,这个接口管的是读取BeanDefinition的信息(准确的说是从外部文件读取信息,填充到BeanDefinition中)。
它有两个实现类 PropertiesBeanDefinitionReader, XmlBeanDefinitionReader
看名字就知道,一个从Properties中读,一个从xml中读。
从properties
Properties文件如下:
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 

调用例子如下:
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; 
} 
从xml
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")); 
}  
跟上面的properties每什么区别,就是多了一个XmlBeanFactory。
通过查看api文档,我们知道XmlBeanFactory直接继承自DefaultListableBeanFactory。


注解方式

自从sping2.5之后,有了注解方式,它就一下子收到了广大程序员的热烈欢迎。
bean示例:
@Component 
public class FXNewsProvider  { 
  @Autowired 
  private IFXNewsListener  newsListener; 
  @Autowired 
  private IFXNewsPersister newPersistener; 
  
  public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister) 
  { 
   this.newsListener   = newsListner; 
   this.newPersistener = newsPersister; 
  } 
  ... 
} 
 
@Component 
public class DowJonesNewsListener implements IFXNewsListener  { 
  ... 
} 
 
@Component 
public class DowJonesNewsPersister implements IFXNewsPersister  { 
  ... 
}   
@Component 是告诉spring的扫描器:这是一个bean。
@Autowired 是告诉spring的扫描器:这里需要一个注入对象。
示例xml
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:tx="http://www.springframework.org/schema/tx" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-2.5.xsd 
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> 
<context:component-scan base-package="cn.spring21.project.base.package"/> 
</beans>  

<context:component-scan base-package="cn.spring21.project.base.package"/>就是告诉spring容器,扫描cn.spring21.project.base.package这个包,看哪里有 @Component @Autowired等等。


下面就是大家看到的第一spring程序。
public static void main(String[] args) { 
  ApplicationContext ctx = new ClassPathXmlApplicationContext("配置文件路径"); 
  FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("FXNewsProvider"); 
   newsProvider.getAndPersistNews(); 
}    

 感谢glt

参考资料

http://blog.csdn.net/souichiro/article/details/6068552
http://blog.csdn.net/turkeyzhou/article/details/2910888
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值