一、IOC的概念
Inversion of control 控制反轉;
以前: 對象的協作關系A類自己來負責;
A do() ---> B f1()
---> C f2()
do(){
new B().f1();
new C().f2();
}
對象A負責建立與其協作對象的關系;
問題:
1)對象協作關系的建立與業務本身的邏輯糾纏在一起;
2)協作關系也是不容易維護的;(B, C發生改變)
現在: 對象的協作關系容器來負責;
A do() ---> IB <---B f1()
---> IC <---C f2()
setIB(B)
setIC(C)
對象不再負責建立與協作對象的關系, 由容器來負責對象的協作關系;
控制: 指對象的協作關系由誰來建立;
反轉: 原來由對象自己調用的變成了容器調用;
好處: 代碼更好維護, 協作關系不需要考慮了;
對象依賴于接口, 協作對象發生改變以后, 不需要改變;
控制反轉又叫依賴注入: 將對象的協作關系由容器來注入;
二、 基本的容器與IOC的使用
1, spring-framework-2.0.6-with-dependencies
MyEclipse 自帶的有jar包,但是會有一些問題;
我們再創建自己的user庫: spring2_lib:
需要三個地方的包:
spring framework/dist/spring.jar
lib/jakarta-commons/*.jar
lib/cglib/*.jar
新建工程springdev 右擊, myeclipse-- add spring capabilities--- add user libriay--spring2_lib
接口: Greeter
package ioc1;
public interface Greeter {
public String sayHello();
}
實現類: GreeterImpl
package ioc1;
public class GreeterImpl implements Greeter{
private String user;
//提供一個set方法, 可以注入
public void setUser(String user) {
this.user = user;
}
public String sayHello() {
return "你好 " +user;
}
}
配置文件: applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="
xmlns:xsi="
xsi:schemaLocation="http://www.springframework.org/schema/beans
<bean id="greeter" class="ioc1.GreeterImpl">
<property name="user"><!-- 屬性名字 -->
<value>zhangsan</value>
</property>
</bean>
</beans>
測試類:Test
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class Test {
public static void main(String[] args) {
//配置文件的路徑
Resource rs=new ClassPathResource("/ioc1/applicationContext.xml");
BeanFactory bf=new XmlBeanFactory(rs);
Greeter gt=(Greeter) bf.getBean("greeter");//配置文件中id的名字
System.out.println(gt.sayHello());
}
}
2, IOC的類型:
1) set 方式來注入:
<property name=””></property>
其中,name屬性的取值依setter方法名而定
對于基本類型,spring容器會自動做類型轉換,以便賦值;
2)構造器的方式來注入;
<constructor-arg index=””></ constructor-arg>
其中,index表示構造方法中的參數位置(第一個參數索引為0)
3)實現特定的接口來注入,
這種方式已經不采用了,因為業務對象會依賴框架的特定接口;
" 侵入式 "方案;
注入的類型:
1)八種基本類型+String ;
2)對象
3)集合: list, set , map , properties
4)null
3, Spring 容器 :
用于實例化對象, 然后賦值或者裝配, 生命周期管理的一種類;
容器的種類:
BeanFactory: 1)所有容器的父接口, 組件的生成和組裝;
2)提供了基本的IOC的功能;
3)一個實現: XmlBeanFactory ,
需要提供一個Resource的實例給XmlFactory, Resource對象來包裝配置文件;
4)getBean(String)方法,返回一個裝配好的對象;
5)在默認情況下返回的對象都采用的單例模式;
6)直到調用getBean方法時, 才會實例化并且裝配對象;
實例化 -- 構造對象, 給屬性賦值 -- 裝配/注入 ;
ApplicationContext: Spring的上下文, 也是一個容器, 是BeanFactory的子接口;
增強了BeanFactory, 提供了事件處理, 國際化,復雜的生命周期管理;
一般情況下用ApplicationContext, 而不用BeanFactory;
實現類:
ClassPathXmlApplicationContext: 依據classpath查找配置文件;
容器初始化的時候會預先實例化單例的對象; 可以配置, 有的采用單例有的不用; 三、set 方式注入:
<1>. 基本類型的注入:
注入簡單類型: 八種基本類型+String
方式: id是bean的id號, 唯一的; class是bean類的全路徑;
<bean id="" ,class="">
<property name=""> <!--屬性名, set方法去掉set并將首字母小寫后的名字 -->
<value>屬性值</value>
</property>
</bean>
對于基本類型, 會自動轉換數據類型; 跟序列化的意思差不多;
用ApplicationContext 容器寫測試類:
ApplicationContext ctx=new ClassPathXmlApplicationContext("/ioc1/applicationContext.xml");
Greeter gt2=(Greeter) ctx.getBean("greeter");
Greeter gt3=(Greeter) ctx.getBean("greeter");
System.out.println(gt2.sayHello());
//測試單例模式;true
System.out.println(gt2==gt3);
<2>. 對象類型的注入:
1) <ref local> 要注入的對象在同一個配置文件中;
2) <ref bean> 要注入的對象可以分散在其他的配置文件中;
3) 將要注入的bean類的配置信息直接嵌入到bean類中;(有點象內部類)
不讓客戶端直接用getBean方式獲得,該對象只被注入的對象使用;
例子1:
實現類:
public class OderServiceImpl implements OrderService{
private SomeBean sb; //最好用接口
}
============================第一種:<ref local>=================================
配置文件1: <ref local>
使用local則定義的兩個類必須出現在同一個配置文件中
<!-- 第一個bean -->
<bean id="otherBean" class="IOC.pm.set.Class.OtherBean">
<property name="str"><!-- 屬性名字 -->
<value>someinfo</value>
</property>
</bean>
<!-- 第二個bean -->
<bean id="someBean" class="IOC.pm.set.Class.SomeBean">
<property name="ob">
<ref local="otherBean"/> //另外一個bean的id;
</property>
</bean>
===========================第二種:嵌套==================================
或者2: 嵌到一起: 被嵌套的bean就不能夠使用getBean()來返回了;
使用嵌套定義的兩個類必須出現在同一個配置文件中
<bean id="someBean" class="IOC.pm.set.Class.SomeBean">
<property name="ob">
<!--這樣的話ohterBean只能被someBean調用 -->
<bean id="otherBean" class="IOC.pm.set.Class.OtherBean">
<property name="str1"><!-- 屬性名字 -->
<value>tarena</value>
</property>
</bean>
</property>
</bean>
============================第三種:<ref bean>==================================
或者3: <ref bean>: 功能最強大的搜索多個配置文件;
加一個配置文件: otherContext.xml 放someBean的配置;
applicationContext.xml中 <ref bean="otherBean"/>
測試類中: 寫成一個字符串數組;
ApplicationContext ctx=new ClassPathXmlApplicationContext(
new String[]{"/ioc2/applicationContext.xml","/ioc2/other-config.xml.xml"});
<3>. 集合的注入: (看老師的示例; )
List: 可重復的,有序
可以存放字符串,對象, 以及集合;
<property name="list">
<list>
<value>String1</value>
<value>String2</value>
</list>
</property>
Set: 不重復, 無序
可以存放字符串,對象, 以及集合;
<property name="set">
<set>
<value>String1</value>
<value>String1</value>
</set>
</property>
Map:Key(只能用String), value 鍵值對;
<property name="map">
<map>
<entry key="key1"><!-- 用key來存放key的值,用value來存放value值 -->
<value>value1</value>
</entry>
<entry key="key2">
<value>value2</value>
</entry>
</map>
</property>
Properties: key(String),value(String) 鍵值對
<property name="properties">
<props>
<prop key="key11">value11</prop>
<prop key="key22">value22</prop>
</props>
</property>
四、構造器的方式注入:
<1>. <constructor-arg>
<value>基本類型</value>
</constructor-arg>
<2>. <constructor-arg>
<ref bean=""></ref>
</constructor-arg>
<bean id="someBean" class="ioc4.SomeBean">
<constructor-arg index="1"><!--參數的順序可以改變,下標從0開始 -->
<value>String1</value>
</constructor-arg>
<constructor-arg index="0">
<value>String2</value>
</constructor-arg>
</bean>
構造器注入和set方式注入比較:
用構造器注入:如果要注入的對象,屬性不是特別多,并且屬性值一定要被注入后才能來使用;
其他情況用set方式注入;
五、IOC的特例 ;
1, 自動裝配, 讓容器依據某種規則,自動的對組件實施裝配;
在bean中:
autowire=""
<1>. byName: 匹配屬性的名字與bean的id號進行匹配; 找set方法;
someBean中有一個ob的屬性:
<bean id="ob" class="ioc6.OtherBean">
<property name="str1">
<value>String1</value>
</property>
</bean>
<bean id="someBean" class="ioc6.SomeBean" autowire="byName">
<property name="str1">
<value>String1</value>
</property>
</bean>
<2>. byType: 尋找配置文件, 匹配的屬性類型與bean一致; 找set方法;
上面例子改成byType 也是正確的, 并且ob可以改為別的名字;
如果在配置文件中找到符合條件的bean的個數超過一個,會報錯;
<3>. constructor: 匹配構造器,看構造器的參數類型和配置文件bean的類型是否一致,
一致就成功,否則報錯;
匹配個數超過一個,也報錯;
提供一個構造器;
<4>. autodetect: 自動檢測, 先構造器, 再set方法(也就是可以set方法和構造方法可以同時使用);
自動從配置文件中尋找對應的bean的名字;
注意:手動裝配會覆蓋自動裝配的策略;
問題:
1)自動裝配用的很少, 配置文件中,bean的關系不清晰, 易出錯;
在建立原型的時候, 快速搭建, 真正開發的時候, 一般手動配置;
2)自動裝配和手動裝配配合在一起使用; 手動可以覆蓋自動的;
3)自動裝配搭配一個裝配檢查; 屬性先檢查, 出錯后就不再進行了;
dependency-check=""
simple :只檢查基本類型屬性是否裝配成功;
objects:只檢查Object類型是否裝配成功;
all:全檢查;
既可以和自動裝配一起使用, 也可以單獨用來檢查是否裝配成功;
自動裝配的使用情況:
1,構建系統原形;
2,與dependency-check=""配合使用;
3,手動裝配會和自動裝配要配合使用;
2, 用工廠的方式創建對象: 靜態工廠, 實例工廠;
原因是: 很多對象不方便采用配置文件去裝配;
如: 獲得數據庫連接, 可以寫一個ConnectionFactory, getConnection方法
靜態工廠類, 靜態方法, getBean("connection")就可以得到連接;
public static Car createCar(){// 靜態工廠,靜態方法
return new Car();
}
<bean id="car" class="ioc5.CarFactory" factory-method="createCar" />
Car car=(Car) ctx.getBean("car");
System.out.println(car);
實例工廠: 指工廠實例化后才能使用,
public Car createCar(){// 必須是實例方法
return new Car();
}
<bean id="carFactory" class="ioc5.CarFactory" />
<bean id="car" factory-bean="carFactory" factory-method="createCar" />
3,繼承關系:
<bean id="abstractBean" class="day02.ioc7.SomeBean" abstract="true">
<property name="str1">
<value>string1</value>
</property>
<property name="num">
<value>20</value>
</property>
</bean>
<!-- 繼承關系 -->
<bean id="someBean" parent="abstractBean">
<property name="num">
<value>30</value>
</property>
</bean>
得到的bean不想用單例時, 在bean標簽上加 Scope=singleton/prototype
裝配中的其它情況:
單例:scope=""
singleton,prototype
初始化與銷毀: init-method,destroy-method