spring揭祕 讀書筆記 二 BeanFactory的物件註冊與依賴繫結
阿新 • • 發佈:2018-12-31
本文是王福強所著<<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中讀取依賴關係的物件嘛。或者
OK,這次使用了ClassPathXmlApplicationContext,就是一個實現了ApplicationContext且能從xml中讀取依賴關係的物件嘛。ApplicationContext container = new ClassPathXmlApplicationContext("配置檔案路徑"); FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider"); newsProvider.getAndPersistNews();
再或者
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