什么是依賴注入
在以前的java開發中,某個類中需要依賴其它類的方法時,通常是new一個依賴類再調用類實例的方法,這種方法耦合度太高並且不容易測試,spring提出了依賴注入的思想,即依賴類不由程序員實例化,而是通過spring容器幫我們new指定實例並且將實例注入到需要該對象的類中。
依賴注入的方式
依賴注入有3種方式:構造器注入、set注入和注解注入。
1.構造器注入
構造器注入保證一些必要的屬性在Bean實例化時就得到設置,並且確保了Bean實例在實例化后就可以使用。
使用方式:
- 在類中,不用為屬性設置setter方法,但是需要生成該類帶參的構造方法。
- 在配置文件中配置該類的bean,並配置構造器,在配置構造器中用到了<constructor-arg>節點,該節點有四個屬性
- index:指定注入屬性的順序索引,從0開始;
- type:指該屬性所對應的類型;
- ref:引用的依賴對象;
- value:當注入的不是依賴對象,而是基本數據類型時,就用value;
例子1:
public class SpringAction { //注入對象springDao private SpringDao springDao; private User user; public SpringAction(SpringDao springDao,User user){ this.springDao = springDao; this.user = user; System.out.println("構造方法調用springDao和user"); } public void save(){ user.setName("卡卡"); springDao.save(user); } }
<bean name="springAction" class="com.bless.springdemo.action.SpringAction"> <!--(2)創建構造器注入,如果主類有帶參的構造方法則需添加此配置--> <constructor-arg index="0" ref="springDao"></constructor-arg> <constructor-arg index="1" ref="user"></constructor-arg> </bean> <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean> <bean name="user" class="com.bless.springdemo.vo.User"></bean>
其中index屬性表示注入的bean在構造方法中的參數順序。
例子2:
有時需要聯合使用type和index才能確定匹配項和構造函數入參的對應關系,看下面的代碼。
public Car(String brand, String corp,double price){ this.brand=brand; this.corp=corp; this.price=price; } public Car(String brand, String corp,int maxSpeed){ this.brand=brand; this.corp=corp; this.maxSpeed=maxSpeed; }
這里,Car擁有兩個重載的構造函數,它們都有三個入參。針對這種情況,按照入參索引的配置方式又難以滿足要求了,這時需要聯合使用<constructor-arg>的type和index才能解決問題
<!-- 構造函數注入(通過入參類型和位置索引確定對應關系) --> <!-- 對應public Car(String brand, String corp,int maxSpeed)構造函數 --> <bean id="car3" class="com.spring.model.Car"> <constructor-arg index="0" type="java.lang.String" value="奔馳"></constructor-arg> <constructor-arg index="1" type="java.lang.String" value="中國一汽"></constructor-arg> <constructor-arg index="2" type="int" value="200"></constructor-arg> </bean>
對於由於參數數目相同而類型不同所引起的潛在配置歧義問題,Spring容器可以正確啟動且不會給出報錯信息,它將隨機采用一個匹配的構造函數實例化Bean,而被選擇的構造函數可能並不是用戶所希望的。因此,必須特別謹慎,以避免潛在的錯誤。
2.set注入
set注入要求Bean提供一個默認的構造函數,並為需要注入的屬性提供對應的Setter方法。
Spring先調用Bean的默認構造函數實例化Bean對象,然后通過反射的方式調用Setter方法注入屬性值。
假設Bean顯示定義了一個帶參的構造函數,則需要同時提供一個默認無參的構造函數,否則使用屬性注入時將拋出異常。
例子1:
package com.bless.springdemo.action; public class SpringAction { //注入對象springDao private SpringDao springDao; //一定要寫被注入對象的set方法 public void setSpringDao(SpringDao springDao) { this.springDao = springDao; } public void ok(){ springDao.ok(); } }
<!--配置bean,配置后該類由spring管理--> <bean name="springAction" class="com.bless.springdemo.action.SpringAction"> <!--(1)依賴注入,配置當前類中相應的屬性--> <property name="springDao" ref="springDao"></property> </bean> <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>
這里不再使用“constructor-arg”而是使用“property”屬性。
3.注解注入
注解注入其實是使用注解的方式進行構造器注入或者set注入。
spring2.5開始提供了基於注解(Annotation-based)的配置,我們可以通過注解的方式來完成注入依賴。在Java代碼中可以使用 @Resource或者@Autowired注解方式來經行注入。
雖然@Resource和@Autowired都可以來完成注入依賴,但它們之間是有區別的:
- @Resource默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean才會按照類型來裝配注入;
- @Autowired默認是按照類型裝配注入的,如果想按照名稱來轉配注入,則需要結合@Qualifier一起使用;
- @Resource注解是又J2EE提供,而@Autowired是由Spring提供;
例子:
package com.bless.springdemo.action; public class SpringAction { //注入對象springDao @Resource private SpringDao springDao; //不需要要寫被注入對象的set方法,spring會幫你get/set public void ok(){ springDao.ok(); } }
<context:annotation-config/>
<bean name="springAction" class="com.bless.springdemo.action.SpringAction"> <!--不需要配置屬性,@Resource默認按照名稱裝配注入--> </bean> <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>
<context:annotation-config/>的作用是開啟注解。
@Autowired的同理,只不過默認是按照類型裝配注入。
補充:
@Autowired 可以對成員變量、方法以及構造函數進行注釋。那么對成員變量和構造函數進行注釋又有什么區別呢?
先來看下面一段代碼:
@Autowired private User user; private String school; public UserAccountServiceImpl(){ this.school = user.getSchool(); }
這段代碼執行會報錯,因為Java類會先執行構造方法,然后再給注解了@Autowired 的user注入值,所以在執行構造方法的時候,就會報錯。
解決辦法是,使用構造器注入,如下:
private User user; private String school; @Autowired public UserAccountServiceImpl(User user){ this.user = user; this.school = user.getSchool(); }