@Autowired和@Resource這兩個(gè)注解最大的區(qū)別:
- @Autowired 根據(jù)類型注入
- @Resource 根據(jù)名稱注入
以上是這兩個(gè)注解最主要的裝配方式,具體使用方式見下方。
以下只針對(duì)于一個(gè)接口有一個(gè)或多個(gè)實(shí)現(xiàn)類的情況進(jìn)行討論。沒有實(shí)現(xiàn)類的情況不討論,個(gè)人覺得自動(dòng)注入允許為空的情況是十分不安全的,實(shí)際開發(fā)中也沒有太大意義。
@Autowired
1.接口與實(shí)現(xiàn)類一一對(duì)應(yīng)
接口:
package com.example.demo.service;
/**
* @author chenhy
* @date 2021/4/11
*/
public interface IUserService {
void say();
}
實(shí)現(xiàn)類01:
package com.example.demo.service.impl;
import com.example.demo.service.IUserService;
import org.springframework.stereotype.Service;
/**
* @author chenhy
* @date 2021/4/11
*/
@Service
public class UserServiceImpl01 implements IUserService {
@Override
public void say() {
System.out.println("I am UserServiceImpl01.................");
}
}
測(cè)試類:
package com.example.demo.test;
import com.example.demo.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author chenhy
* @date 2021/4/11
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
@Autowired
IUserService userService;
@Test
public void test() {
userService.say();
}
}
運(yùn)行測(cè)試方法,控制臺(tái)輸出“I am UserServiceImpl01.................”。
此時(shí),Spring容器中只有一個(gè)IUserService 的實(shí)現(xiàn)類UserServiceImpl01 ,byType進(jìn)行注入時(shí),會(huì)自動(dòng)找到實(shí)現(xiàn)類UserServiceImpl01進(jìn)行注入。從控制臺(tái)的打印結(jié)果我們也可以看出,調(diào)用的是類UserServiceImpl01的方法。驗(yàn)證了@Autowired是根據(jù)類型注入的觀點(diǎn)。
2.接口與實(shí)現(xiàn)類的關(guān)系為一對(duì)多
新建實(shí)現(xiàn)類02:
package com.example.demo.service.impl;
import com.example.demo.service.IUserService;
import org.springframework.stereotype.Service;
/**
* @author chenhy
* @date 2021/4/11
*/
@Service
public class UserServiceImpl02 implements IUserService {
@Override
public void say() {
System.out.println("I am UserServiceImpl02.................");
}
}
運(yùn)行測(cè)試方法,控制臺(tái)報(bào)錯(cuò)如下:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.demo.service.IUserService' available: expected single matching bean but found 2: userServiceImpl01,userServiceImpl02
意思就是有多個(gè)實(shí)現(xiàn)類,程序不知道到底該注入哪個(gè)實(shí)現(xiàn)類了。
測(cè)試類處理如下:
package com.example.demo.test;
import com.example.demo.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author chenhy
* @date 2021/4/11
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
@Autowired
@Qualifier("userServiceImpl02")
IUserService userService;
@Test
public void test() {
userService.say();
}
}
運(yùn)行測(cè)試方法,控制臺(tái)輸出“I am UserServiceImpl02.................”。
我們新增了@Qualifier注解,該注解與@Autowired結(jié)合使用,可以在一個(gè)接口有多個(gè)實(shí)現(xiàn)類的情況下,指定注入的實(shí)現(xiàn)類的名稱。此處,而我們注入了第二個(gè)實(shí)現(xiàn)類UserServiceImpl02。(注意,Qualifier注解中的名稱首字母為小寫)。
@Autowired+@Qualifier的使用,實(shí)現(xiàn)了bean自動(dòng)注入時(shí),先按照類型再按照名稱進(jìn)行注入的功能。
@Resource
1.根據(jù)名稱注入(byName)
新建實(shí)現(xiàn)類:
package com.example.demo.service.impl;
import com.example.demo.service.IUserService;
import org.springframework.stereotype.Service;
/**
* @author chenhy
* @date 2021/4/11
*/
@Service
public class UserService implements IUserService {
@Override
public void say() {
System.out.println("I am UserService.................");
}
}
修改測(cè)試類如下:
package com.example.demo.test;
import com.example.demo.service.IUserService;
import com.example.demo.service.impl.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* @author chenhy
* @date 2021/4/11
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
// @Autowired
// @Qualifier("userServiceImpl02")
@Resource
IUserService userService;
@Test
public void test() {
userService.say();
}
}
其他文件不做改動(dòng)。
現(xiàn)在的整體情況是,IUserService 有3個(gè)實(shí)現(xiàn)類,如果按類型注入的話肯定是失敗的。
運(yùn)行測(cè)試方法,控制臺(tái)輸出“I am UserService.................”。
由此可見,@Resource在進(jìn)行bean注入時(shí),首先會(huì)按照名稱(byName)進(jìn)行裝配。
2.根據(jù)類型注入
將UserService.java全部注釋掉,再次運(yùn)行測(cè)試方法進(jìn)行測(cè)試。
運(yùn)行測(cè)試程序,此時(shí)控制臺(tái)報(bào)錯(cuò)如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.example.demo.test.UserTest': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.demo.service.IUserService' available: expected single matching bean but found 2: userServiceImpl01,userServiceImpl02
與之前測(cè)試Autowired注解的情形相同,按照類型注入失敗了。
測(cè)試類處理如下:
package com.example.demo.test;
import com.example.demo.service.IUserService;
import com.example.demo.service.impl.UserServiceImpl01;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* @author chenhy
* @date 2021/4/11
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
// @Autowired
// @Qualifier("userServiceImpl02")
@Resource(type = UserServiceImpl01.class)
IUserService userService;
@Test
public void test() {
userService.say();
}
}
運(yùn)行測(cè)試程序,控制臺(tái)輸出“I am UserServiceImpl01.................”。
通過在@Resource注解中指定注入的實(shí)現(xiàn)類來實(shí)現(xiàn)bean的注入。
此外,還可以在@Resource注解中指定類名稱。
總結(jié)
| 對(duì)比項(xiàng) | @Autowired | @Resource |
|---|---|---|
| 注解來源 | Spring注解 | JDK注解(JSR-250標(biāo)準(zhǔn)注解,屬于J2EE) |
| 裝配方式 | 默認(rèn)byType,其次byName | 默認(rèn)byName,其次byType |
| 屬性 | required | name、type |
| 作用范圍 | 字段、setter方法、構(gòu)造器 | 字段、setter方法 |
1.處理這2個(gè)注解的BeanPostProcessor不一樣
CommonAnnotationBeanPostProcessor是處理@ReSource注解的;
AutoWiredAnnotationBeanPostProcessor是處理@AutoWired注解的。
2.注入方式不同
@Autowired只按照byType注入;
@Resource默認(rèn)按byName自動(dòng)注入,也提供按照byType注入。
3.屬性不同
@Autowired按類型裝配依賴對(duì)象,默認(rèn)情況下它要求依賴對(duì)象必須存在,如果允許null值,可以設(shè)置它required屬性為false。如果我們想使用按名稱裝配,可以結(jié)合@Qualifier注解一起使用。
@Resource有兩個(gè)中重要的屬性:name和type。name屬性指定byName,如果沒有指定name屬性,當(dāng)注解標(biāo)注在字段上,即默認(rèn)取字段的名稱作為bean名稱尋找依賴對(duì)象,當(dāng)注解標(biāo)注在屬性的setter方法上,即默認(rèn)取屬性名作為bean名稱尋找依賴對(duì)象。需要注意的是,@Resource如果沒有指定name屬性,并且按照默認(rèn)的名稱仍然找不到依賴對(duì)象時(shí), @Resource注解會(huì)回退到按類型裝配。但一旦指定了name屬性,就只能按名稱裝配了。
4.裝配順序不同
@Autowired:首先通過類型來查找bean,如果只找到一個(gè),則直接注入,如果沒有找到,則拋出異常;如果找到多個(gè)bean也會(huì)拋出異常。
解決方法一:可以在配置bean的時(shí)候加上@Primary注解,來提高優(yōu)先級(jí),這樣就不會(huì)報(bào)錯(cuò);
解決方法二:會(huì)默認(rèn)使用字段名來匹配,如果沒有匹配上,拋出異常。
如果需要直接通過bean的id來查找,可以配合@Qualifier來使用,沒有找到拋出異常。
這里重點(diǎn)需要指出,使用@Autowired 是有優(yōu)先級(jí)的,@Qualifier > 按類型找(如果找到多個(gè)繼續(xù)使用之后的策略) > @Primary > 按名字找
@Autowired通過設(shè)置required=false,在沒有找到bean的情況下,不會(huì)拋出異常。
@Resource裝配順序:
如果同時(shí)指定了name和type,則從Spring上下文中找到唯一匹配的bean進(jìn)行裝配,找不到則拋出異常。
如果指定了name,則從上下文中查找名稱(id)匹配的bean進(jìn)行裝配,找不到則拋出異常。
如果指定了type,則從上下文中找到類似匹配的唯一bean進(jìn)行裝配,找不到或是找到多個(gè),都會(huì)拋出異常。
如果既沒有指定name,又沒有指定type,則自動(dòng)按照byName方式進(jìn)行裝配;如果沒有匹配,則回退為一個(gè)原始類型進(jìn)行匹配,如果匹配則自動(dòng)裝配。