1. 程式人生 > >spring揭秘 讀書筆記 二 BeanFactory的對象註冊與依賴綁定

spring揭秘 讀書筆記 二 BeanFactory的對象註冊與依賴綁定

oct anr ctx nfc col line 增加 sso default

本文是王福強所著<<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> 
看看client的代碼
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

spring揭秘 讀書筆記 二 BeanFactory的對象註冊與依賴綁定