Spring二 Bean詳解


Bean詳解

Spring框架的本質其實是:通過XML配置來驅動Java代碼,這樣就可以把原本由java代碼管理的耦合關系,提取到XML配置文件中管理。這樣就實現了系統中各組件的解耦,有利於后期的升級和維護。
1.Bean的基本定義和Bean別名
<beans>元素是Spring配置文件的根元素,該元素可以指定如下屬性:
default-lazy-init:指定<beans>元素下配置的所有bean默認的延遲初始化行為
default-merge:指定<beans>元素下配置的所有bean默認的merge行為
default-autowire:指定<beans>元素下配置的所有bean默認的自動裝配行為
default-init-method:指定<beans>元素下配置的所有bean默認的初始化方法
default-destroy-method:指定<beans>元素下配置的所有bean默認的回收方法
default-autowire-candidates:指定<beans>元素下配置的所有bean默認是否作為自動裝配的候選Bean
<beans>元素下所指定的屬性都可以在每個<bean>子元素中指定,只要將屬性名去掉default即可,這樣就可以對特定bean起作用。
定義Bean時,通常要指定兩個屬性:
id:確定該Bean的唯一標識,Bean的id屬性在Spring容器中應該是唯一的。
class:指定該Bean的具體實現類,這里不能是接口,因為在通常情況下,Spring會直接使用new關鍵字創建該bean的實例。
2.Bean的作用域
Spring支持5種作用域:
singleton:單例模式,在整個Spring IoC容器中,singleton作用域的Bean將只生成一個實例。
prototype:每次通過容器的getBean()方法獲取prototype作用域的Bean時,都將產生一個新的Bean實例。
request、session:對於一次HTTP請求,request/session作用域的Bean將只產生一個實例,這意味着,在同一次HTTP請求內,程序每次請求該Bean,得到的總是同一個實例。只有在Web應用中使用Spring,該作用域才真正有效。
global session:每個全局的HTTP Session對應一個Bean實例,在典型的情況下,僅在使用portlet context的時候有效。只有在Web應用中使用Spring,該作用域才真正有效。
注意:如果一個Bean被設置成prototype作用域,程序每次請求該id的Bean,Spring都會新建一個Bean實例,然后返回程序。在這種情況下,Spring容器僅僅使用new關鍵字創建Bean實例,一旦創建成功,容器就不會再跟蹤實例,也不會去維護bean實例的狀態。
Java在創建Java實例時,需要進行內存申請,銷毀實例時,要進行垃圾回收,這些工作會增加系統開銷。因此,使用prototype作用域的Bean的創建、銷毀代價比較大。而singleton作用域的Bean實例一旦創建成功,就可以重復使用。
例如:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
//創建一個singleton Bean實例
<bean id="p1" class="mySpring.Person" />
//創建一個prototype Bean實例
<bean id="p2" class="mySpring.Person" scope="prototype"/>
<bean id="date" class="java.util.Date" />
</beans>

 


測試:

public class myTest {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("a/ApplicationContext.xml");
System.out.println(ctx.getBean("p1")==ctx.getBean("p1"));
System.out.println(ctx.getBean("p2")==ctx.getBean("p2"));
System.out.println(ctx.getBean("date"));
Thread.sleep(1000);
System.out.println(ctx.getBean("date"));
}
}

