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();
那誰來處理呢?丟給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類圖。
RootBeanDefinition與ChildBeanDefinition有什麽差別?
java中的類是有繼承關系,在xml中,bean包括一個parent屬性。
RootBeanDefinition:一個bean就是一個頂級對象(沒有parent)
ChildBeanDefinition:相應於一個子Bean定義。他是從一個已有的Bean繼承而來
ChildBeanDefinitionl裏面有一個私有變量parentName。
下來就是BeanDefinitionRegistry
BeanDefinitionRegistry也是一個接口,其方法例如以下:
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/6068552http://blog.csdn.net/turkeyzhou/article/details/2910888
spring揭秘 讀書筆記 二 BeanFactory的對象註冊與依賴綁定