Spring DI(依賴注入)之注解配置
1. 前言
上一節(jié),我們通過(guò) xml 文件的配置方式,實(shí)現(xiàn)了對(duì)多種依賴類型的注入,當(dāng)然體會(huì)到了 xml 文件配置方式的弊端:有一點(diǎn)麻煩。
依賴注入是有兩種方式,一種是 xml ,另外一種就是注解的配置方式。
本節(jié),我們演示下通過(guò)注解配置這種方式來(lái)實(shí)現(xiàn)注入依賴。
來(lái)吧 ,直入主題,莫浪費(fèi)大好光陰…
2. 工程實(shí)例
2.1 注解的介紹
在正式使用注解之前,我們首先介紹下注解語(yǔ)法以及它的作用。
- @Autowired: 此注解自動(dòng)按照類型注入。從容器中尋找符合依賴類型的實(shí)例,當(dāng)使用該注解注入屬性時(shí),set 方法可以省略。但是因?yàn)榘凑疹愋推ヅ洌绻萜髦杏卸鄠€(gè)匹配的類型,會(huì)拋出異常,需要指定引入的實(shí)例 id。如果找不到匹配的實(shí)例,那么也會(huì)拋出異常;
- @Qualifier: 此注解不能單獨(dú)使用,它的作用是在按照類型注入的基礎(chǔ)之上,再按照 Bean 的 id 注入。所以如果是使用了 @Autowire 注解自動(dòng)注入,但是容器中卻有多個(gè)匹配的實(shí)例,可以搭配此注解,指定需要注入的實(shí)例 id;
- @Resource 此注解的作用是指定依賴按照 id 注入,還是按照類型注入。當(dāng)只使用注解,但是不指定注入方式的時(shí)候,默認(rèn)按照 id 注入,找不到再按照類型注入。
2.2 @Autowired 注解
1. 為了測(cè)試效果,我們創(chuàng)建 Service 和 Dao 兩個(gè)類, Dao 作為 Service 的依賴。代碼如下:
//service實(shí)現(xiàn)類的代碼
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public void saveUser() {
System.out.println("執(zhí)行service中的保存邏輯");
}
}
//dao實(shí)現(xiàn)類的代碼
@Repository
public class UserDaoImpl implements UserDao {
public void saveUser() {
System.out.println("執(zhí)行dao的保存方法");
}
}
代碼解釋:
上面代碼可以看到,兩個(gè)類的實(shí)例化方式都是通過(guò)注解注入到容器, 并且在 service 實(shí)現(xiàn)類中的 userDao 屬性上面加了注解 @Autowired
。
我們首先測(cè)試下:能否通過(guò)這個(gè)注解,實(shí)現(xiàn)依賴注入,另外再測(cè)試下它是否是按照類型注入。
2. 配置文件的內(nèi)容為注解實(shí)現(xiàn) IoC。
配置文件解釋: 注解實(shí)現(xiàn) IoC 的章節(jié)說(shuō)過(guò),需要通過(guò)組件掃描來(lái)實(shí)例化容器。
3. 編寫測(cè)試代碼
public class SpringAnTest {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean(UserService.class);
userService.saveUser();
}
}
測(cè)試結(jié)果:
結(jié)果解釋
可以看到 service 中的代碼執(zhí)行,并且通過(guò) dao 的示例調(diào)用的方法也執(zhí)行了,那么說(shuō)明 @Autowired
注解實(shí)現(xiàn)了屬性 userDao 的注入。
當(dāng)然這種操作是小兒科,沒(méi)有一個(gè)同學(xué)覺(jué)得他有什么。 我們驗(yàn)證下它的特點(diǎn):set 方法我們是省略了,那么它是否按照類型注入的呢?
如果我們的實(shí)現(xiàn)類中有多個(gè) userDao 接口的實(shí)現(xiàn)類呢,又該如何呢?
4. 添加 UserDaoImpl2 一樣實(shí)現(xiàn) userDao 的接口,代碼如下:
@Repository
public class UserDaoImpl1 implements UserDao {
public void saveUser() {
System.out.println("執(zhí)行dao1的保存方法");
}
}
測(cè)試結(jié)果
結(jié)果解釋:
可以看到上面控制臺(tái)打印的異常堆棧信息,清楚的提示錯(cuò)誤原因,沒(méi)有指定的 bean 實(shí)例 UserDao 類型的,期待單個(gè) bean 匹配,但是找到了兩個(gè)。
一個(gè)是 userDaoImpl
一個(gè)是 userDaoImpl1
??吹竭@可以證明: @Autowired
注解是按照類型注入,如果匹配的類型多了就會(huì)報(bào)錯(cuò)。
疑問(wèn)導(dǎo)出:
難道使用了 Spring 框架以后,我們的接口只能有一個(gè)實(shí)現(xiàn)類嗎? 當(dāng)然不可能,畢竟我們看 Spring 的源碼的時(shí)候 已經(jīng)看到了,很多的接口對(duì)應(yīng)一大堆的實(shí)現(xiàn)類。
那么,針對(duì)這種多個(gè)接口實(shí)例的情況,怎么解決的呢?繼續(xù)我們注解的學(xué)習(xí)。
2.3 @Qualifier 注解
1. 此注解的作用,我們介紹過(guò)了,這里再看一下:它的作用是在按照類型注入的基礎(chǔ)之上,再按照 Bean 的 id 注入,不能單獨(dú)使用,搭配上面的 @Autiwired
注解。
在兩個(gè)實(shí)現(xiàn)類的基礎(chǔ)之上改造代碼如下:
@Service
public class UserServiceImpl implements UserService {
@Qualifier("userDaoImpl")
@Autowired
private UserDao userDao;
public void saveUser() {
System.out.println("執(zhí)行service中的保存邏輯");
userDao.saveUser();
}
}
代碼解釋
在屬性注入的地方,通過(guò)注解 @Qualifier
的參數(shù),指定了注入的 bean 實(shí)例 id 為 userDaoImpl。
2. 測(cè)試方法繼續(xù)執(zhí)行查看結(jié)果。
結(jié)果解釋:
那么可以看到,我們的方法正常執(zhí)行,而且執(zhí)行的就是 userDaoImpl 中的方法。
3. 繼續(xù)改造 service 的代碼如下,將 @Qualifier
注解中的值換成 userDaoImpl1 以后再看看結(jié)果。
@Service
public class UserServiceImpl implements UserService {
@Qualifier("userDaoImpl1")
@Autowired
private UserDao userDao;
public void saveUser() {
System.out.println("執(zhí)行service中的保存邏輯");
userDao.saveUser();
}
}
結(jié)果如下:
通過(guò)修改 @Qualifier
注解中 id 的屬性值 ,可以分別注入不同的實(shí)現(xiàn)類,那么證明了 @Qualifier
注解的作用。
2.4 @Resources 注解
此注解的作用是指定依賴按照 id 注入,還是按照類型注入。當(dāng)只使用注解,但是不指定注入方式的時(shí)候,默認(rèn)按照 id 注入,找不到時(shí)再按照類型注入。
語(yǔ)法如下:
@Resource //默認(rèn)按照 id 為 userDao的bean實(shí)例注入
@Resource(name="userDao") //按照 id 為 userDao的bean實(shí)例注入
@Resource(type="UserDao") //按照 類型 為 UserDao的bean實(shí)例注入
這里就只做個(gè)語(yǔ)法的介紹,注解的使用大同小異,大家按照上方步驟自行測(cè)試即可。
3. 小結(jié)
本節(jié)重點(diǎn)講解注解注入依賴的使用,咱們做個(gè)總結(jié):
1. 常用的注解有 3 種 :@Autowired
@Qualifier
@Resources
;
2. 注解注入的形式兩種 : 按照 bean 的 id 注入,或者按照 bean 的類型注入;
3. 哪種注解的目的,都是為了成功的注入使用的依賴,所以為了我們的開(kāi)發(fā)服務(wù),大家靈活使用即可。
不費(fèi)力氣就能得到的… 只有年齡。