溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

@Autowired注入依賴失敗的問題如何解決

發(fā)布時(shí)間:2022-08-26 15:14:10 來源:億速云 閱讀:194 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“@Autowired注入依賴失敗的問題如何解決”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“@Autowired注入依賴失敗的問題如何解決”吧!

@Autowired注入依賴失敗的問題

1、現(xiàn)象描述

在Spring Boot項(xiàng)目中使用@Autowired注解,程序啟動(dòng)時(shí)發(fā)現(xiàn)服務(wù)啟動(dòng)失敗,提示:

Description:
 
Field metrics in com.be.fallback.servlet.FallbackServlet required a bean of type 'com.be.fallback.metrics.FallbackMetrics' that could not be found.
 
The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)
 
 
Action:
 
Consider defining a bean of type 'com.be.fallback.metrics.FallbackMetrics' in your configuration.

2、問題分析

這里的錯(cuò)誤原因很好分析。結(jié)合報(bào)錯(cuò)信息及代碼,報(bào)錯(cuò)處的代碼為FallbackMetrics注解了@Autowired進(jìn)行依賴注入,但是沒有找到可以被用來注入的實(shí)例。即Spring Boot獲取FallbackMetrics的實(shí)例失敗。

3、解決方案

根據(jù)分析,需要檢查可能導(dǎo)致Spring Boot依賴注入失敗的因素。

1.檢查掃描路徑。

掃描路徑是由@ComponentScan來指定的,默認(rèn)為標(biāo)注類當(dāng)前包及當(dāng)前包的子包。

也就是說,標(biāo)注了@ComponentScan的啟動(dòng)類放在com.be.fallback包下面,只會(huì)掃描com.be.fallback包中的類,以及com.be.fallback.servlet、com.be.fallback.util等子包中的類,對(duì)于com.be.service等包中的類是不會(huì)掃描的。

  • 注意事項(xiàng)一:很多人沒有使用@ComponentScan,但是使用了@SpringBootApplication。@SpringBootApplication是通過內(nèi)部封裝@ComponentScan注解來實(shí)現(xiàn)實(shí)例掃描的,所以使用@SpringBootApplication也是一樣的。

  • 注意事項(xiàng)二:也可以通過為@ComponentScan或@SpringBootApplication注解指定參數(shù)來修改掃描路勁,示例:

// 如果使用@ComponentScan注解:
@ComponentScan(basePackages = "com.be.fallback")
 
// 如果使用@SpringBootApplication注解:
@SpringBootApplication(scanBasePackages = "com.be.fallback")

2.檢查實(shí)例注冊(cè)。

檢查想要使用@Autowired注解自動(dòng)注入依賴的類,是否標(biāo)注了用來注冊(cè)給Spring Boot的注解。這些注解包括@Component,@Service,@Repository,@Controller等。

3.其他問題。

如果上述步驟檢查完成,服務(wù)啟動(dòng)又沒有產(chǎn)生其他異常,這時(shí)候基本上已經(jīng)排查代碼的問題。這時(shí)候需要檢查依賴、開發(fā)環(huán)境等是否有問題。檢查依賴需要了解自己需要哪些依賴,看是否配置齊全;檢查開發(fā)環(huán)境,可以通過將代碼拷貝到其他機(jī)器上執(zhí)行來判斷。

@Autowired依賴注入為啥不推薦了

這幾天更新升級(jí)了一下java編碼神器IDEA,升級(jí)完進(jìn)行日常開發(fā),可能是以前用的IDEA版本比較老舊,升級(jí)之后發(fā)現(xiàn)之前的日常寫法有了個(gè)warning提醒。來看圖:

@Autowired注入依賴失敗的問題如何解決

如上圖,這就奇怪了,我們經(jīng)常寫的業(yè)務(wù)層就是service接口層和對(duì)應(yīng)的實(shí)現(xiàn)類層進(jìn)行屬性注入的時(shí)候都是采用注解進(jìn)行注入的。這也是springIOC給提供的比較方便的地方。我使用IDEA提供的自動(dòng)修復(fù)提示修復(fù)了之后變成采用構(gòu)造函數(shù)的形式進(jìn)行注入了。

@Autowired注入依賴失敗的問題如何解決

但是多年面向Spring開發(fā)的經(jīng)驗(yàn)告訴我,使用@Autowired注解進(jìn)行依賴注入,肯定是沒有問題的。但是我的代碼潔癖不允許我這么不明不白的留一個(gè)警告在這里。所以,帶著我的潔癖,和我的好奇心,我開始研究起了這個(gè)警告。

警告內(nèi)容

@Autowired注入依賴失敗的問題如何解決

我們簡(jiǎn)單翻譯一下自動(dòng)提示的是啥意思:

不建議直接在字段上進(jìn)行依賴注入。

