轉:Spring裝配bean

在Spring中,對象間的協作是通過IoC機制完成的。
反向控制也叫依賴注入(Dependency Injection,DI),簡單來說就是將JavaBean需要的
對象通過配置文件加載進來。

Spring提供了兩種裝配Bean的容器,一是BeanFactoy,另一個是ApplicationContext。
兩者做爲容器,所有的Bean都應該通過容器裝配,而容器也知道自己都裝配了哪些Bean。

Bean裝配實際上就是讓容器知道程序中都有哪些Bean,可以通過以下兩種方式實現:
配置文件(最爲常用,體現了依賴注入DI的思想)
編程方式(寫的過程中向BeanFactory去註冊)

ApplicationContext與BeanFactory都是接口,ApplicationContext是由BeanFactory
接口擴展而來,它增強了BeanFactory的功能。

Bean容器能夠加載的對象並不一定是嚴格遵循JavaBeans規範的Java類,任何可實例化的類都
可以通過Spring Bean容器加載進來。通常稱這些類爲POJO。

要記住,BeanFactory不僅僅只具備實例化Bean的功能,它還知道所有的Bean,可以配置和管理它們。


Spring給出一些BeanFactory的實現類,其中最爲常用的是XmlBeanFactory。
1、通過文件系統
Resource res = new FileSystemResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
2、通過類路徑
ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
3、通過ApplicationContext加載
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
BeanFactory factory = (BeanFactory) appContext;


sping-frameword-1.2.7-with-dependencies中的dist裏面是spring的包
將其中的spring.jar加入工程的類路徑裏面,下一步是寫個javaBean

package bean ;

public class HelloBean
{
public HelloBean
{
System.out.println("init!");
}
private String msg ;

//加上getter和setter方法
};

public class Demo
{
public static void main(String[] args)
{
Resource res = new ClassPathResource("bean.xml") ;
//配置文件放在類路徑的根路徑下
BeanFactory factory = new XmlBeanFactory(res) ;
//在dist文件夾裏面有spring-beans的dtd文檔
HelloBean bean = (HelloBean)factory.getBean("hello") ;
//根據xml配置文件中的id號去找bean,然後從這個工廠中取出bean
System.out.println(bean.getMsg());

HelloBean bean1 = (HelloBean)factory.getBean("hello") ;
System.out.println(bean==bean1); //因爲配置文件中的singleton設置爲true,
//故是單例模式,所以兩個bean肯定一樣,所以打印true

//只有ApplicationContext才能實現bean的非延遲加載功能,所以要想讓bean中的
//"init!"字符串在"before getbean!"字符串之前顯示的話,那麼首先必須得是單例模式,
//然後還得設置lazy-init="false",纔可以做到,如果singleton="false"的話,是無法
//做到非延遲加載的!可以通過下面的代碼來驗證,看"before getbean!"和"init!"這兩個
//字符串出現的先後順序就一目瞭然了。
ApplicationContext factory = new ClassPathXmlApplicationContext("bean.xml");
System.out.println("before getbean!");
HelloBean bean2 = (HelloBean)factory.getBean("hello");
}
}

配置文件bean.xml:

將<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
拷貝進去……

<beans>
//singleton屬性表明容器的bean是否使用單例模式……
//默認情況下,beanFactory就是延遲加載的
<bean id="hello" class="bean.HelloBean" singleton="true"
lazy-init="false">
<property name="msg">
<value>Hello,Spring!</value>
</property>
</bean>

</beans>

//上面的配置文件將屬性的值注入到bean的屬性中,這就是依賴注入的一個例子
//同時將那個javaBean註冊到容器中
通常可以通過三種方式裝配Bean

通過setter方法
通過構造函數
自動裝配
其中,使用setter方法裝配Bean是最爲常用的。

如下代碼的執行結果是什麼?
FirstBean bean = (FirstBean)factory.getBean("my");
FirstBean bean1 = (FirstBean)factory.getBean("my");
System.out.println(bean==bean1);
結果依賴於bean元素的singleton屬性,如果設置true,則以單例模式加載bean,
則永遠指向同一個bean;反之則每次實例化一個。
也即在單例模式下,BeanFactory維護所有的bean

如果想讓一個單例模式的bean被清理掉的話,不能簡單的將bean置爲null,而是必須使用
destroySingletons方法
還有一個destroyBean(String beanName,Object bean)
這兩個方法都可以來垃圾回收單例模式中的bean


--------------------------------------------------------------


延遲加載:

如下代碼執行的結果是什麼?
System.out.println("before loading");
FirstBean bean = (FirstBean)factory.getBean("my");
System.out.println(“after loading”);
在bean的構造函數中,包含:
System.out.println(“bean init”);
結果依賴於bean的lazy-init屬性,如果設置爲true,則在第一次加載該bean時初始化;
否則在初始化容器時就加載所有bean。
延遲加載僅在bean單例模式下起作用。

