一、相同點(diǎn)
這個(gè)兩個(gè)注解都是用來完成組件的裝配的臂拓,即利用依賴注入(DI)潜腻,完成對(duì)IOC容器當(dāng)中各個(gè)組件之間依賴的裝配賦值。
二融涣、不同點(diǎn)
2.1 來源不同
2.1.1 @Resource
@Resource
是javaEE的注解,它遵循的是JSR-250規(guī)范剃斧,需要導(dǎo)入包javax.annotation.Resource
。
2.1.2 @Autowired
@Autowired
為Spring提供的注解忽你,需要導(dǎo)入包org.springframework.beans.factory.annotation.Autowired
幼东。
2.2 裝配順序不同
2.2.1 @Resource
默認(rèn)按照byName方式進(jìn)行裝配,屬于J2EE自帶注解科雳,沒有指定name時(shí)根蟹,name指的是變量名。
如果同時(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)裝配。
2.2.2 @Autowired
- 默認(rèn)按byType自動(dòng)注入约计,是Spring的注解。
- 默認(rèn)情況下必須要求依賴對(duì)象必須存在迁筛,如果要允許null值煤蚌,可以設(shè)置它的required屬性為false,
@Autowired(required = false)
细卧。 - 按類型裝配的過程中尉桩,如果發(fā)現(xiàn)找到多個(gè)bean,則又按照byName方式進(jìn)行比對(duì)贪庙,如果還有多個(gè)蜘犁,則報(bào)出異常。
裝配順序圖
1这橙、@Autowired的裝配順序圖
2屈扎、@Resource的裝配順序圖
2.1 如果同時(shí)指定了name和type
2.2 如果指定了name
2.3 如果指定了type
2.4 如果既沒有指定name,也沒有指定type
三模蜡、選擇
@Autowired
跟Spring強(qiáng)耦合了忍疾,如果換成了JFinal等其他框架膝昆,功能就會(huì)失效荚孵。而@Resource
是JSR-250提供的收叶,它是Java標(biāo)準(zhǔn)判没,絕大部分框架都支持,因此個(gè)人更傾向于選@Resource
嫉沽。
四绸硕、樣例代碼
4.1 接口只有一個(gè)實(shí)現(xiàn)類
4.1.1 接口
public interface Animal {
String talk();
}
4.1.2 實(shí)現(xiàn)類
/**
* 注意:@Service 默認(rèn)value=dog,首字母小寫
*/
@Service
public class Dog implements Animal {
@Override
public String talk() {
return "Dog talk:";
}
}
4.1.3 裝配類
@RestController
@Controller(value = "/zoo")
@RequestMapping("/zoo")
public class ZooController {
/**
* 1玻佩、裝配方式:既沒有指定name咬崔,又沒有指定type垮斯,則默認(rèn)按照byName方式進(jìn)行裝配甚脉。
* 2牺氨、裝配過程:通過byName沒有找到name=animal的對(duì)象猴凹,則用byType去裝配郊霎,找到了type為Animal的對(duì)象Dog,這里注入的是Dog對(duì)象爷绘。
*/
@Resource
private Animal animal;
/**
* 1土至、裝配方式:既沒有指定name陶因,又沒有指定type,則默認(rèn)按照byName方式進(jìn)行裝配贴见。
* 2片部、裝配過程:通過byName找到了name=dog的對(duì)象吞琐,直接注入。
*/
@Resource
private Animal dog;
/**
* 1黍图、裝配方式:默認(rèn)按byType自動(dòng)注入
* 2助被、裝配過程:通過byType去裝配揩环,找到type為Animal的對(duì)象Dog,這里注入的是Dog對(duì)象。
*/
@Autowired
private Animal animal2;
@GetMapping("/talk")
public void animalTalk() {
animal.talk();
dog.talk();
animal2.talk();
}
}
4.1.4 結(jié)果
可以正常編譯啟動(dòng)顾犹,運(yùn)行正常炫刷。
4.2 接口有多個(gè)實(shí)現(xiàn)類
4.2.1 接口
public interface Animal {
String talk();
}
4.2.2 實(shí)現(xiàn)類
/**
* 注意:@Service 默認(rèn)value=dog,首字母小寫
*/
@Service
public class Dog implements Animal {
@Override
public String talk() {
return "Dog talk:";
}
}
/**
* 注意:@Service 默認(rèn)value=cat,首字母小寫
*/
@Service
public class Cat implements Animal {
@Override
public String talk() {
return "Cat talk";
}
}
4.2.3 裝配類
4.2.3.1 @Resource默認(rèn)按byName-失敗
@RestController
@Controller(value = "/zoo")
@RequestMapping("/zoo")
public class ZooController {
/**
* 1浑玛、裝配方式:既沒有指定name顾彰,又沒有指定type涨享,則默認(rèn)按照byName方式進(jìn)行裝配灰伟。
* 2栏账、裝配過程:通過byName沒有找到name=animal的對(duì)象挡爵,則用byType去裝配茶鹃,找到了type為Animal的對(duì)象有兩個(gè):Dog闭翩、Cat,因此注入失敗兑障。
*/
@Resource
private Animal animal;
}
啟動(dòng)錯(cuò)誤信息
No qualifying bean of type 'com.alanchen.Animal' available: expected single matching bean but found 2: cat,dog
4.2.3.2 @Resource默認(rèn)按byName-成功
@RestController
@Controller(value = "/zoo")
@RequestMapping("/zoo")
public class ZooController {
/**
* 1流译、裝配方式:既沒有指定name福澡,又沒有指定type革砸,則默認(rèn)按照byName方式進(jìn)行裝配业岁。
* 2笔时、裝配過程:通過byName找到了name=dog的對(duì)象允耿,直接注入较锡。
*/
@Resource
private Animal dog;
}
4.2.3.3 @Resource指定name按byName-成功
@RestController
@Controller(value = "/zoo")
@RequestMapping("/zoo")
public class ZooController {
/**
* 1蚂蕴、裝配方式:指定了name,則從上下文中查找名稱(id)匹配的bean進(jìn)行裝配熔号,找不到則拋出異常引镊。
* 2弟头、裝配過程:通過byName找到了name=dog的對(duì)象赴恨,直接注入伦连。
*/
@Resource(name = "dog")
private Animal animal;
}
4.2.3.4 @Autowired默認(rèn)按byType-失敗
@RestController
@Controller(value = "/zoo")
@RequestMapping("/zoo")
public class ZooController {
/**
* 1、裝配方式:默認(rèn)按byType自動(dòng)注入
* 2沛膳、裝配過程:通過byType去裝配锹安,找到了type為Animal的對(duì)象有兩個(gè):Dog叹哭、Cat,因此注入失敗糠排。
*/
@Autowired
private Animal animal;
}
啟動(dòng)錯(cuò)誤信息
Field animal in com.alanchen.ZooController required a single bean, but 2 were found:
4.2.3.5 @Autowired和@Qualifier一起配合按byName-成功
@RestController
@Controller(value = "/zoo")
@RequestMapping("/zoo")
public class ZooController {
/**
* 1入宦、裝配方式:和@Qualifier一起配合按byName
* 2乾闰、裝配過程:通過byName找到了name=dog的對(duì)象涯肩,直接注入病苗。
*/
@Autowired
@Qualifier("dog")
private Animal animal;
}
五、Spring注解
5.1 @Qualifier
@Qualifier
意思是合格者继谚,一般跟@Autowired
配合使用花履,需要指定一個(gè)bean的名稱诡壁,通過bean名稱就能找到需要裝配的bean荠割。
5.2 @Primary
當(dāng)我們使用自動(dòng)配置的方式裝配bean時(shí),如果這個(gè)bean有多個(gè)候選者砰蠢,假如其中一個(gè)候選者具有@Primary
注解修飾,該候選者會(huì)被選中,作為自動(dòng)配置的值。
六其馏、@Autowired的使用范圍
平時(shí)我們使用@Autowired
都是使用在成員變量上叛复,但@Autowired
的強(qiáng)大之處扔仓,遠(yuǎn)非如此当辐。先看看@Autowired注解的定義:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
6.1 成員變量
在成員變量上使用Autowired注解
@Service
public class UserService {
@Autowired
private IUser user;
}
6.2 構(gòu)造器
在構(gòu)造器上使用Autowired注解
@Service
public class UserService {
private IUser user;
@Autowired
public UserService(IUser user) {
this.user = user;
System.out.println("user:" + user);
}
}
在構(gòu)造器上加Autowired注解缘揪,實(shí)際上還是使用了Autowired裝配方式,并非構(gòu)造器裝配慷吊。
6.3 方法
在普通方法上加Autowired注解
@Service
public class UserService {
@Autowired
public void test(IUser user) {
user.say();
}
}
Spring會(huì)在項(xiàng)目啟動(dòng)的過程中溉瓶,自動(dòng)調(diào)用一次加了@Autowired注解的方法堰酿,我們可以在該方法做一些初始化的工作触创。
也可以在setter方法上Autowired注解:
@Service
public class UserService {
private IUser user;
@Autowired
public void setUser(IUser user) {
this.user = user;
}
}
6.4 參數(shù)
可以在構(gòu)造器的入?yún)⑸霞覣utowired注解
@Service
public class UserService {
private IUser user;
public UserService(@Autowired IUser user) {
this.user = user;
System.out.println("user:" + user);
}
}
也可以在非靜態(tài)方法的入?yún)⑸霞覣utowired注解
@Service
public class UserService {
public void test(@Autowired IUser user) {
user.say();
}
}
七哼绑、@Autowired自動(dòng)裝配多個(gè)實(shí)例
平時(shí)我們一般都是通過@Autowired
自動(dòng)裝配單個(gè)實(shí)例抖韩,但這里我會(huì)告訴你茂浮,它也能自動(dòng)裝配多個(gè)實(shí)例块攒。
7.1 實(shí)現(xiàn)自動(dòng)裝配多個(gè)實(shí)例
將UserService方法調(diào)整一下佃乘,用一個(gè)List集合接收IUser類型的參數(shù)
@Service
public class UserService {
@Autowired
private List<IUser> userList;
@Autowired
private Set<IUser> userSet;
@Autowired
private Map<String, IUser> userMap;
public void test() {
System.out.println("userList:" + userList);
System.out.println("userSet:" + userSet);
System.out.println("userMap:" + userMap);
}
}
增加一個(gè)controller
@RequestMapping("/u")
@RestController
public class UController {
@Autowired
private UserService userService;
@RequestMapping("/test")
public String test() {
userService.test();
return "success";
}
}
userList趣避、userSet和userMap都打印出了兩個(gè)元素住练,說明@Autowired會(huì)自動(dòng)把相同類型的IUser對(duì)象收集到集合中讲逛。
7.2 用途
可以與策略模式來搭配使用岭埠,變種后的策略模式:
1、不需要content類來按條件選擇具體的策略類止喷。
2混聊、一個(gè)策略接口预愤,多個(gè)具體策略實(shí)現(xiàn)類鳖粟。
3拙绊、每個(gè)具體策略實(shí)現(xiàn)類标沪,通過入?yún)?shù)判斷自己是否需要執(zhí)行檩赢,如果不需要執(zhí)行违寞,直接返回趁曼。
4挡闰、調(diào)用策略的client類摄悯,通過@Autowired自動(dòng)裝配多個(gè)實(shí)例,一次性拿到所有策略實(shí)現(xiàn)類申钩,通過循環(huán)調(diào)用這些策略實(shí)現(xiàn)類撒遣。
5钢猛、有新的策略實(shí)現(xiàn)命迈,新建新的具體實(shí)現(xiàn)類就可以了壶愤,不需要像以前一樣去修改content類征椒,真正實(shí)現(xiàn)了開閉原則勃救。
示例代碼:
/**
* 推送策略類
*/
public interface IPushStrategy {
/**
* @param userId:用戶ID
* @param msg:推送消息
* @param supplier:推送供應(yīng)商
* @return
*/
boolean push(Long userId, String msg, String supplier);
}
/**
* 華為推送策略
*/
@Service
public class HuaWeiPushService implements IPushStrategy {
@Override
public boolean push(Long userId, String msg, String supplier) {
if ("HuaWei".equals(supplier)) {
System.out.println("華為推送給用戶" + userId + ",推送消息:" + msg);
return true;
}
return false;
}
}
/**
* Oppo推送策略
*/
@Service
public class OppoPushService implements IPushStrategy {
@Override
public boolean push(Long userId, String msg, String supplier) {
if ("Oppo".equals(supplier)) {
System.out.println("Oppo推送給用戶" + userId + ",推送消息:" + msg);
return true;
}
return false;
}
}
/**
* 小米推送策略
*/
@Service
public class XiaoMiPushService implements IPushStrategy {
@Override
public boolean push(Long userId, String msg, String supplier) {
if ("XiaoMi".equals(supplier)) {
System.out.println("小米推送給用戶" + userId + ",推送消息:" + msg);
return true;
}
return false;
}
}
/**
* 推送策略context
*/
@Component
public class PushContext implements IPushStrategy {
@Autowired
private List<IPushStrategy> pushStrategyList;
@Override
public boolean push(Long userId, String msg, String supplier) {
boolean supplierMatched = false;
for (IPushStrategy pushStrategy : pushStrategyList) {
supplierMatched = pushStrategy.push(userId, msg, supplier);
if (supplierMatched) {
break;
}
}
return supplierMatched;
}
}
@Api(tags = "推送策略模式")
@RestController
@RequestMapping("strategy")
public class TestController {
@Resource
private PushContext pushContext;
@ApiOperation(value = "推送")
@GetMapping("push")
public boolean push() {
Long userId = 273L;
String msg = "您有新的訂單";
String supplier = "HuaWei";
return pushContext.push(userId, msg, supplier);
}
}
八、@Autowired注入失敗場(chǎng)景
8.1 沒有加@Service注解
在類上面忘了加@Controller
晕讲、@Service
瓢省、@Component
、@Repository
等注解痊班,Spring就無法完成自動(dòng)裝配的功能勤婚。
8.2 注入Filter或Listener
web應(yīng)用啟動(dòng)的順序是:listener
->filter
->servlet
。眾所周知辩块,SpringMvc的啟動(dòng)是在DisptachServlet里面做的蛔六,而它是在listener和filter之后執(zhí)行荆永。如果我們想在listener和filter里面@Autowired
某個(gè)bean废亭,肯定是不行的,因?yàn)閒ilter初始化的時(shí)候具钥,此時(shí)bean還沒有初始化豆村,無法自動(dòng)裝配。如果工作當(dāng)中真的需要這樣做掌动,我們?cè)撊绾谓鉀Q這個(gè)問題呢?答案是使用WebApplicationContextUtils.getWebApplicationContext
獲取當(dāng)前的ApplicationContext,再通過它獲取到bean實(shí)例。
8.3 注解未被@ComponentScan掃描
通常情況下坐榆,@Controller
愉昆、@Service
、@Component
、@Repository
、@Configuration
等注解,是需要通過@ComponentScan
注解掃描,收集元數(shù)據(jù)的卖毁。但是,如果沒有加@ComponentScan
注解,或者@ComponentScan
注解掃描的路徑不對(duì),或者路徑范圍太小,會(huì)導(dǎo)致有些注解無法收集株旷,到后面無法使用@Autowired
完成自動(dòng)裝配的功能齿尽。有個(gè)好消息是炎疆,在springboot項(xiàng)目中全跨,如果使用了@SpringBootApplication
注解挪钓,它里面內(nèi)置了ComponentScan
注解的功能绍赛。
8.4 循環(huán)依賴問題
如果A依賴于B纯出,B依賴于C焕襟,C又依賴于A,這樣就形成了一個(gè)死循環(huán)。
Spring的bean默認(rèn)是單例的老赤,如果單例bean使用@Autowired
自動(dòng)裝配,大多數(shù)情況床未,能解決循環(huán)依賴問題传货。但是如果bean是多例的,會(huì)出現(xiàn)循環(huán)依賴問題粮宛,導(dǎo)致bean自動(dòng)裝配不了眷昆。還有有些情況下漂问,如果創(chuàng)建了代理對(duì)象,即使bean是單例的逢享,依然會(huì)出現(xiàn)循環(huán)依賴問題航罗。