Spring知識點總結(二)之Spring IOC
1.建立bean類,並在spring中進行配置交由spring來管理1. IOC(DI) - 控制反轉(依賴注入)
所謂的IOC稱之為控制反轉,簡單來說就是將物件的建立的權利及物件的生命週期的管理過程交由Spring框架來處理,從此在開發過程中不再需要關注物件的建立和生命週期的管理,而是在需要時由Spring框架提供,這個由spring框架管理物件建立和生命週期的機制稱之為控制反轉。而在 建立物件的過程中Spring可以依據配置對物件的屬性進行設定,這個過稱之為依賴注入,也即DI。
2. IOC的入門案例
a. 下載Spring
訪問Spring官網,下載Spring相關的包
b. 解壓下載好的壓縮包
其中包含著Spring的依賴包
c. 建立一個java專案
spring並不是非要在javaweb環境下才可以使用,一個普通的java程式中也可以使用Spring。
d.匯入Spring的libs目錄下IOC相關的jar包
e. 建立Spring的配置檔案
Spring採用xml檔案作為配置檔案,xml檔名字任意,但通常都取名為applicationContext.xml,通常將該檔案放置在類載入的目錄裡下 (src目錄),方便後續使用。
f.建立bean類,並在spring中進行配置交由spring來管理
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> 6 7 <bean id="person" class="cn.tedu.beans.Person"></bean> 8 9 </beans>
g. 在程式中通過Spring容器獲取物件並使用
1 public class Person_Test { 2 @Test 3 /** 4 * SpringIOC的入門案例 5 */ 6 public void test1(){ 7 ApplicationContext context = 8 new ClassPathXmlApplicationContext("applicationContext.xml"); 9 Person p = (Person) context.getBean("person"); 10 p.say(); 11 } 12 }
3. IOC的實現原理
在初始化一個Spring容器時,Spring會去解析指定的xml檔案,當解析到其中的<bean>標籤時,會根據該標籤中的class屬性指定的類的全路徑名,通過反射建立該類的物件,並將該物件存入內建的Map中管理。其中鍵就是該標籤的id值,值就是該物件。
之後,當通過getBean方法來從容器中獲取物件時,其實就是根據傳入的條件在內建的Map中尋找是否有匹配的鍵值,如果有則將該鍵值對中儲存的物件返回,如果沒有匹配到則丟擲異常。
由此可以推測而知:
預設情況下,多次獲取同一個id的bean,得到的將是同一個物件。
即使 是同一個類,如果配置過多個<bean>標籤具有不同的id,每個id都會在內建Map中有一個鍵值對,其中的值是這個類建立的不同的物件
同一個<beans>標籤下不允許配置多個同id的<bean>標籤,如果配置則啟動拋異常
4. IOC獲取物件的方式
通過context.getBeans()方法獲取bean時,可以通過如下兩種方式獲取:
傳入id值
傳入class型別
通過class方式獲取bean時,如果同一個類配置過多個bean,則在獲取時因為無法確定到底要獲取哪個bean會丟擲異常。
而id是唯一的,不存在這樣的問題,所以建議大家儘量使用id獲取bean。
1 @Test 2 /** 3 * SpringIOC獲取bean的方式 4 */ 5 public void test3(){ 6 /* 7 <bean id="person" class="cn.tedu.beans.Person"></bean> 8 */ 9 /* 10 <bean id="person" class="cn.tedu.beans.Person"></bean> 11 <bean id="personx" class="cn.tedu.beans.Person"></bean> 12 */ 13 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 14 //--通過id獲取 15 Person p1 = (Person) context.getBean("person"); 16 p1.say(); 17 //--通過class獲取 18 Person p2 = context.getBean(Person.class); 19 p2.say(); 20 }
5. 別名標籤
在 Spring中提供了別名標籤<alias>可以為配置的<bean>起一個別名,要注意的是這僅僅是對指定的<bean>起的一個額外的名字,並不會額外的建立物件存入map。
<alias name="要起別名的bean的id" alias="要指定的別名"/>
1 @Test 2 /** 3 * SpringIOC中bean別名 4 */ 5 public void test4(){ 6 /* 7 <bean id="person" class="cn.tedu.beans.Person"></bean> 8 <alias name="person" alias="personx"/> 9 */ 10 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 11 //--通過id獲取 12 Person p1 = (Person) context.getBean("personx"); 13 p1.say(); 14 }
6. Spring建立物件的方式
a. 通過類的無法構造方法建立物件
在入門案例中使用的就是這種方式。當用最普通方式配置一個<bean>時,預設就是採用類的無參構造建立物件。在Spring容器初始化時,通過<bean>上配置的class屬性反射得到位元組碼物件,通過newInstance()建立物件
1 Class c = Class .forName("類的全路徑名稱") 2 Object obj = c.newInstance() 這種方式下spring建立物件,要求類必須有無參的構造,否則無法通過反射建立物件,會丟擲異常。 1 package cn.tedu.beans; 2 3 public class Person { 4 public Person(String arg) { 5 System.out.println("Person的無參構造執行了。。。"); 6 } 7 public void say(){ 8 System.out.println("person hello spring~"); 9 } 10 } 11 @Test 12 /** 13 * SpringIOC 建立物件方式 1 - 通過無參構造方法建立物件 14 */ 15 public void test5(){ 16 /* 17 <bean id="person" class="cn.tedu.beans.Person"></bean> 18 */ 19 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 20 Person p = (Person) context.getBean("person"); 21 p.say(); 22 }
b. 通過靜態工廠建立物件
很多的時候,我們面對的類是無法通過無參構造去建立的,例如該類沒有無參構造、是一抽象類 等等情況 ,此時無法要求spring通過無參構造建立物件,此時可以使用靜態工廠 方式建立物件。
1 public class CalendarStaticFactory { 2 public static Calendar getCalendar(){ 3 return Calendar.getInstance(); 4 } 5 } 1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> 6 7 <bean id="calendar" class="cn.tedu.factory.CalendarStaticFactory" factory-method="getCalendar"></bean> 8 9 </beans> 1 @Test 2 /** 3 * SpringIOC 建立物件方式 2 - 靜態工廠 4 */ 5 public void test6(){ 6 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 7 Calendar calendar = (Calendar) context.getBean("calendar"); 8 System.out.println(calendar); 9 }
c. 例項工廠建立物件
例項工廠也可以解決類是無法通過無參構造建立的問題,解決的思路和靜態 工廠類似,只不過例項工廠提供的方法不是靜態的。spring需要先創建出例項工廠的物件,在呼叫例項工廠物件上指定的普通方法來建立物件。所以例項工廠也需要配置到Spring中管理。
1 package cn.tedu.factory; 2 3 import java.util.Calendar; 4 5 public class CalendarFactory { 6 public Calendar getCalendar(){ 7 return Calendar.getInstance(); 8 } 9 } 1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> 6 7 <bean id="calendarFactory" class="cn.tedu.factory.CalendarFactory"></bean> 8 <bean id="calendar" factory-bean="calendarFactory" factory-method="getCalendar"/> 9 10 </beans> 1 @Test 2 /** 3 * SpringIOC 建立物件方式 3 - 例項工廠 4 */ 5 public void test7(){ 6 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 7 Calendar calendar = (Calendar) context.getBean("calendar"); 8 System.out.println(calendar); 9 }
d. Spring工廠建立物件
Spring內建了工廠介面,也可以通過實現這個介面來開發Spring工廠,通過這個工廠建立物件。
1 package cn.tedu.factory; 2 3 import java.util.Calendar; 4 5 import org.springframework.beans.factory.FactoryBean; 6 7 public class CalendarSpringFactory implements FactoryBean<Calendar>{ 8 9 /** 10 * Spring工廠生產物件的方法 11 */ 12 @Override 13 public Calendar getObject() throws Exception { 14 return Calendar.getInstance(); 15 } 16 17 /** 18 * 獲取當前工廠生產的物件的型別的方法 19 */ 20 @Override 21 public Class<?> getObjectType() { 22 return Calendar.class; 23 } 24 25 /** 26 * Spring工廠生產物件時是否採用單例模式 27 * 如果返回true,則在spring中該物件只建立一次 後續 重複使用 28 * 如果返回false,則每次獲取該bean 都重新 建立物件 29 */ 30 @Override 31 public boolean isSingleton() { 32 return true; 33 } 34 35 } 1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> 6 7 <bean id="calendar" class="cn.tedu.factory.CalendarSpringFactory"></bean> 8 9 </beans> 1 @Test 2 /** 3 * SpringIOC 建立物件方式 3 - spring工廠 4 */ 5 public void test8(){ 6 /* 7 <bean id="calendar" class="cn.tedu.factory.CalendarSpringFactory"></bean> 8 */ 9 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 10 Calendar calendar = (Calendar) context.getBean("calendar"); 11 System.out.println(calendar); 12 }
7. 單例和多例
Spring容器管理的bean在預設情況下是單例的,也即,一個bean只會建立一個物件,存在內建 map中,之後無論獲取多少次該bean,都返回同一個物件。
Spring預設採用單例方式,減少了物件的建立,從而減少了記憶體的消耗。
但是在實際開發中是存在多例的需求的,Spring也提供了選項可以將bean設定為多例模式。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> 6 7 <!-- 8 scope屬性控制當前bean的建立模式: 9 singleton:則當前bean處在單例模式中,預設就是此模式 10 prototype:則當前bean處在多例模式中 11 --> 12 <bean id="cart" class="cn.tedu.beans.Cart" scope="prototype"></bean> 13 14 </beans>
bean在單例模式下的生命週期:
bean在單例模式下,spring容器啟動時解析xml發現該bean標籤後,直接建立該bean的物件存入內部map中儲存,此後無論呼叫多少次getBean()獲取該bean都是從map中獲取該物件返回,一直是一個物件。此物件一直被Spring容器持有,直到容器退出時,隨著容器的退出物件被銷燬。
bean在多例模式下的生命週期:
bean在多例模式下,spring容器啟動時解析xml發現該bean標籤後,只是將該bean進行管理,並不會建立物件,此後每次使用 getBean()獲取該bean時,spring都會重新建立該物件返回,每次都是一個新的物件。這個物件spring容器並不會持有,什麼銷燬取決於使用該物件的使用者自己什麼時候銷燬該物件。
實驗:通過斷點除錯模式 ,觀察spring單例和多例的bean執行構造的過程
1 略
8. 懶載入機制
Spring預設會在容器初始化的過程中,解析xml,並將單例的bean建立並儲存到map中,這樣的機制在bean比較少時問題不大,但一旦bean非常多時,spring需要在啟動的過程中花費大量的時間來建立bean 花費大量的空間儲存bean,但這些bean可能很久都用不上,這種在啟動時在時間和空間上的浪費顯得非常的不值得。
所以Spring提供了懶載入機制。所謂的懶載入機制就是可以規定指定的bean不在啟動時立即建立,而是在後續第一次用到時才建立,從而減輕在啟動過程中對時間和記憶體的消耗。
懶載入機制只對單例bean有作用,對於多例bean設定懶載入沒有意義。
懶載入的配置方式:
為指定bean配置懶載入
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd" 6 > 7 8 <bean id="cart" class="cn.tedu.beans.Cart" lazy-init="true"></bean> 9 10 </beans> 為全域性配置懶載入 1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd" 6 default-lazy-init="true" 7 > 8 9 <bean id="cart" class="cn.tedu.beans.Cart"></bean> 10 11 </beans>
**如果同時設定全域性和指定bean的懶載入機制,且配置不相同,則對於該bean區域性配置覆蓋全域性配置。
實驗:通過斷點除錯,驗證懶載入機制的執行過程
1 package cn.tedu.beans; 2 3 public class Cart { 4 public Cart() { 5 System.out.println("Cart init..."); 6 } 7 } 1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd" 6 > 7 8 <bean id="cart" class="cn.tedu.beans.Cart" lazy-init="true"></bean> 9 10 </beans> 1 @Test 2 /** 3 * SpringIOC 懶載入機制 4 */ 5 public void test10(){ 6 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 7 Cart cart1 = (Cart) context.getBean("cart"); 8 Cart cart2 = (Cart) context.getBean("cart"); 9 System.out.println(cart1 == cart2); 10 }
9. 配置初始化和銷燬的方法
在Spring中如果某個bean在初始化之後 或 銷燬之前要做一些 額外操作可以為該bean配置初始化和銷燬的方法 ,在這些方法中完成要功能。
實驗:通過斷點除錯模式,測試初始化方法 和 銷燬方法的執行
1 package cn.tedu.beans; 2 3 public class ProdDao { 4 5 public ProdDao() { 6 System.out.println("ProdDao 被建立。。。"); 7 } 8 9 public void init(){ 10 System.out.println("init。。連線資料庫。。。。。"); 11 } 12 13 14 public void destory(){ 15 System.out.println("destory。。斷開資料庫。。。。。"); 16 } 17 18 public void addProd(){ 19 System.out.println("增加商品。。"); 20 } 21 public void updateProd(){ 22 System.out.println("修改商品。。"); 23 } 24 public void delProd(){ 25 System.out.println("刪除商品。。"); 26 } 27 public void queryProd(){ 28 System.out.println("查詢商品。。"); 29 } 30 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd" 6 > 7 8 <bean id="prodDao" class="cn.tedu.beans.ProdDao" 9 init-method="init" destroy-method="d y"></bean> 10 11 </beans> 1 @Test 2 /** 3 * SpringIOC 初始化和 銷燬方法 4 */ 5 public void test11(){ 6 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 7 ProdDao prodDao = (ProdDao) context.getBean("prodDao"); 8 prodDao.addProd(); 9 context.close(); 10 }
**Spring中關鍵方法的執行順序:
在Spring建立bean物件時,先建立物件(通過無參構造或工廠),之後立即呼叫init方法來執行初始化操作,之後此bean就可以哪來呼叫其它普通方法,而在物件銷燬之前,spring容器呼叫其destory方法來執行銷燬操作。