前言
Spring最基礎的功能就是一個bean工廠,所以本文講解的是Spring生成bean的種種方法及細節,Spring配置檔案的名字是bean.xml,定義幾個類:
一個Person類:
public class Person { private String personName; // 人的名字 private int personAge; // 人的年齡 public Person(String personName, int personAge) { this.personName = personName; this.personAge = personAge; } public String getPersonName() { return personName; } public void setPersonName(String personName) { this.personName = personName; } public int getPersonAge() { return personAge; } public void setPersonAge(int personAge) { this.personAge = personAge; } public String toString() { return "personName = " + personName + ", personAge = " + personAge; } }
一個Family類,裡面持有Person的引用:
public class Family { private Person person; private String familyName; public Family(Person person, String familyName) { this.person = person; this.familyName = familyName; } public String toString() { return person.toString() + ", familyName = " + familyName; } }
一個單例類:
public class SingletonClass { private SingletonClass instance = new SingletonClass(); private SingletonClass(){} public SingletonClass getInstance() { return instance; } }
一個空的類,為了測試初始化和銷燬用的:
public class EmptyClass { static { System.out.println("Enter EmptyClass.static block"); } public EmptyClass() { System.out.println("Enter EmptyClass.construct()"); } public void init() { System.out.println("Enter EmptyClass.init()"); } public void destory() { System.out.println("Enter EmptyClass.destory()"); } }
一個集合類,為了演示集合注入:
public class CollectionClass { private List<String> list; private Map<Family, Person> map; private int[] ints; public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } public Map<Family, Person> getMap() { return map; } public void setMap(Map<Family, Person> map) { this.map = map; } public int[] getInts() { return ints; } public void setInts(int[] ints) { this.ints = ints; } }
最簡單的bean例項化
bean.xml中注入這個bean,以Person類為例:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"> <bean id="person" class="com.xrq.bean.Person" /> </beans>
main函式這麼寫:
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); Person person1 = (Person)ctx.getBean("person"); Person person2 = (Person)ctx.getBean("person"); System.out.println(person1 == person2); }
執行結果為true,也就是說Spring預設以單例的形式給開發者構造出指定的bean。另外有兩點要注意:
1、同一個spring.xml中不可以定義兩個id相同的bean
2、ClassPathXmlApplicationContext中有一個可變長度的建構函式,用於載入多個.xml中的bean,如果bean中有id相同,那麼id相同的bean,後載入的會覆蓋先載入的。
bean的作用域及生命週期
程式碼不動,把bean.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"> <bean id="person" class="com.xrq.bean.Person" scope="prototype" lazy-init="true"/> </beans>
這裡出現了兩個屬性,scope和lazy-init:
1、scope表示的是bean的作用域,有prototype、request、session、singleton四種,其中singleton是預設的,表示單例。prototype表示每次建立都會產生一個bean例項。request和session只在web專案中才會用,其作用域就和web中的request和session一樣
2、lazy-init表示的是bean的生命週期,預設為false。當scope=singleton時,bean會在裝在配置檔案時例項化,如果希望bean在產生時才例項化,可以把lazy-init設定為true。當scope=prototype時,在產生bean時才會例項化它。補充一點,如果希望該配置檔案中所有的bean都延遲初始化,則應該在beans根節點中使用lazy-init="true"
三種注入方式
所謂注入即注入bean中的屬性,Spring為使用者提供了三種注入方式,settter注入、構造方法注入、註解注入,不過對於Spring的講解,不講註解,所以看一下前兩種注入方式:
1、setter注入,bean.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"> <bean id="person" class="com.xrq.bean.Person"> <property name="personName" value="Alice"/> <property name="personAge" value="10" /> </bean> </beans>
2、構造方法注入,bean.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"> <bean id="family" class="com.xrq.bean.Family"> <constructor-arg name="person" ref="person" /> <constructor-arg name="familyName" value="friendly" /> </bean> <bean id="person" class="com.xrq.bean.Person"> <property name="personName" value="Alice"/> <property name="personAge" value="10" /> </bean> </beans>
這裡故意把family的定義寫在person的定義上面,說明即使前面的beanA持有beanB的引用,把beanA定義在beanB前面也不影響
集合注入
spring對於集合屬性的支援非常好,以CollectionClass為例,看下如何配置bean.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"> <bean id="collectionClass" class="com.xrq.bean.CollectionClass"> <property name="list"> <list> <value>111</value> <value>222</value> </list> </property> <property name="map"> <map> <entry key="111"> <bean class="com.xrq.bean.Person"> <property name="personName" value="Mike"/> <property name="personAge" value="11" /> </bean> </entry> </map> </property> <property name="ints"> <array> <value>333</value> <value>444</value> </array> </property> </bean> </beans>
工廠方式生成類
Spring雖然可以指定bean以單例的方式生成出來,但是每次都要用getBean方法獲取類的例項非常麻煩,有辦法像單例模式一樣使用XXX.getInstance()就好了,不過還真有,以SingletonClass作為例子:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"> <bean id="singletonClass" class="com.xrq.bean.SingletonClass" factory-method="getInstance"> </bean> </beans>
這樣,我們就可以使用單例的方式去呼叫這個類了,如果類裡面有一些私有屬性,還可以注入的方式在生成這個bean的時候就注入進去,非常方便
init-method和destory-method
有時候我們希望,在某個bean載入的時候做一些事情,銷燬的時候做一些事情(是不是想到了Servlet),所以我們可以自定義初始化和銷燬的方法EmptyClass這個類,bean.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"> <bean id="emptyClass" class="com.xrq.bean.EmptyClass" init-method="init" destroy-method="destory"/> </beans>
注意兩點:
1、例項化類的時候,幾個方法的載入順序為靜態資源->構造方法->初始化方法
2、觸發destory()方法的呼叫可以使用"((ClassPathXmlApplicationContext)ctx).close();",注意scope="prototype"是不會觸發destory()的,沒有為什麼,設計就是這樣
父子類繼承關係
有時候我們有要求,一個類是某一個類/抽象類的子類,可以這麼做:
public abstract class AbstractClass { }
public class ImplClass extends AbstractClass { }
此時bean.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd"> <bean id="abstractClass" class="com.xrq.bean.abstractClass" abstract="true"/> <bean id="implClass" class="com.xrq.bean.ImplClass" parent="abstractClass" /> </beans>
注意這種寫法對介面也有效。