1. IOC(DI) - 控制反轉(依賴注入)
所謂的IOC稱之為控制反轉,簡單來說就是將對象的創建的權利及對象的生命周期的管理過程交由Spring框架來處理,從此在開發過程中不再需要關注對象的創建和生命周期的管理,而是在需要時由Spring框架提供,這個由spring框架管理對象創建和生命周期的機制稱之為控制反轉。而在 創建對象的過程中Spring可以依據配置對對象的屬性進行設置,這個過程之為依賴注入,也即DI。
2. IOC的入門案例
a. 創建一個java項目
spring並不是非要在javaweb環境下才可以使用,一個普通的java程序中也可以使用Spring。
b. 導入Spring的libs目錄下IOC相關的jar包
c. 創建Spring的配置文件
Spring採用xml文件作為配置文件,xml文件名字任意,但通常都取名為applicationContext.xml,通常將該文件放置在類加載的目錄裡下(src目錄),方便後續使用。
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="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.xsd"></beans>
d. 創建bean類,並在spring中進行配置交由spring來管理
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beansxmlns="http://www.springframework.org/schema/beans"3xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> 6 7 <beanid="person"class="cn.tedu.beans.Person"></bean> 8 9 </beans>
e. 在程序中通過Spring容器獲取對象並使用
/*** SpringIOC方式創建並管理bean */ @Testpublicvoidtest02(){//1.初始化Spring容器 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.通過Spring容器獲取bean Person p = (Person) context.getBean("person"); p.eat(); p.say(); //3.關閉Spring容器 ((ClassPathXmlApplicationContext)context).close(); }
3. IOC的實現原理
在初始化一個Spring容器時,Spring會去解析指定的xml文件,當解析到其中的<bean>標籤時,會根據該標籤中的class屬性指定的類的全路徑名,通過反射創建該類的對象,並將該對象存入內置的Map中管理。其中鍵就是該標籤的id值,值就是該對象。
之後,當通過getBean方法來從容器中獲取對象時,其實就是根據傳入的條件在內置的Map中尋找是否有匹配的鍵值,如果有則將該鍵值對中保存的對象返回,如果沒有匹配到則拋出異常。
由此可以推測而知:
默認情況下,多次獲取同一個id的bean,得到的將是同一個對象。
不可以配置多個id相同的bean
可以配置多個id不同但class相同的bean
4. IOC獲取對象的方式
通過context.getBean()方法獲取bean時,可以通過如下兩種方式獲取:
傳入id值
傳入class類型
通過class方式獲取bean時,如果同一個類配置過多個bean,則在獲取時因為無法確定到底要獲取哪個bean會拋出異常。
而id是唯一的,不存在這樣的問題,所以建議大家儘量使用id獲取bean。
/*** 獲取對象的方式 * 通過id獲取bean * 如果找不到,拋異常NoSuchBeanDefinitionException * 如果找到唯一的,返回對象 * 因為id不重複,不可能找到多個 * 通過class獲取bean * 如果找不到,拋出異常NoSuchBeanDefinitionException * 如果找到唯一,返回對象 * 如果找到多個,拋出異常NoUniqueBeanDefinitionException */ @Testpublicvoidtest04(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//獲取對象方式1:通過id獲取 //Person p = (Person)context.getBean("person"); //p.eat(); //p.say(); //獲取對象方式2:通過class獲取 Person p = context.getBean(Person.class); p.eat(); p.say(); ((ClassPathXmlApplicationContext)context).close(); }
SpringIOC在通過class獲取bean時,如果找不到該類型的bean還會去檢查是否存在該類型的子孫類型的bean,如果有則返回,如果找不到或找到多個則拋出異常。這符合java面向對象思想中的多態的特性。
@Test public void test02() {//1.初始化spring容器 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//2.獲取Bean JavaTeacher jt = (JavaTeacher) context.getBean(Teacher.class); System.out.println(jt); }
5. 別名標籤
在 Spring中提供了別名標籤<alias>可以為配置的<bean>起一個別名,要注意的是這僅僅是對指定的<bean>起的一個額外的名字,並不會額外的創建對象存入map。
<alias name="要起別名的bean的id" alias="要指定的別名"/>/** * 別名標籤 * 可以通過別名標籤為bean的id起一個別名,此後除了可以通過別名指代id * <alias name="person"alias="pers"></alias> */ @Test public void test05(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//Person p = (Person) context.getBean("person"); Person p = (Person) context.getBean("pers"); System.out.println(p); ((ClassPathXmlApplicationContext)context).close(); }
6. Spring創建對象的方式
a. 通過類的無法構造方法創建對象
在入門案例中使用的就是這種方式。
當用最普通方式配置一個<bean>時,默認就是採用類的無參構造創建對象。
在Spring容器初始化時,通過<bean>上配置的class屬性反射得到字節碼對象,通過newInstance()創建對象
1 Class c = Class .forName("類的全路徑名稱")
2 Object obj = c.newInstance()
這種方式下spring創建對象,要求類必須有無參的構造,否則無法通過反射創建對象,會拋出異常。
publicclassPerson {publicPerson(){System.out.println("Person被創建了.."); } }/** * SpringIOC創建對象方式1 - 反射創建對象 * bean必須有無參構造才可以 */ @Testpublicvoidtest01() throws Exception { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Person p = (Person) context.getBean("person"); System.out.println(p); ((ClassPathXmlApplicationContext)context).close(); }
b. 通過靜態工廠創建對象
很多的時候,我們面對的類是無法通過無參構造去創建的,例如該類沒有無參構造、是一抽象類 等等情況 ,此時無法要求spring通過無參構造創建對象,此時可以使用靜態工廠 方式創建對象。
publicclassPerson{publicPerson(String name){System.out.println("Person被創建了.."); } }/** * 靜態工廠 */publicclassPersonStaticFactory {privatePersonStaticFactory(){}publicstatic Person getInstance(){returnnew Person("zs"); } } <bean id="person"class="cn.tedu.factory.PersonStaticFactory" factory-method="getInstance"></bean> @Testpublicvoidtest02(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Person p = (Person) context.getBean("person"); System.out.println(p); ((ClassPathXmlApplicationContext)context).close(); }
c. 實例工廠創建對象
實例工廠也可以解決類是無法通過無參構造創建的問題,解決的思路和靜態工廠類似,只不過實例工廠提供的方法不是靜態的。
spring需要先創建出實例工廠的對象,在調用實例工廠對象上指定的普通方法來創建對象。所以實例工廠也需要配置到Spring中管理。
publicclassPerson{public Person(String name){System.out.println("Person被創建了.."); } }/** * 實例工廠 */publicclassPersonInstanceFactory{public Person getInstance(){return new Person("ls"); } } <beanid="personInstanceFactory"class="cn.tedu.factory.PersonInstanceFactory"></bean> <beanid="person"factory-bean="personInstanceFactory"factory-method="getInstance"></bean>@Testpublicvoidtest03(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Person p = (Person) context.getBean("person"); System.out.println(p); ((ClassPathXmlApplicationContext)context).close(); }
d. Spring工廠創建對象 Spring內置了工廠接口,也可以通過實現這個接口來開發Spring工廠,通過這個工廠創建對象。
publicclassPerson{public Person(String name){System.out.println("Person被創建了.."); } }package cn.tedu.factory;import cn.tedu.domain.Person;import org.springframework.beans.factory.FactoryBean;/** * Spring工廠 */publicclassPersonSpringFactoryimplementsFactoryBean<Person> {/** * 生產bean對象方法 */@Overridepublic Person getObject() throws Exception {return new Person("ww"); }/** * 獲取bean類型方法 */@Overridepublic Class<?> getObjectType() {return Person.class; } /** * 告知當前bean是否要採用單例模式 */@OverridepublicbooleanisSingleton() {returntrue; } } <bean id="person"class="cn.tedu.factory.PersonSpringFactory"></bean>@Testpublicvoidtest04(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Person p = (Person) context.getBean("person"); System.out.println(p); ((ClassPathXmlApplicationContext)context).close(); }
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" 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容器並不會持有,什麼銷毀取決於使用該對象的用戶自己什麼時候銷毀該對象。
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 <beansxmlns="http://www.springframework.org/schema/beans"3xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"6 > 7 8 <beanid="cart"class="cn.tedu.beans.Cart"lazy-init="true"></bean> 9 10 </beans>
為全局配置懶加載
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beansxmlns="http://www.springframework.org/schema/beans"3xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"6default-lazy-init="true"7 > 8 9 <beanid="cart"class="cn.tedu.beans.Cart"></bean> 10 11 </beans>
如果同時設定全局和指定bean的懶加載機制,且配置不相同,則對於該bean局部配置覆蓋全局配置。
實驗:通過斷點調試,驗證懶加載機制的執行過程
1packagecn.tedu.beans;23publicclassCart{4publicCart(){5System.out.println("Cartinit...");6}7}1<?xmlversion="1.0"encoding="UTF-8"?>2<beansxmlns="http://www.springframework.org/schema/beans"3xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4xsi:schemaLocation="http://www.springframework.org/schema/beans5http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"6>78<beanid="cart"class="cn.tedu.beans.Cart"lazy-init="true"></bean>910</beans>1@Test2/**3*SpringIOC懶加載機制4*/5publicvoidtest10(){6ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");7Cartcart1=(Cart)context.getBean("cart");8Cartcart2=(Cart)context.getBean("cart");9System.out.println(cart1==cart2);10}
9. 配置初始化和銷毀的方法
在Spring中如果某個bean在初始化之後 或 銷毀之前要做一些 額外操作可以為該bean配置初始化和銷毀的方法 ,在這些方法中完成要功能。
實驗:通過斷點調試模式,測試初始化方法 和 銷毀方法的執行
1 package cn.tedu.beans;23publicclassProdDao {45publicProdDao() {6 System.out.println("ProdDao 被創建。。。");7 }89publicvoidinit(){10 System.out.println("init。。連接資料庫。。。。。");11 }121314publicvoiddestory(){15 System.out.println("destory。。斷開資料庫。。。。。");16 }1718publicvoidaddProd(){19 System.out.println("增加商品。。");20 }21publicvoidupdateProd(){22 System.out.println("修改商品。。");23 }24publicvoiddelProd(){25 System.out.println("刪除商品。。");26 }27publicvoidqueryProd(){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 >78 <bean id="prodDao"class="cn.tedu.beans.ProdDao"9 init-method="init" destroy-method="destory"></bean>1011 </beans>1 @Test2/** 3 * SpringIOC 初始化和 銷毀方法 4 */5publicvoidtest11(){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方法來執行銷毀操作。
#Java#