-------------------------------------------------------------------------


初始化和清理:

Spring的初始化肯定是在依賴注入(屬性設置)全部完成後纔去做的。
框架提供的初始化方法肯定是先於我們自己定義的初始化方法的!比如,如果繼承了
InitializingBean的話,會有一個這個接口提供的初始化方法,叫做afterPropertySet,
它的執行就是先於你定義的初始化方法的……

同理,DisposableBean接口中也定義了一個清理的方法,這個方法肯定也是先於你定義的
清理方法的!

建議使用你自己定義的初始化和清理方法,不要使用容器提供的,這樣可以避免bean被污染!!!

在許多情況下,需要在對象加載後立即初始化資源,而在刪除對象前先清理資源
在spring中,提供了兩種方式實現初始化和清理工作。
通過設置bean的init-method和destroy-method指定初始與清理方法
通過實現InitializingBean及DisposableBean接口,由容器在加載與刪除bean時自動調用。

//既然定義了一個init方法,所以可以在javaBean中設置下面的初始化方法:
public void init()
{

}

//同樣銷燬方法也可以類似的定義:
public void destroy()
{

}
//特別注意,如果你想運行上面自定義的destroy方法的話,你必須首先執行
factory.destroySingletons();
//否則不會去執行你上面自定義的destroy方法的!

原型bean在創建之後就脫離了factory的管理,所以呢,對於原型bean,只能初始化,而
不可以清理

<bean id="first" class="mybeans.FirstBean" init-method="init"
destroy-method="destroy" >
<property name="message">
<value>Hi, Rod Johnson</value>
</property>
</bean>
可以通過destroySingletons方法刪除所有單例bean
原型bean在創建後即脫離BeanFactory的維護,所以只能調用初始化方法,而不能做清理工作。

===========================================================================

下午課程開始:

public class HelloBean implements BeanNameAware, BeanFactoryAware
{
private BeanFactory factory ;
private String name ;

public void setBeanName(String a)//由BeanNameAware繼承來
{
this.name = a ;
}

//由BeanFactoryAware繼承而來。
public void setBeanFactory(BeanFactory factory)
throws BeanException
{
this.factory = factory
}
}

-------------------------------------------------------


下面說說編程式加載Bean方式:

XmlBeanFactory提供兩種編程方式加載Bean:
registerSingleton和registerBeanDefinition,前者不參與bean生命週期
後者參與bean的生命週期。

public static void main(String[] args)
{
Resource res = new ClassPathResource("bean.xml");

XmlBeanFactory factory = new XmlBeanFactory(res);
factory.registerSingleton("bb",new HelloBean());
HelloBean bean = (HelloBean)factory.getBean("bb");
System.out.println(bean.getMsg());

factory.destroySingletons();
}

如果使用BeanDefinition的話

public static void main(String[] args)
{
Resource res = new ClassPathResource("bean.xml");

XmlBeanFactory factory = new XmlBeanFactory(res);
RootBeanDefinition rbd = new RootBeanDefinition();
rbd.setBeanClass(HelloBean.class);
rbd.setInitMethodName("init");
//上面這句話告訴Spring自定義的方法名字叫做init,如果不寫的話,自定義的
//初始化方法不會執行的。

/*
RootBeanDefinition rbd1 = new RootBeanDefinition(Integer.class);
ContructorArgumentValues cav = new ConstructorArgumentValues();
cav.addGenericArgumentValue("12");
rbd1.setConstructorArgumentValues(cav);

factory.registerBeanDefinition("aa",rbd1);
System.out.println(factory.getBean("aa"));
*/
factory.registerBeanDefinition("aa",rbd);


HelloBean bean = (HelloBean)factory.getBean("aa");
System.out.println(bean.getMsg());

factory.destroySingletons();
}

老師的關鍵代碼如下:

XmlBeanFactory factory = new XmlBeanFactory(res);
RootBeanDefinition rbd = new RootBeanDefinition();
rbd.setBeanClass(HelloBean.class);
rbd.setInitMethodName("init");

RootBeanDefinition rbdi = new RootBeanDefinition(Integer.class);
ConstructorArgumentValues cav = new ConstructorArgumentValues();
cav.addGenericArgumentValue("12");
rbdi.setConstructorArgumentValues(cav);

=====================================================================

bean裏面的屬性是各式各樣的,我們上面使用的實體類只是使用了String類型,根本沒有考慮
其他的類型

bean裝配方式:

通過Setter方法、通過構造函數、自動裝配三種方法都可以達到裝配Bean的目的。
其中使用setter方法裝配Bean是最爲常用的。

