什么是依賴注入
在以前的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();
}

