Spring管理Bean-IOC-02 2.基於XML配置bean 2.7通過util空間名稱創建list BookStore.java: package com.li.bean; import java.util.List; /** * @author 李 * @version 1.0 */ pu ...
Spring管理Bean-IOC-02
2.基於XML配置bean
2.7通過util空間名稱創建list
BookStore.java:
package com.li.bean;
import java.util.List;
/**
* @author 李
* @version 1.0
*/
public class BookStore {
private List<String> bookList;
//如果類中沒有其他構造器,預設構造器可以不寫
// 如果有其他構造器,則必須顯示定義無參構造器
public BookStore() {
}
public List<String> getBookList() {
return bookList;
}
public void setBookList(List<String> bookList) {
this.bookList = bookList;
}
@Override
public String toString() {
return "BookStore{" +
"bookList=" + bookList +
'}';
}
}
如果有多個BookStore對象,list的內容都一樣,按照之前的做法,每一個list都要寫上相同的value值。但使用util命名空間可以達到數據復用的效果。
beans.xml:
<!--定義util:list,並指定id,可以達到數據復用的效果-->
<util:list id="myBookList">
<value>三國演義</value>
<value>紅樓夢</value>
<value>西游記</value>
<value>水滸傳</value>
</util:list>
<!--配置BookStores對象-->
<bean class="com.li.bean.BookStore" id="bookStore">
<property name="bookList" ref="myBookList"/>
</bean>
註意引入util命名空間,一般來說ide會有提示,如果沒有則按如下引入:
SpringBeanTest:
package com.li.test;
import com.li.bean.BookStore;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;
/**
* @author 李
* @version 1.0
*/
public class SpringBeanTest {
//使用util:list名稱空間給屬性賦值
@Test
public void setBeanByUtilList() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
BookStore bookStore = ioc.getBean("bookStore", BookStore.class);
System.out.println("bookStore=" + bookStore);
}
}
2.8級聯屬性賦值
-
案例說明:spring的ioc容器,可以直接給對象屬性的屬性賦值,即級聯屬性賦值
-
完成步驟:
(1)創建Dept.java和Emp.java
(2)配置beans.xml
Dept.java:
package com.li.bean;
/**
* @author 李
* @version 1.0
* 部門類
*/
public class Dept {
private String name;
public Dept() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dept{" +
"name='" + name + '\'' +
'}';
}
}
Emp.java:
package com.li.bean;
/**
* @author 李
* @version 1.0
* 員工類
*/
public class Emp {
private String name;
private Dept dept;
public Emp() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"name='" + name + '\'' +
", dept=" + dept +
'}';
}
}
beans.xml:
<!--配置Dept對象-->
<bean class="com.li.bean.Dept" id="dept"/>
<!--配置Emp對象-->
<bean class="com.li.bean.Emp" id="emp">
<property name="name" value="jack"/>
<property name="dept" ref="dept"/>
<!--這裡我希望給dept的name屬性賦值[級聯屬性賦值]-->
<property name="dept.name" value="Java開發部門"/>
</bean>
SpringBeanTest:
package com.li.test;
import com.li.bean.Emp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;
/**
* @author 李
* @version 1.0
*/
public class SpringBeanTest {
//使用級聯賦值給屬性的屬性賦值
@Test
public void setBeanByRelation() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Emp emp = ioc.getBean("emp", Emp.class);
System.out.println("emp=" + emp);
}
}
2.9通過靜態工廠獲取對象
-
在spring的ioc容器,可以通過靜態工廠獲取bean對象
-
完成步驟:
(1)靜態工廠類MyStaticFactory類和Monster類
(2)配置beans.xml
Monster類詳見上一篇--2.1通過類型來獲取bean
MyStaticFactory.java:
package com.li.factory;
import com.li.bean.Monster;
import java.util.HashMap;
import java.util.Map;
/**
* @author 李
* @version 1.0
* 靜態工廠類-可以返回Monster對象
*/
public class MyStaticFactory {
private static Map<String, Monster> monsterMap;
//使用static靜態代碼塊進行初始化
// (靜態代碼塊在類載入的同時就直接執行,且只執行一次)
static {
monsterMap = new HashMap<>();
monsterMap.put("monster01", new Monster(100, "孫悟空", "七十二變"));
monsterMap.put("monster02", new Monster(200, "金蟬子", "普渡眾生"));
}
//提供一個方法,返回monster對象
public static Monster getMonster(String key) {
return monsterMap.get(key);
}
}
beans.xml:
<!--配置monster,通過靜態工廠獲取
1.通過靜態工廠配置bean
2.class不再是monster的路徑,而是靜態工廠的全路徑
3.factory-method 表示指定靜態工廠的哪個方法來返回對象
4.constructor-arg value="monster02" 指定要返回靜態工廠的哪個對象
-->
<bean id="my_monster01" class="com.li.factory.MyStaticFactory" factory-method="getMonster">
<constructor-arg value="monster02"/>
</bean>
SpringBeanTest:
package com.li.test;
import com.li.bean.Monster;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;
/**
* @author 李
* @version 1.0
*/
public class SpringBeanTest {
//使用靜態工廠來獲取 bean
@Test
public void getBeanByStaticFactory() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster my_monster01 = ioc.getBean("my_monster01", Monster.class);
//這裡如果使用的是相同id獲取bean,那麼獲取的實際上是同一個對象,例如:
Monster my_monster001 = ioc.getBean("my_monster01", Monster.class);
// 因為靜態工廠中的靜態代碼塊只執行一次,這裡不同的對象引用指向的都是同一個對象
System.out.println(my_monster01 == my_monster001);//true
System.out.println("my_monster01=" + my_monster01);
}
}
2.10通過實例工廠獲取對象
Monster類詳見上一篇--2.1通過類型來獲取bean
MyInstanceFactory:
package com.li.factory;
import com.li.bean.Monster;
import java.util.HashMap;
import java.util.Map;
/**
* @author 李
* @version 1.0
* 實例工廠類
*/
public class MyInstanceFactory {
private Map<String, Monster> monster_map;
//通過普通代碼塊進行初始化
//普通代碼塊每創建一個對象就會執行一次
{
monster_map = new HashMap<>();
monster_map.put("monster03", new Monster(300, "豬八戒", "九尺釘耙"));
monster_map.put("monster04", new Monster(400, "沙和尚", "丈二禪杖"));
}
//寫一個方法返回Monster對象
public Monster getMonster(String key) {
return monster_map.get(key);
}
}
beans.xml:
<!--因為是實例工廠對象,所以需要配置才能使用(靜態工廠可以直接使用不用配置)-->
<bean class="com.li.factory.MyInstanceFactory" id="myInstanceFactory"/>
<bean class="com.li.factory.MyInstanceFactory" id="myInstanceFactory02"/>
<!--配置monster,通過實例工廠獲取
1.factory-bean 表示使用哪個實例工廠對象返回bean
2.factory-method 指定使用實例工廠對象的哪個方法返回bean
3.constructor-arg value="monster03" 指定獲取實例工廠中的哪個對象
-->
<bean id="my_monster02" factory-bean="myInstanceFactory" factory-method="getMonster">
<constructor-arg value="monster03"/>
</bean>
<bean id="my_monster03" factory-bean="myInstanceFactory02" factory-method="getMonster">
<constructor-arg value="monster03"/>
</bean>
SpringBeanTest:
package com.li.test;
import com.li.bean.Monster;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;
/**
* @author 李
* @version 1.0
*/
public class SpringBeanTest {
//使用實例工廠來獲取 bean
@Test
public void getBeanByInstanceFactory() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster my_monster02 = ioc.getBean("my_monster02", Monster.class);
System.out.println("my_monster02=" + my_monster02);
// 這裡使用相同的id獲取bean,那麼返回的對象也是同一個!!
Monster my_monster002 = ioc.getBean("my_monster02", Monster.class);
// 因為雖然是實例工廠,但是兩次 ioc.getBean("my_monster02", Monster.class)使用的都是
// 同一個工廠實例(id="myInstanceFactory"),返回的自然也就是同一個實例工廠中的 monster
System.out.println(my_monster02 == my_monster002);//true
//換而言之,如果不是同一個實例工廠,那麼返回的就不是同一個對象了
//(這裡的my_monster03,在beans.xml使用的是 id=myInstanceFactory02的實例工廠)
Monster my_monster03 = ioc.getBean("my_monster03", Monster.class);
System.out.println("my_monster03=" + my_monster03);
System.out.println(my_monster02 == my_monster03);//false
}
}
2.11通過FactoryBean獲取對象(重點)
在spring的ioc容器,通過FactoryBean獲取bean對象
MyFactoryBean:
package com.li.factory;
import com.li.bean.Monster;
import org.springframework.beans.factory.FactoryBean;
import java.util.HashMap;
import java.util.Map;
/**
* @author 李
* @version 1.0
* FactoryBean
*/
public class MyFactoryBean implements FactoryBean<Monster> {
//這個就是你配置的時候,指定要獲取的對象對應的key
private String key;
private Map<String, Monster> monster_map;
//代碼塊完成初始化
{
monster_map = new HashMap<>();
monster_map.put("monster05", new Monster(5, "黑風怪", "翻江倒海"));
monster_map.put("monster06", new Monster(6, "金角大王", "超能力"));
}
public void setKey(String key) {
this.key = key;
}
@Override
public Monster getObject() throws Exception {
return monster_map.get(key);
}
@Override
public Class<?> getObjectType() {
return Monster.class;
}
@Override
public boolean isSingleton() {//這裡指定是否是單例對象
return true;
}
}
beans.xml:
<!--配置monster對象,通過FactoryBean來獲取
1.這裡的class 指定要使用的FactoryBean
2.key是 你設置的FactoryBean的屬性key
3.value 就是你要獲取的對象的對應 key
-->
<bean id="my_monster05" class="com.li.factory.MyFactoryBean">
<property name="key" value="monster05"/>
</bean>
SpringBeanTest:
package com.li.test;
import com.li.bean.Monster;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;
/**
* @author 李
* @version 1.0
*/
public class SpringBeanTest {
//通過FactoryBean獲取bean對象
@Test
public void getBeanByFactoryBean() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster my_monster05 = ioc.getBean("my_monster05", Monster.class);
System.out.println("my_monster05=" + my_monster05);
}
}
2.12bean配置信息重用(繼承)
在spring的ioc容器中,提供了一種繼承的方式來實現bean配置信息的重用
beans.xml:
<!--配置Monster對象-->
<bean class="com.li.bean.Monster" id="monster10">
<property name="monsterId" value="10"/>
<property name="name" value="蜈蚣精"/>
<property name="skill" value="蜇人"/>
</bean>
<!--現在配置另一個Monster對象,
1.這個對象的屬性值 和 id="monster10"對象屬性一樣
2.parent="monster10" 指定當前這個配置的對象的屬性值從id=monster10的對象來
-->
<bean id="monster11" class="com.li.bean.Monster" parent="monster10"/>
SpringBeanTest:
package com.li.test;
import com.li.bean.Monster;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;
/**
* @author 李
* @version 1.0
*/
public class SpringBeanTest {
//配置Bean(通過繼承)
@Test
public void getBeanByFactoryBean() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster10 = ioc.getBean("monster10", Monster.class);
Monster monster11 = ioc.getBean("monster11", Monster.class);
System.out.println("monster10=" + monster10);
System.out.println("monster11=" + monster11);
}
}
註意:
-
如果bean指定了abstract="true" 表示該bean對象是只用於被繼承的
-
那麼這個bean就不能被獲取/實例化
此時如果輸出monster10,就會顯示錯誤:
2.13bean創建順序
在spring的ioc容器,預設是按照配置的順序創建bean對象。比如:
<bean id="student01" class="com.hspedu.bean.Student" />
<bean id="department01" class="com.hspedu.bean.Department" />
會先創建 student01 這個 bean 對象,然後創建 department01 這個 bean 對象
但如果這樣配置:
<bean id="student01" class="com.hspedu.bean.Student" depends-on="department01"/>
<bean id="department01" class="com.hspedu.bean.Department" />
會先創建 department01 對象,再創建 student01 對象.
例子
Student:
package com.li.bean;
/**
* @author 李
* @version 1.0
*/
public class Student {
public Student() {
System.out.println("Student 構造器被執行...");
}
}
Department:
package com.li.bean;
/**
* @author 李
* @version 1.0
*/
public class Department {
public Department() {
System.out.println("Department 構造器被執行...");
}
}
beans.xml:
<!--測試 bean對象的創建順序-->
<bean id="student01" class="com.li.bean.Student"/>
<bean id="department01" class="com.li.bean.Department"/>
測試類:
//測試 Bean的創建順序
@Test
public void testBeanByCreate() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
System.out.println("ok~");
}
如果我們在配置student01對象時,加上depends-on="department01"
,spring就會認為你的student01對象時依賴於department01對象,會先創建department01對象。
<bean id="student01" class="com.hspedu.bean.Student" depends-on="department01"/>
<bean id="department01" class="com.hspedu.bean.Department" />
總結:在預設情況下,bean創建的順序是按照預設配置順序來的。但是如果我們在對象配置中指定了依賴對象,就會先創建被依賴的對象。
一個問題?
1.先看下麵的配置,請問兩個bean創建的順序是什麼?並分析執行流程
<!--配置MemberDAOImpl對象-->
<bean class="com.li.dao.MemberDAOImpl" id="memberDAOImpl"/>
<bean class="com.li.service.MemberServiceImpl" id="memberServiceImpl">
<property name="memberDAO" ref="memberDAOImpl"/>
</bean>
(1)先創建 id=memberDaoImpl 的對象
(2)再創建 id=memberServiceImpl 的對象
(3)調用 memberServiceImpl.setMemberDAO() 方法,完成引用
2.先看下麵的配置,請問兩個bean創建的順序是什麼?並分析執行流程
<bean class="com.li.service.MemberServiceImpl" id="memberServiceImpl">
<property name="memberDAO" ref="memberDAOImpl"/>
</bean>
<!--配置MemberDAOImpl對象-->
<bean class="com.li.dao.MemberDAOImpl" id="memberDAOImpl"/>
(1)先創建 id=memberServiceImpl 的對象
(2)再創建 id=memberDaoImpl 的對象
(3)調用 memberServiceImpl.setMemberDAO() 方法,完成引用
總結:ioc容器會把整個bean的創建當做一個整體來對待,會把配置文件中所有對象bean先創建好,然後才完成對象間的引用。
見2.4
2.14bean對象的單例和多例
在spring的ioc容器中,預設是按照單例創建的,即配置一個bean對象後,ioc容器只會創建一個bean實例。
如果希望ioc容器配置的某個bean對象,是以多個實例形式創建的,可以通過配置scope="prototype"來指定。
例子1-單例對象
Cat:
package com.li.bean;
/**
* @author 李
* @version 1.0
*/
public class Cat {
private Integer id;
private String name;
public Cat() {
System.out.println("Cat() 構造器被執行...");
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
beans.xml:
<!--配置 Cat對象
1.預設情況下,scope屬性的值為 "singleton",即ioc容器中只會有一個這樣的bean對象
當執行getBean時,返回的是同一個對象
2.如果希望每次使用getBean都返回新的Bean對象,就要把scope的屬性設為 prototype
-->
<bean id="cat" class="com.li.bean.Cat">
<property name="id" value="10"/>
<property name="name" value="小花貓"/>
</bean>
測試類:
//測試Scope
@Test
public void testBeanScope() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Cat cat1 = ioc.getBean("cat", Cat.class);
Cat cat2 = ioc.getBean("cat", Cat.class);
Cat cat3 = ioc.getBean("cat", Cat.class);
System.out.println("cat1="+cat1);
System.out.println("cat2="+cat2);
System.out.println("cat3="+cat3);
}
輸出如下:地址值相同,證明這三個對象引用都指向了同一個對象
例子2-多例對象
現在我們把例子1的cat對象的配置改為scope="prototype"
<bean id="cat" class="com.li.bean.Cat" scope="prototype">
<property name="id" value="10"/>
<property name="name" value="小花貓"/>
</bean>
輸出如下:構造器執行了三次,說明創建了三個Cat對象(對象的地址值也不一樣)
使用細節
-
當bean為scope="singleton"(預設值),在啟動容器時,就會創建單例對象,並放入singletonObjects集合
-
當bean設置為scope="prototype",即設置為多實例機制後,該bean是在getBean時才創建
-
如果是單例singleton,但又希望在getBean時才創建,可以指定懶載入lazy-init="true" (預設值是false)
-
通常情況下,lazy-init 使用預設值false,因為在開發看來,用空間換時間是值得的,除非有特殊要求。
-
如果scope="prototype",這時你的lazy-init屬性值不管設置為什麼,都預設為true
因為多例情況下,spring無法知道創建幾個對象,因此只有在用到的時候才能創建