先說一下setter方法的裝配:
setter方法可以裝配基本類型、Bean類型、內部bean類型、集合類型、設置空值

所謂Bean類型屬性是指這個屬性類型是個javaBean,並且這個javaBean必須已經在Spring
中註冊過了!

對於基本數據類型,
可以包括八種基本類型以及其包裝類

<property name="" value="" />就可以了,不用在意具體類型,Spring會自動轉換的。

裝配基本類型:
包括八種基本類型及它們的包裝類,還有String類型。
不必關心具體類型,spring可以通過反射機制瞭解到屬性的類型信息
可以通過<property>的子元素<value>來設置基本類型的值,也可以通過其屬性value來設置,
效果完全相同。

<bean id="first" class="mybeans.FirstBean" >
<property name="message">
<value>Hi, Rod Johnson</value>
</property>
<property name="age" value="1" />
//注意不能同時使用value子元素和value屬性
</bean>

--------------------------------------------------------------------------

裝配bean類型屬性:
只有通過配置文件向容器註冊的bean才能通過注入機制設置到其它bean的屬性上。
使用<ref>子元素或ref屬性設置
Spring即是通過這種方式建立起bean間的依賴關係,實現強大的bean管理功能。

舉例說明:
BasicDataSource

引入包commons-dbcp、commons-pool以及commons-collections三個包引入
還有mysql的驅動程序
將BasicDataSource中的setter方法注入Spring中去。

在bean.xml中:
//將BasicDataSource中的setter方法中的對應值設置進去!
<beans>
<bean id="data" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///j2ee"/>
<property name="username" value="root" />
<property name="password" value="root" />

</bean>

<bean id="course" class="bean.CourseBean">
<property name="dataSource" ref="data"/>
//由於是bean,所以使用ref!
</bean>
</beans>

建立一個Bean,對應數據庫中的一個表
public class CourseBean
{
private int id ;
private String name ;
private DataSource dataSource ;
//加入getter和setter方法

public void save()
{

}
}

然後就可以進行一下數據庫的操作了……

在main函數中執行下面的代碼:
---------------------------------------------------------------
Resource res = new ClassPathResource("bean.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
CourseBean course = (CourseBean)factory.getBean("course");
//由於數據源和courseBean之間的依賴關係已經通過配置文件給配置好了,所以這裏
//只需要得到courseBean,就自然有了數據源的bean了。
course.setName("adsf");
course.save();


-----------------------------------------------------------------


所謂DAO就是既有值,又可以將這個值存入數據庫的對象,也就是數據庫訪問對象
Data Access Object

所謂VO就是僅僅有值,也就是value object,值對象。
上面的這個course就是DAO了。

上面的save方法可以簡單的寫成下面的樣子:

public void save()
{
try
{
Connection con = this.dataSource.getConnection();
String sql = "insert into courses (coursename,period)"+
"values(rtrim(?),rtrim(?))" ;
PreparedStatement pstmt = con.prepareStatement(sql) ;
pstmt.setString(1,this.coursename);
pstmt.setInt(2,this.period.intValue());

pstmt.executeUpdate();
}
catch (SQLException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}

======================================================================

如果一個bean在使用前,其某個屬性的值必須已經通過注入機制注入,spring可以協助檢查這個屬性
是否被初始化。
bean的屬性dependency-check即用來實現這樣的功能,其可選值包括
none --不檢查
objects --僅檢查bean類型
simple --僅檢查基本類型(8種)和String類型
all --全檢查
default --默認,不檢查


此外,因爲可能會調用其它對象的方法,有時一個對象在使用前必須要求另外一個對象已經初始化完成
這種依賴關係可以通過depends-on屬性來實現,spring會保證在初始化當前對象前將depends-on
指定的對象也初始化出來。

如果依賴多個對象,使用逗號將它們的名字分開。

內部Bean
<bean id="outer" class="...">
<property name="target">
<bean class="com.mycompany.PersonImpl">
<property name="name">
<value>Tony</value>
</property>
</bean>
</property>
</bean>

----------------------------------------------------


Spring支持以下四種集合的裝配:
<list>--java.util.List
<set>--java.util.Set
<map>--java.util.Map
<props>--java.util.Properties


list:
<property name="someList">
<list>
<value>someValue</value>
<ref bean="myDataSource"/>
<list>
<value>anyValue</value>
</list>
</list>
</property>


set:
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource"/>
</set>
</property>

map:
<property name="someMap">
<map>
<entry>
<key><value>yup an entry</value></key>
<value>just some string</value>
</entry>
<entry>
<key><value>yup a ref</value></key>
<ref bean="myDataSource"/>
</entry>
</map>
</property>

props:
<property name="people">
<props>
<prop key="HarryPotter">
The magic property
</prop>
<prop key="JerrySeinfeld">
The funny property
</prop>
</props>
</property>
==========================================================
==========================================================

如果屬性沒有setter方法的話,需要考慮使用構造方法裝配pbean的屬性,這就是
構造裝配
一般在以下情況使用:

屬性沒有setter方法,是隻讀屬性
屬性只需要設置一次,以後就不會再更改
一般使用構造裝配只裝配少量屬性

注意在構造裝配下,構造函數中應該包含有需要裝配的屬性

比如:
<bean id="i" class="java.lang.Integer">
<constructor-arg value="23" />
</bean>

這就是利用構造函數將Integer注入到容器中去。

Integer i = (Integer)factory.getBean("i");
System.out.println(i);

-----------------------------------------------------


<bean id="first" class="mybeans.FirstBean">
<constructor-arg>
<value>http://www.sohu.com</value>
</constructor-arg>
<constructor-arg>
<value>http://localhost:8080</value>
</constructor-arg>
<property name="age" value="1" />
</bean>
多個參數時使用多個constructor-arg指定,出現次序決定了參數次序,或者使用index屬性指定


======================================================

自動裝配:

可以通過spring框架自動爲bean裝配屬性。
自動裝配只能裝配bean類型,即取代ref元素。
使用自動裝配只需在bean元素中加入屬性autowire,自動裝配可被手動裝配覆蓋。
也可以選擇在beans中加入default-autowire屬性,爲所有bean設置默認自動裝配。

“no“-不使用自動裝配,spring推薦。
“byName”-依據名稱或ID裝配bean,如果沒有找到,則不裝配,可依賴dependency-check
屬性檢查是否裝配。
“byType”-依據類型自動裝配,如果存在兩個以上同種類型,則拋出異常。
“constructor”-依據當前bean構造函數裝配,查找與構造函數參數同類型bean
“autodetect”-自動檢測,先通過constructor,再使用byType

Spring是不推薦使用自動裝配的,首先是系統開銷的問題,還有就是同名的設置可能會導致
意外的問題……

====================================================================

通過BeanFactory獲取Bean與直接new一個Bean是完全不同的。
不相同的地方在於:從factory中得到的bean是被注入依賴關係的bean,而new出來的bean
則沒有依賴關係,屬性是空的。

通過BeanFactory獲取的bean能將所有依賴注入到bean中,屬性的值會依設置而定。
New出來的bean屬性值必須主動設置。
在需要使用spring框架情況下,所有bean都應該由容器來管理。


===================================================================

單例和原型的問題:依賴於singleton參數,true爲單例模式。
延遲加載:體現在整個容器初始化的時候立刻加載bean還是等到使用這個bean的時候才加載。
factory.getBean的時候就已經是開始使用bean了,並不一定要訪問其中的屬性
纔算是使用。

只有ApplicationContext才能做到容器初始化的時候立刻加載bean,BeanFactory
是做不到的。

註冊bean的時候,name屬性是用的別名,id屬性標明真名。name一般是在web開發裏面去用,name
裏面是可以包含一些特殊符號的,用於告訴開發者當前的屬性代表什麼具體的實際意思。

setter方法、構造函數和自動裝配三種裝配bean的方式

=====================================================================

ApplicationContext:

ApplicationContext與BeanFactory的作用相類似,都起到裝配、管理Bean的作用。
不同的是,ApplicationContext在此基礎上增強了BeanFactory的功能,這包括國際化、
加載資源、發佈事件等等。

國際化的支持:

Spring提供了兩個實現了MessageSource接口的類,它們都在
org.springframework.context.support包中:

ResourceBundleMessageSource使用JFC中標準的java.util.ResourceBundle來操作資源
ReloadableResourceBundleMessageSource 可以在不重啓虛擬機的情況下重新加載資源。

ResourceBundleMessageSource是具體的類而且有setter方法,所以可以注入
根據setBasename,
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="bean.res" />
</bean>

千萬注意一點,只要是資源包的話,id必須起名字爲“messageSource”!!!!

這裏還需要在bean包下面建立一個資源包res.properties,
在裏面寫上:
msg=Hello,{0}

public static void main(String[] args)
{
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
String s = context.getMessage("msg",new Object[]{"Spring-Hibernate"},new Locale("zh"));
System.out.println(s);
}

這就打印出了“Hello,Spring-Hibernate”

--------------------------------------------------------


假如有多個資源包呢?
比如我又在bean包裏面建了一個ress.properties
m=M

那麼
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>bean.res</value>
<value>bean.ress</value>
</list>
</property>
</bean>

--------------------------------------------------------

可以通過IOC機制加載資源,如
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>springResource</value>
</property>
</bean>
或者
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>

千萬注意一點,只要是資源包的話,id必須起名字爲“messageSource”!!!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章