結果是: true false Sun May 11 14:39:03 CST 2014 Sun May Sun May 11 14:39:03 CST 2014
request和session作用域只在Web應用中有效,並且要在Web應用中增加額外配置才會生效,因此必須要將HTTP請求對象綁定到為該請求提供服務的線程上,這樣才能使得具有request和session作用域的Bean實例能夠在后面的調用鏈中被訪問到。對於支持Servlet2.4及更新規范的Web容器,可以在Web應用的web.xml文件中添加如下的Listener配置:
在WEB-INF\web.xml文件中:
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
JSP腳本:
<body>
<%
WebApplicationContext ctx=WebApplicationContextUtils.getWebApplicationContext(application);
Perosn p1=(Perosn)ctx.getBean("perosn");
Perosn p2=(Perosn)ctx.getBean("perosn");
out.println((p1==p2)+"<br />");
out.println(p1);
%>
</body>
結果:刷新頁面依舊輸出true,但是兩次的Perosn Bean不一樣。
3.配置依賴
不管是設值注入,還是構造注入,都視為Bean依賴,接受Spring容器管理,依賴關系的值要么是一個確定的值,要么是Spring容器中其他Bean的引用。通常不建議使用配置文件管理Bean的基本類型的屬性值,通常只使用配置文件管理容器中Bean與Bean之間的依賴關系。
創建BeanFactory時不會立即創建Bean實例,所以有可能程序可以正確創建BeanFactory實例,但當請求Bean實例時依然拋出一個異常,創建Bean實例或注入它的依賴關系時出現錯誤。配置錯誤的延遲出現,也會給系統帶來不安全因素。ApplicationContext實例化過程的時間和內存開銷大,但可以在容器初始化階段就檢驗出配置錯誤。
Java類的成員變量可以是各種可以是各種數據類型,除了基本類型值、字符串類型值等,還可以是其他Java實例,也可以是容器中的其他Bean實例,甚至是Java集合、數組等,所以Spring允許通過如下元素為setter方法、構造器參數指定參數值:value,ref,bean,list,set,map及props。
(1)設置普通屬性值
<value/>元素用於指定基本類型及其包裝、字符串類型的參數值,Spring使用XML解析器來解析出這些數據,然后利用java.beans.PropertyEditor完成類型轉換:從java.lang.String類型轉換為所需的參數值類型。如果目標類型是基本類型及其包裝類,通常可以正確轉換。
如:
<bean id="exampleBean" class="mySpring.ExampleBean">
//指定int型的參數值
<property name="field1" value="1" />
//指定double型的參數值
<property name="field2" value="2.3" />
</bean>
(2)配置合作者Bean
如果需要為Bean設置的屬性值是容器中的另一個Bean實例,則要用<ref />元素。使用<ref/>元素時可指定一個bean屬性,該屬性用於引用容器中其他Bean實例的id屬性值。
如:
Person.java

public class Person {
private String name;
private int age;
private Car car;
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public Person(String name, int age, Car car) {
super();
this.name = name;
this.age = age;
this.car = car;
}
public Person() {
super();
}
}

Car.java

public class Car {    
private String brand;
private String corp;
private double price;
private int maxSpeed;
public Car(String brand, String corp, double price) {
super();
this.brand = brand;
this.corp = corp;
this.price = price;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price
+ ", maxSpeed=" + maxSpeed + "]";
}
}

ApplicationContext.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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="car" class="a.Car">
<constructor-arg value="BaoMa" type="java.lang.String"></constructor-arg>
<constructor-arg value="ShangHai" type="java.lang.String"></constructor-arg>
<constructor-arg value="240" type="int"></constructor-arg>
</bean>
<bean id="person" class="a.Person">
<property name="name" value="Tom"></property>
<property name="age" value="20"></property>
<property name="car" ref="car"></property> //也可以寫為<property name="car"><ref bean="car" /></property>
</bean>
</beans>

測試:

public class myTest {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("a/ApplicationContext.xml");
Person person=ctx.getBean(Person.class);
System.out.println(person);
}
}