Spring 開發(fā)團(tuán)隊(duì)建議:在Java Bean中永遠(yuǎn)使用構(gòu)造方法進(jìn)行依賴注入。對(duì)于必須的依賴,永遠(yuǎn)使用斷言來確認(rèn)。

我們說明上面的問題之前先回顧幾個(gè)spring相關(guān)的問題:

依賴注入的方式

Spring 有三種依賴注入的方式

1.基于屬性的注入

這種注入方式就是在bean的變量上使用注解進(jìn)行依賴注入。本質(zhì)上是通過反射的方式直接注入到field。這是我平常開發(fā)中看的最多也是最熟悉的一種方式。

@Autowired
PushTaskService pushTaskService;

2.基于setter方法的注入

通過對(duì)應(yīng)變量的setXXX()方法以及在方法上面使用注解,來完成依賴注入。比如:

private static TaskGroupTemplateRepository taskGroupTemplateRepository;
private static TaskGroupService taskGroupService;

@Autowired
public void setTaskGroupTemplateRepository(TaskGroupTemplateRepository taskGroupTemplateRepository,TaskGroupService taskGroupService){
    ExcelListener2.taskGroupTemplateRepository = taskGroupTemplateRepository;
    ExcelListener2.taskGroupService = taskGroupService;
}

說明:在 Spring 4.5 及更高的版本中,setXXX 上面的 @Autowired 注解是可以不寫的。

3.基于構(gòu)造方法的注入

將各個(gè)必需的依賴全部放在帶有注解構(gòu)造方法的參數(shù)中,并在構(gòu)造方法中完成對(duì)應(yīng)變量的初始化,這種方式,就是基于構(gòu)造方法的注入。比如:

@Autowired
public ExcelListener(@Qualifier("taskGroupService") TaskGroupService taskGroupService) {
    this.taskGroupService = taskGroupService;
}

@Autowired是干啥的

我們一般開發(fā)需要注入屬性的時(shí)候都會(huì)使用的這個(gè)注解@Autowired,跟這個(gè)注解類似的還有2個(gè),@Resource, @Inject。Spring 支持使用@Autowired, @Resource, @Inject 三個(gè)注解進(jìn)行依賴注入。我們先看一下有啥區(qū)別:

@Autowired為Spring框架提供的注解,可以理解是Spring的親兒子。這里先給出一個(gè)示例代碼

public interface IndexService {

    void sayHello();
}

@Service
public class IndexServiceImpl implements IndexService {

    @Override
    public void sayHello() {
        System.out.println("hello, this is IndexServiceImpl");
    }

}

@Service
public class IndexServiceImpl2 implements IndexService {

    @Override
    public void sayHello() {
        System.out.println("hello, this is IndexServiceImpl2");
    }

}

測(cè)試方法

@SpringBootTest
public class Stest {

    @Autowired
    // @Qualifier("indexServiceImpl2")
    IndexService indexService;

    @Test
    void gooo() {
        Assertions.assertNotNull(indexService);
        indexService.sayHello();
    }
}

按照type在上下文中查找匹配,查找type為IndexService的bean

  • 如果有多個(gè)bean,則按照name進(jìn)行匹配

如果有@Qualifier注解,則按照@Qualifier指定的name進(jìn)行匹配,查找name為indexServiceImpl2的bean

如果沒有,則按照變量名進(jìn)行匹配。查找name為indexService的bean

  • 匹配不到,則報(bào)錯(cuò)。(@Autowired(required=false),如果設(shè)置required為false(默認(rèn)為true),則注入失敗時(shí)不會(huì)拋出異常)

@Inject是干啥的

在Spring 的環(huán)境下,@Inject和@Autowired 是相同的,因?yàn)樗鼈兊囊蕾囎⑷攵际鞘褂肁utowiredAnnotationBeanPostProcessor這個(gè)后置處理器來處理的。

@Autowired注入依賴失敗的問題如何解決

這兩個(gè)的區(qū)別,首先@Inject是Java EE包里的,在SE環(huán)境需要單獨(dú)引入。另一個(gè)區(qū)別在于@Autowired可以設(shè)置required=false而@Inject并沒有這個(gè)屬性。也有的說@Inject是spring的干兒子。

@Resource是干啥的

@Resource是JSR-250定義的注解。Spring 在 CommonAnnotationBeanPostProcessor實(shí)現(xiàn)了對(duì)JSR-250的注解的處理,其中就包括@Resource。

這個(gè)@Resource有2個(gè)屬性name和type。在spring中name屬性定義為bean的名字,type這是bean的類型。

