第三章:Ioc容器
3.2.1 容器的概念
Spring容器提供的功能主要包括
1.組件的生命周期管理
2.組件的配置和組裝服務
3.AOP支持
4.建立在AOP之上的聲明式事物服務
3.2.2 IoC的概念
IoC inversion of Control 控制反轉
public class BookService {
private BookDao bookDao = new DbBookDao();
public List<Book> listBooksByAuthor(String author) {
List<Book> books = bookDao.listAll();
Iterator<Book> it = books.iterator();
while(it.hasNext()) {
if(!it.next().getAuthor().equals(author)) {
it.remove();
}
}
return books;
}
}
如果系統中有很多組件,其生命周期和相互之間的依賴關系如果由組件自己來維護,不但大大增加了系統的復雜程度,而且導致組件組件很緊的耦合,繼而給測試和維護帶來很大困難.
在Ioc模式下,控制權發生了反轉.組件不再由應用程序來創建和配置,而是由Ioc容器來負責,應用程序只是直接使用已經創建和配置好的組件.
public class BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao){
this.bookDao=bookDao;
}
public List<Book> listBooksByAuthor(String author) {
List<Book> books = bookDao.listAll();
Iterator<Book> it = books.iterator();
while(it.hasNext()) {
if(!it.next().getAuthor().equals(author)) {
it.remove();
}
}
return books;
上面沒有創建組件bookDao的代碼,只有使用組件.從而實現了組件的配置和使用的分離,并由Ioc容器負責管理組件的生命周期.
3.2.3 依賴注入的方式
為了讓組件能在IoC容器中被裝配出來,需要某種注入機制,Ioc使用"注入"機制將一種組件注入到另一種組件中.
Type1:接口注入
Type2:設置屬性注入
Type3:構造方法注入
3.3 Spring提供的IoC容器
1.高擴展性無侵入式的框架
2.支持兩種依賴注入方式:設置屬性注入和構造方法注入.
3.提供兩種不同的容器:BeanFactory和ApplicationContext
3.3.1 BeanFactory
應用程序將Bean的創建和配置全部委托給BeanFactory,然后從BeanFactory獲取Bean并使用Bean.流程如下:
啟動應用程序-->實例化BeanFactory-->從BeanFactory中獲取Bean-->使用Bean-->銷毀BeanFactory-->應用程序結束
org.springframework.beans.factory.BeanFactory的具體實現:
XmlBeanFactory 通過一個xml文件創建和配置Bean
從本地文件中載入:
InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = newXmlBeanFactory(is);
或者使用ClassPath定位:
ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
3.3.2 使用ApplicationContext
從BeanFactory繼承而來,所以本質仍然是一個BeanFactory.與基本的BeanFactory相比,還提供了國際化支持,事件的發布和通知機制等
3.4 bean初始化
基本的BeanFactory初始化和ApplicationContext初始化不同:
基本的BeanFactory在調用getBean("beanid")的時候才會創建這個bean
ApplicationContext則在自身初始化的時候創建了所有bean
因為ApplicationContext的特性,ApplicationContext是最佳選擇.在啟動的時候,可以檢測配置文件出錯.這比使用基本的BeanFactory在運行一段時間以后調用getBean()拋出異常要好得多.并且,演示加載會帶來性能上的損失.ApplicationContext唯一的缺點是,由于要在啟動的時候一次性創建所有bean.當定義的bean很多時,啟動的時間比較長.
3.5 裝配bean
裝配Bean的方式就是在xml配置文件中對每個<bean>節點進行配置.
______________________________________________________________________________
public class ExampleBean {
private List list;
private int size = 100;
private String version;
private List list;
private int size = 100;
private String version;
private BasicBean basicBean;
public void setSize(int size) {
this.size=size;
}
public void setVersion(String version) {
this.version = version;
}
public void setSize(int size) {
this.size=size;
}
public void setVersion(String version) {
this.version = version;
}
public void setBean(BasicBean basicBean) {
this.basicBean = basicBean;
}
public void init() {
list = new ArrayList(size);
}
}
public void init() {
list = new ArrayList(size);
}
}
<bean id="exampleBean" class="example.chapter3.ExampleBean" init-method="init">
<property name="size" value="10" />
<property name="version" value="1.0" />
<property name="size" value="10" />
<property name="version" value="1.0" />
<property name="bean" ref="basicBean" />
<property name="bean" <null /></property>
</bean>
</bean>
1.init-method指定一個無參數的初始化方法
2.基本類型的注入,Spring會自動將value的值轉化為合適的類型.若無法完成,則拋出異常.
3.如果注入的屬性不是基本類型,而是引用類型,則用<ref>代替<value>.
4.如果對某個屬性注入null,則必須使用<null/>
_________________________________________________________________________________________
public class ListBean {
public void setChildren(List children){}//普通list
public void setPrices(List<Float> prices) {}//泛型list
public void setFi(int[] fi) {}//int數組
}
public void setPrices(List<Float> prices) {}//泛型list
public void setFi(int[] fi) {}//int數組
}
<bean id="listBean" class="example.chapter3.ListBean">
<property name="children">
<list>
<value>A String</value>
<ref bean="basicBean" />
</list>
</property>
<property name="prices">
<list>
<value>1.24</value>
<value>2.24</value>
<value>3.24</value>
</list>
</property>
<property name="fi">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
<value>5</value>
</list>
</property>
</bean>
<property name="children">
<list>
<value>A String</value>
<ref bean="basicBean" />
</list>
</property>
<property name="prices">
<list>
<value>1.24</value>
<value>2.24</value>
<value>3.24</value>
</list>
</property>
<property name="fi">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
<value>5</value>
</list>
</property>
</bean>
1.設置list和數組的方法是在<list>和</list>之間包含若干<value>或<ref>節點,可以混合使用<value>和<ref>
2.使用java5的泛型,Spring2.0可以強制將<value>的值轉化為Float類型
3.set類型的注入和普通list相同,但set中不包含重復元素.
4.map的注入,每一項都是由一個鍵值和對應的值組成.例如:
public class MapBean {
public void setWeekday(Map map) {}
}
}
<bean id="mapBean" class="example.chapter3.MapBean">
<property name="weekday">
<map>
<entry key="Monday">
<value>one</value>
</entry>
<entry key="Tuesday">
<ref bean="listBean" />
</entry>
</map>
</property>
</bean>
key也可以是任意的Object.
<bean id="mapBean" class="example.chapter3.MapBean">
<property name="weekday">
<map>
<entry>
<key><ref bean="basicBean"></key>
<value>basic</value>
</entry>
<entry>
<key><ref bean="basicBean"></key>
<ref bean="setBean" />
</entry>
<!--
可簡寫成
<entry key-ref="basicBean" value="basic" />
<entry key-ref="listBean" value-ref="setBean" />
-->
</map>
</property>
</bean>
<property name="weekday">
<map>
<entry key="Monday">
<value>one</value>
</entry>
<entry key="Tuesday">
<ref bean="listBean" />
</entry>
</map>
</property>
</bean>
key也可以是任意的Object.
<bean id="mapBean" class="example.chapter3.MapBean">
<property name="weekday">
<map>
<entry>
<key><ref bean="basicBean"></key>
<value>basic</value>
</entry>
<entry>
<key><ref bean="basicBean"></key>
<ref bean="setBean" />
</entry>
<!--
可簡寫成
<entry key-ref="basicBean" value="basic" />
<entry key-ref="listBean" value-ref="setBean" />
-->
</map>
</property>
</bean>
3.6 構造方法注入
public class ConstructorBean {
public ConstructorBean(int min,int max) {
System.out.println("(int,int)");
}
public ConstructorBean(String min,String max) {
System.out.println("(String,String)");
}
}
public ConstructorBean(int min,int max) {
System.out.println("(int,int)");
}
public ConstructorBean(String min,String max) {
System.out.println("(String,String)");
}
}
<bean id="constructorBean" class="example.chapter3.ConstructorBean">
<constructor-arg type="int" value="100">
<constructor-arg type="int" value="200">
</bean>
<constructor-arg type="int" value="100">
<constructor-arg type="int" value="200">
</bean>
1.使用type屬性指定構造方法的參數類型
2.推薦優先考慮設置屬性方法注入
3.7 bean的作用域
<bean id="beanid" class="class" scope="">
spring2.0中一共定義了5中作用域:singleton,prototype,request,session,application.
1.singleton
如果不指定scope,默認值為singleton.Spring的IoC容器僅為每個bean創建一個實例,并保持Bean的引用.也就是說每次調用getBean()方法請求某一bean 時,Spring總返回相同的Bean實例
2.prototype
每次都返回一個新創建的bean實例.因此,Spring容器一旦將實例交給客戶端,就不再對其跟蹤引用,所以不能對prototype作用域的Bean定義destroy-method.
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
System.out.println("Get date: "+context.getBean("date"));
Thread.sleep(1000);
System.out.println("Get date: "+context.getBean("date"));
Thread.sleep(1000);
System.out.println("Get date: "+context.getBean("date"));
Thread.sleep(1000);
}
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
System.out.println("Get date: "+context.getBean("date"));
Thread.sleep(1000);
System.out.println("Get date: "+context.getBean("date"));
Thread.sleep(1000);
System.out.println("Get date: "+context.getBean("date"));
Thread.sleep(1000);
}
<bean id="date" class="java.util.Date" scope="prototype" />
打印出的時間是不同的,說明返回3個不同的date對象實例,如果去掉scope,則每次打印出的時間都是一樣的.