結果:Person [name=Tom, age=20, car=Car [brand=BaoMa, corp=ShangHai, price=0.0, maxSpeed=240]]
(3)使用自動裝配注入合作者Bean
Spring能自動裝配Bean與Bean之間的依賴關系,即無須使用ref顯式指定依賴Bean,而是由Spring容器檢查XML配置文件內容,根據某種規則,為調用者Bean注入被依賴的Bean。
Spring的自動裝配可通過<beans>元素的default-autowire屬性指定,該屬性對配置文件中所有的Bean起作用;也可以通過<bean>元素的autowire屬性指定,該屬性只對該Bean起作用。default-autowire、autowire屬性可以接受如下值:
no:不使用自動裝配,Bean依賴必須通過ref元素定義,在較大的部署環境下不鼓勵這個配置,顯示配置合作者能得到更清晰的依賴關系。
byName:根據setter方法名進行自動裝配。Spring容器查找容器中的全部Bean,找出其id與setter方法名去掉set前綴,並小寫首字母后同名的Bean來完成注入。如果沒有找到匹配的Bean,則不注入。
byType:根據setter方法的形參類型來自動裝配。Spring容器查找容器中的全部Bean,如果正好有一個Bean類型與setter方法的形參類型匹配,就自動注入這個Bean;如果找到多個,就拋出一個異常;如果沒找到,就什么也不會發生。
具體使用:
byName規則
<bean id="chinese" class="mySpring.Chinese" autowire="byName" />
<bean id="gunDog" class="mySpring.GunDog">
<property name="name" value="wangwang" />
</bean>
在Chinese類中一定會有如下setter方法:
public void setGunDog(Dog dog){
this.dog=dog;
}
Spring容器會尋找id為gunDog的Bean,如果能找到,該Bean就會作為調用setGunDog()方法的參數。
byType規則
<bean id="chinese" class="mySpring.Chinese" autowire="byType" />
<bean id="gunDog" class="mySpring.GunDog">
<property name="name" value="wangwang" />
</bean>
在Chinese類中一定會有如下setter方法:
public void setGunDog(Dog dog){
this.dog=dog;
}
上面setter方法的形參類型是Dog,而mySpring.GunDog Bean類實現了Dog接口,但是如果還有一個類同時也實現了Dog接口的話,如下:
<bean id="gunDog" class="mySpring.PetDog">
<property name="name" value="ohoh" />
</bean>
由於容器中有兩個類型為Dog的Bean,Spring無法確定應為chinese Bean注入哪個Bean,則程序會拋出異常。
當一個Bean既使用自動裝配依賴,又使用ref顯式指定依賴時,則顯示指定的依賴會覆蓋掉自動裝配依賴。
(4)注入嵌套Bean
如果某個Bean所依賴的Bean不想被Spring容器直接訪問,則可以使用嵌套Bean。這樣的Bean稱為內部Bean,不能被外部Bean引用,只能在內部使用,這里的bean不用id。
<bean id="person" class="a.Person">
<property name="name" value="Tom"></property>
<property name="age" value="20"></property>
<property name="car">
<bean class="a.Car">
<constructor-arg value="Ford"></constructor-arg>
<constructor-arg value="Changan"></constructor-arg>
<constructor-arg value="200000"></constructor-arg>
</bean>
</property>
</bean>
當形參類型是基本類型、String、日期等,直接使用value指定字面值即可;但形參類型是復合類(如Person、DataSource等),那就需要傳入一個Java對象作為實參,主要有兩種方法:使用ref引用一個容器中已經配置的Bean,或者使用<bean>元素配置一個嵌套Bean;形參類型是Set、List、Map等集合或者是數組類型時,要進行如下配置。
(5)注入集合值
集合元素<list>,<set>,<map>,<props>的具體設置
Address.java

public class Address{
private String city;
private String street;
...//此處省略getter/setter方法及其他方法
}

Car.java

public class Car {    
private String brand;
private String corp;
private double price;
...//此處省略getter/setter方法及其他方法
}

Person.java

public class Person {
    private List<Car> cars;
    private List<String> schools;
    private Map scores;
    private Map<String,Car> cars1;
    private Properties health;
    private String[] books;
    ...//此處省略getter/setter方法及其他方法
}

ApplicationContext.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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="car1" class="SpringTest.Car">
    <property name="brand" value="Ford"></property>
    <property name="corp" value="Changan"></property>
    <property name="price" value="200000"></property>
    </bean>
    
    <bean id="car2" class="SpringTest.Car">
    <property name="brand" value="BaoMa"></property>
    <property name="corp" value="ShangHai"></property>
    <property name="price" value="240000"></property>
    </bean>
    
    <bean id="person" class="SpringTest.Person">
    <property name="cars">
            <list>
                <ref bean="car1"></ref>
                <ref bean="car2"></ref>
            </list>
        </property>
        
    <property name="schools">
        <list>
            <value>小學</value>
            <value>中學</value>
            <value>大學</value>
        </list>
    </property>
    
    <property name="scores">
        <map>
            <entry key="數學" value="90" />
            <entry key="語文" value="90" />
            <entry key="英語" value="90" />
        </map>
    </property>
    
    <property name="cars1">
            <map>
                <entry key="AA" value-ref="car1"></entry>
                <entry key="BB" value-ref="car2"></entry>
            </map>
        </property>
        
    <property name="health">
        <props>
            <prop key="血壓">正常</prop>
            <prop key="身高">175</prop>
        </props>
    </property>
    
    <property name="books">
        <list>
            <value>java學習</value>
            <value>javascript學習</value>
            <value>javaEE學習</value>
        </list>
    </property>
    </bean>
</beans>

測試:

public class MainTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("SpringTest/ApplicationContext.xml");        
        Person p=(Person)ctx.getBean("person");
        System.out.println(p);
        ctx.close();
    }
}

結果:
Person [cars=[Car [brand=Ford, corp=Changan, price=200000.0], Car [brand=BaoMa, corp=ShangHai, price=240000.0]], schools=[小學, 中學, 大學], scores={數學=90, 語文=90, 英語=90}, cars1={AA=Car [brand=Ford, corp=Changan, price=200000.0], BB=Car [brand=BaoMa, corp=ShangHai, price=240000.0]}, health={血壓=正常, 身高=175}, books=[java學習, javascript學習, javaEE學習]]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM