@Autowired與@Resource有何區(qū)別

一、相同點(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的裝配順序圖
1
2屈扎、@Resource的裝配順序圖
2.1 如果同時(shí)指定了name和type
2
2.2 如果指定了name
3
2.3 如果指定了type
4
2.4 如果既沒有指定name,也沒有指定type
5

三模蜡、選擇

@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)依賴問題航罗。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茫舶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子荤懂,更是在濱河造成了極大的恐慌,老刑警劉巖册烈,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異灶搜,居然都是意外死亡剿涮,警方通過查閱死者的電腦和手機(jī)猿棉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來键兜,“玉大人苇侵,你說我怎么就攤上這事榆浓《镀海” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)斩例,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任香府,我火速辦了婚禮董栽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘企孩。我一直安慰自己锭碳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布柠硕。 她就那樣靜靜地躺著工禾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝗柔。 梳的紋絲不亂的頭發(fā)上闻葵,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音癣丧,去河邊找鬼槽畔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛胁编,可吹牛的內(nèi)容都是我干的厢钧。 我是一名探鬼主播鳞尔,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼早直!你這毒婦竟也來了寥假?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤霞扬,失蹤者是張志新(化名)和其女友劉穎糕韧,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喻圃,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萤彩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斧拍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雀扶。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖肆汹,靈堂內(nèi)的尸體忽然破棺而出愚墓,到底是詐尸還是另有隱情,我是刑警寧澤县踢,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布转绷,位于F島的核電站伟件,受9級(jí)特大地震影響硼啤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜斧账,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一谴返、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咧织,春花似錦嗓袱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至闪萄,卻和暖如春梧却,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背败去。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工放航, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人圆裕。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓广鳍,卻偏偏與公主長(zhǎng)得像荆几,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赊时,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容