Spring框架-IOC/DI詳細學習
一.IOC/DI概念
參考博客:https://www.cnblogs.com/xdp-gacl/p/4249939.html
IOC(inversion of control, 控制反轉)是一種設計思想,而不是一種技術. IOC意味著將我們設計好的對象交給容器控制,而不是交給對象內部控制.它指導我們設計出松耦合,更優良的程序. 傳統程序都是由在類內部主動創建依賴對象來使用, 從而導致類與類之間高耦合, 難於測試; 有了IOC容器,把創建和查找依賴對象的控制權交給了容器, 由容器進行註入組合對象,所以對象和對象之間是松耦合, 這樣使得程序的整個體系結構非常靈活.
實際上,IOC可以看做是一個大型中介, 將對象的信息登記其中,這個對象是什麽, 這個對象又需要什麽 ,由中介公司給對象分配它需要的資源, 以及將對象分配給需要它的對象.Spring在這個過程中控制著對象的生命周期和對象間的關系, 所有類的創建和銷毀都由Spring控制, 而不是需要它們的類控制, 這就是控制反轉.
DI(Dependency Injection, 依賴註入)是IOC的一個重點,它實現IOC的一種方法,或者說DI是IOC的另一種說法 , 它負責實現IOC動態地向某個對象提供它所需要的其他對象. 比如一個人需要一根掃把, 不是由這個人自己去做掃把, 而是由Spring將做好的掃把給他, 而這個人全程需要做的,就是喊我要一根掃把. 什麽叫做依賴, 人需要掃把, 人就對掃把有了依賴. 而DI就是根據這個依賴,將掃把分配給這個人的.
二.IOC的使用
參考博客:https://www.cnblogs.com/best/p/5727935.html#_lab2_0_4
IOC實現的兩種方式: XML配置
依賴註入的兩種形式: Setter依賴註入, 構造方法依賴註入
1.XML配置的兩種依賴註入形式:
(1)Setter依賴註入: 首先創建一個Book類,將其屬性設置好setter,getter方法
public class Book { private int id; private String name; private User user; public int getId() { return id; } public void setId(intid) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
然後在applicationContext.xml中添加bean, 告訴Spring兩者的依賴關系,實現註入
<bean name="book" class="pojo.Book"> <property name="name" value="book1"></property> <property name="user" ref="user"></property> <!--這裏將book依賴了user,因此在我們獲取book時,會獲取指定的user對象 --> </bean> <bean name="user" class="pojo.User"> <property name="name" value="user1"></property> <property name="age" value="12"></property> </bean>
(2)構造方法依賴註入:
我們將BOOK類中添加了一個兩個參數的構造方法,然後在xml文件中確定兩個參數的值,從而讓spring容器能夠創造對象
public class Book { private int id; private String name; private User user; //Constructor public Book(String name,User user){ this.name = name; this.user = user; } }
xml文件中的配置為: 指定了構造器有兩個參數, 第一個參數是String , 第二個參數指向了另一個bean
<bean name="book" class="pojo.Book"> <constructor-arg type="java.lang.String"> <!-- 指定參數的類型,可以在有多個構造器時不產生歧義 --> <value>Adel</value> </constructor-arg> <constructor-arg> <ref bean="user"></ref> </constructor-arg> </bean> <bean name="user" class="pojo.User"> <property name="name" value="user1"></property> <property name="age" value="12"></property> </bean>
或者可以這樣寫構造參數:
<bean name="book" class="pojo.Book">
<constructor-arg name="name" value="lala"></constructor-arg>
<constructor-arg name="user" ref="user"></constructor-arg>
</bean>
又或者這樣寫:
<bean name="book" class="pojo.Book">
<constructor-arg index="0" value="lala"></constructor-arg>
<constructor-arg index="1" ref="user"></constructor-arg>
</bean>
(3) Scope對象作用域: 從Spring容器中獲取的對象的作用域可以修改,默認的作用域為單例, 具體如下
- Singleton : 每個Spring容器作用域中,一個bean定義 只對應一個對象實例
- Prototype: 一個bean定義對應多個對象實例
- Request: 一個bean定義作用於HTTP request 生命周期, 指每個HTTP request 從Spring獲取的對象實例不一樣. 僅在基於web的Spring ApplicationContext中有效.
- Session: 一個bean定義作用域HTTP session 生命周期,
- Global Session: 一個bean定義作用域全局的HTTP session 生命周期.
- Application: 一個bean定義作用於整個ServletContext生命周期
其在配置中寫在bean的屬性Scope中:
<bean name="book" class="pojo.Book" scope="singleton"> <constructor-arg index="0" value="lala"></constructor-arg> <constructor-arg index="1" ref="user"></constructor-arg> </bean>
(4)延遲初始化bean: 通常ApplicationContext默認在啟動時將所有singleton bean提前實例化. 通常提前實例化是好事,避免某些錯誤因為延遲實例化而不被提前發現. 但是如果想防止提前實例化, 比如你只需要用到book對象, 而不需要它初始化它的user對象, 就可以用lazy-init屬性來控制
<bean name="book" class="pojo.Book" > <constructor-arg index="0" value="lala"></constructor-arg> <constructor-arg index="1" ref="user"></constructor-arg> </bean> <bean name="user" class="pojo.User" lazy-init="true"> <property name="name" value="user1"></property> <property name="age" value="12"></property> </bean>
(5)回調方法: 初始化回調函數init
public class Book { private int id; private String name; private User user; // Constructor public Book(String name,User user){ this.name = name; this.user = user; } public void init(){ System.out.println("創建了一本書"); } public void destroy(){ System.out.println("銷毀了一本書"); } }
在xml文件中設置初始化回調函數,就會在創建時調用
<bean name="book" class="pojo.Book" init-method="init" destroy-method="destroy" >
<constructor-arg index="0" value="lala"></constructor-arg>
<constructor-arg index="1" ref="user"></constructor-arg>
</bean>
2.Spring註解:
註解會削減一些配置的工作量, 但是代碼的耦合度會增加, 應當根據需要使用註解
(1)@Component註解: 這個註解和<bean>作用類似, 通過增加@Component()註解中的參數,指定其在Spring容器中的名稱. 如果不寫,則是默認其在Spring容器中的名稱為類名的小寫 如User類 在Spring容器中默認名稱為 user
@Component("user") public class User { private int id; private int age; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
在配置文件中,即使沒有user這個bean , 其它bean也可以用到
<bean name="book" class="pojo.Book" init-method="init" destroy-method="destroy" > <constructor-arg index="0" value="lala"></constructor-arg> <constructor-arg index="1" ref="user"></constructor-arg> </bean> <!--<bean name="user" class="pojo.User" lazy-init="true">--> <!--<property name="name" value="user1"></property>--> <!--<property name="age" value="12"></property>--> <!--</bean>-->
(2)@Auotowired 註解: 在Spring中,可以使用 @Autowired 註解通過setter方法,構造函數或字段自動裝配Bean。
通過setter方法public class Book { private int id; private String name; private User user; public User getUser() { return user; } @Autowired public void setUser(User user) { this.user = user; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
這樣,即使在applicationContext.xml文件中沒有設置要在book中user依賴哪個對象, Spring容器也會自動裝配
<bean name="book" class="pojo.Book" > <property name="name" value="lala"></property> <property name="id" value="12"></property> </bean> <bean name="user" class="pojo.User" lazy-init="true"> <property name="name" value="user1"></property> <property name="age" value="12"></property> </bean>
通過構造函數:
public class Book { private int id; private String name; private User user; // Constructor @Autowired public Book(User user){ this.user = user; } }
通過字段:
public class Book { private int id; private String name; @Autowired private User user; }
(3)@Resource註解 : 這個註解與@Autowired不同是, 它可以指定裝配對象的名稱,或者類型,或者兩者都指定
如book和user
@Component("u") public class User { private int id; private int age; private String name;
//setter 和 getter
}
@Component("b") public class Book { private int id; private String name; @Resource(name = "u") private User user; //setter和getter }
@Component("b") public class Book { private int id; private String name; @Resource(name = "u",type = User.class) private User user; }
或者這樣
@Component("b") public class Book { private int id; private String name; @Resource(name = "u",type = User.class) private User user;各種註解的解釋, 以下轉自 https://www.cnblogs.com/best/p/5727935.html#_lab2_0_4
@Service用於註解業務層組件(我們通常定義的service層就用這個)
@Controller用於註解控制層組件(如struts中的action)
@Repository用於註解數據訪問組件,即DAO組件
@Component泛指組件,當組件不好歸類的時候,我們可以使用這個註解進行註解。
裝配註解主要有:@Autowired、@Qualifier、@Resource,它們的特點是:
1、@Resource默認是按照名稱來裝配註入的,只有當找不到與名稱匹配的bean才會按照類型來裝配註入;
2、@Autowired默認是按照類型裝配註入的,如果想按照名稱來轉配註入,則需要結合@Qualifier一起使用;
3、@Resource註解是由J2EE提供,而@Autowired是由spring提供,故減少系統對spring的依賴建議使用@Resource的方式;如果Maven項目是1.5的JRE則需換成更高版本的。
4、@Resource和@Autowired都可以書寫註解在字段或者該字段的setter方法之上
5、@Autowired 可以對成員變量、方法以及構造函數進行註釋,而 @Qualifier 的註解對象是成員變量、方法入參、構造函數入參。
6、@Qualifier("XXX") 中的 XX是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動註入的策略就從 byType 轉變成 byName 了。
7、@Autowired 註釋進行自動註入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個,通過屬性required可以設置非必要。
8、@Resource裝配順序
8.1. 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常
8.2. 如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常
8.3. 如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常
8.4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始類型進行匹配,如果匹配則自動裝配;
Spring框架-IOC/DI詳細學習