如果屬性上加@Resource注解那么他的注入流程是:

  • 如果同時(shí)指定了name和type,則從Spring上下文中找到唯一匹配的bean進(jìn)行裝配,找不到則拋出異常。

  • 如果指定了name,則從上下文中查找名稱匹配的bean進(jìn)行裝配,找不到則拋出異常。

  • 如果指定了type,則從上下文中找到類型匹配的唯一bean進(jìn)行裝配,找不到或是找到多個(gè),都會(huì)拋出異常。

  • 如果既沒有指定name,又沒有指定type,則默認(rèn)按照byName方式進(jìn)行裝配;如果沒有匹配,按照byType進(jìn)行裝配。

上面的內(nèi)容都了解了之后我們接下來看為啥IDEA會(huì)有個(gè)warning提醒。IDEA 提示 Field injection is not recommended。warning提醒的注入方式就是第一種使用屬性注解的方式進(jìn)行注入。

屬性注入優(yōu)點(diǎn)

代碼看起來很簡(jiǎn)單,通俗易懂。你的類可以專注于業(yè)務(wù)而不被依賴注入所污染。你只需要把@Autowired扔到變量之上就好了方便開發(fā)人員進(jìn)行代碼的編寫。

屬性注入可能出現(xiàn)的問題

  • 問題1

基于 field 的注入,雖然不是絕對(duì)禁止使用,但是它可能會(huì)帶來一些隱含的問題。來我們舉個(gè)例子:

@Autowired
private Person person;

private String company;

public UserServiceImpl(){
    this.company = person.getCompany();
}

初看起來好像沒有什么問題,Person 類會(huì)被作為一個(gè)依賴被注入到當(dāng)前類中,同時(shí)這個(gè)類的 company 屬性將在初始化時(shí)通過person.getCompany() 方法來獲得值。我們嘗試運(yùn)行一下就會(huì)發(fā)現(xiàn)報(bào)錯(cuò)了。其實(shí)類似這個(gè)問題有人在stackoverflow上提問過,點(diǎn)我跳轉(zhuǎn)。

Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Constructor threw exception; nested exception is java.lang.NullPointerException

在執(zhí)行this.company = person.getCompany();這段代碼的時(shí)候報(bào)了空指針。

出現(xiàn)這個(gè)問題的原因是,Java 在初始化一個(gè)類時(shí),是按照靜態(tài)變量或靜態(tài)語句塊 –> 實(shí)例變量或初始化語句塊 –> 構(gòu)造方法 -> @Autowired 的順序。所以在執(zhí)行這個(gè)類的構(gòu)造方法時(shí),person 對(duì)象尚未被注入,它的值還是 null。

  • 問題2

使用這種基于 field 注入的方式,添加依賴是很簡(jiǎn)單的,就算你的類中有十幾個(gè)依賴你可能都覺得沒有什么問題,如果你一個(gè)類注入非常多的其它的對(duì)象,擁有太多的依賴通常意味著你的類要承擔(dān)更多的責(zé)任,明顯違背了單一職責(zé)原則。順便我看了一下我們現(xiàn)在的業(yè)務(wù)代碼這個(gè)問題在我們的項(xiàng)目代碼中真的很常見。

  • 問題3

這種注入形式就會(huì)造成你的類不能繞過反射(例如單元測(cè)試的時(shí)候)進(jìn)行實(shí)例化,必須通過依賴容器才能實(shí)例化。也就是類和依賴容器強(qiáng)耦合,不能在容器外使用。

spring建議

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.

其實(shí)大概的意思就是2句話

  • 強(qiáng)制依賴就用構(gòu)造器方式

  • 可選、可變的依賴就用setter注入

spring對(duì)采用構(gòu)造方法注入的說明

Spring 團(tuán)隊(duì)提倡使用基于構(gòu)造方法的注入,因?yàn)檫@樣一方面可以將依賴注入到一個(gè)不可變的變量中 (注:final 修飾的變量),另一方面也可以保證這些變量的值不會(huì)是 null。此外,經(jīng)過構(gòu)造方法完成依賴注入的組件 (注:比如各個(gè) service),在被調(diào)用時(shí)可以保證它們都完全準(zhǔn)備好了。與此同時(shí),從代碼質(zhì)量的角度來看,一個(gè)巨大的構(gòu)造方法通常代表著出現(xiàn)了代碼結(jié)構(gòu)問題,這個(gè)類可能承擔(dān)了過多的責(zé)任。

spring對(duì)采用setter方法注入的說明

基于 setter 的注入,則只應(yīng)該被用于注入非必需的依賴,同時(shí)在類中應(yīng)該對(duì)這個(gè)依賴提供一個(gè)合理的默認(rèn)值。如果使用 setter 注入必需的依賴,那么將會(huì)有過多的 null 檢查充斥在代碼中。使用 setter 注入的一個(gè)優(yōu)點(diǎn)是,這個(gè)依賴可以很方便的被改變或者重新注入。

到此,相信大家對(duì)“@Autowired注入依賴失敗的問題如何解決”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI