在之前的java開發中,某個類中須要依賴其它類的方法時,一般是new一個依賴類再調用類實例的方法,這種方法耦合度過高而且不容易測試,spring提出了依賴注入的思想,即依賴類不禁程序員實例化,而是經過spring容器幫咱們new指定實例而且將實例注入到須要該對象的類中。java
依賴注入有3種方式:構造器注入、set注入和註解注入。程序員
構造器注入保證一些必要的屬性在Bean實例化時就獲得設置,而且確保了Bean實例在實例化後就可使用。spring
使用方式:less
例子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:this
有時須要聯合使用type和index才能肯定匹配項和構造函數入參的對應關係,看下面的代碼。spa
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才能解決問題.net
<!-- 構造函數注入(經過入參類型和位置索引肯定對應關係) --> <!-- 對應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,而被選擇的構造函數可能並非用戶所但願的。所以,必須特別謹慎,以免潛在的錯誤。code
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」屬性。
註解注入實際上是使用註解的方式進行構造器注入或者set注入。
spring2.5開始提供了基於註解(Annotation-based)的配置,咱們能夠經過註解的方式來完成注入依賴。在Java代碼中可使用 @Resource或者@Autowired註解方式來經行注入。
雖然@Resource和@Autowired均可以來完成注入依賴,但它們之間是有區別的:
例子:
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(); }
參考: