您好,登錄后才能下訂單哦!
本篇內容主要講解“@Autowired注入依賴失敗的問題如何解決”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“@Autowired注入依賴失敗的問題如何解決”吧!
在Spring Boot項目中使用@Autowired注解,程序啟動時發現服務啟動失敗,提示:
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.
這里的錯誤原因很好分析。結合報錯信息及代碼,報錯處的代碼為FallbackMetrics注解了@Autowired進行依賴注入,但是沒有找到可以被用來注入的實例。即Spring Boot獲取FallbackMetrics的實例失敗。
根據分析,需要檢查可能導致Spring Boot依賴注入失敗的因素。
1.檢查掃描路徑。
掃描路徑是由@ComponentScan來指定的,默認為標注類當前包及當前包的子包。
也就是說,標注了@ComponentScan的啟動類放在com.be.fallback包下面,只會掃描com.be.fallback包中的類,以及com.be.fallback.servlet、com.be.fallback.util等子包中的類,對于com.be.service等包中的類是不會掃描的。
注意事項一:很多人沒有使用@ComponentScan,但是使用了@SpringBootApplication。@SpringBootApplication是通過內部封裝@ComponentScan注解來實現實例掃描的,所以使用@SpringBootApplication也是一樣的。
注意事項二:也可以通過為@ComponentScan或@SpringBootApplication注解指定參數來修改掃描路勁,示例:
// 如果使用@ComponentScan注解: @ComponentScan(basePackages = "com.be.fallback") // 如果使用@SpringBootApplication注解: @SpringBootApplication(scanBasePackages = "com.be.fallback")
2.檢查實例注冊。
檢查想要使用@Autowired注解自動注入依賴的類,是否標注了用來注冊給Spring Boot的注解。這些注解包括@Component,@Service,@Repository,@Controller等。
3.其他問題。
如果上述步驟檢查完成,服務啟動又沒有產生其他異常,這時候基本上已經排查代碼的問題。這時候需要檢查依賴、開發環境等是否有問題。檢查依賴需要了解自己需要哪些依賴,看是否配置齊全;檢查開發環境,可以通過將代碼拷貝到其他機器上執行來判斷。
這幾天更新升級了一下java編碼神器IDEA,升級完進行日常開發,可能是以前用的IDEA版本比較老舊,升級之后發現之前的日常寫法有了個warning提醒。來看圖:
如上圖,這就奇怪了,我們經常寫的業務層就是service接口層和對應的實現類層進行屬性注入的時候都是采用注解進行注入的。這也是springIOC給提供的比較方便的地方。我使用IDEA提供的自動修復提示修復了之后變成采用構造函數的形式進行注入了。
但是多年面向Spring開發的經驗告訴我,使用@Autowired注解進行依賴注入,肯定是沒有問題的。但是我的代碼潔癖不允許我這么不明不白的留一個警告在這里。所以,帶著我的潔癖,和我的好奇心,我開始研究起了這個警告。
我們簡單翻譯一下自動提示的是啥意思:
不建議直接在字段上進行依賴注入。
Spring 開發團隊建議:在Java Bean中永遠使用構造方法進行依賴注入。對于必須的依賴,永遠使用斷言來確認。
我們說明上面的問題之前先回顧幾個spring相關的問題:
Spring 有三種依賴注入的方式
1.基于屬性的注入
這種注入方式就是在bean的變量上使用注解進行依賴注入。本質上是通過反射的方式直接注入到field。這是我平常開發中看的最多也是最熟悉的一種方式。
@Autowired PushTaskService pushTaskService;
2.基于setter方法的注入
通過對應變量的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.基于構造方法的注入
將各個必需的依賴全部放在帶有注解構造方法的參數中,并在構造方法中完成對應變量的初始化,這種方式,就是基于構造方法的注入。比如:
@Autowired public ExcelListener(@Qualifier("taskGroupService") TaskGroupService taskGroupService) { this.taskGroupService = taskGroupService; }
我們一般開發需要注入屬性的時候都會使用的這個注解@Autowired,跟這個注解類似的還有2個,@Resource, @Inject。Spring 支持使用@Autowired, @Resource, @Inject 三個注解進行依賴注入。我們先看一下有啥區別:
@Autowired為Spring框架提供的注解,可以理解是Spring的親兒子。這里先給出一個示例代碼
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"); } }
測試方法
@SpringBootTest public class Stest { @Autowired // @Qualifier("indexServiceImpl2") IndexService indexService; @Test void gooo() { Assertions.assertNotNull(indexService); indexService.sayHello(); } }
按照type在上下文中查找匹配,查找type為IndexService的bean
如果有多個bean,則按照name進行匹配
如果有@Qualifier注解,則按照@Qualifier指定的name進行匹配,查找name為indexServiceImpl2的bean
如果沒有,則按照變量名進行匹配。查找name為indexService的bean
匹配不到,則報錯。(@Autowired(required=false),如果設置required為false(默認為true),則注入失敗時不會拋出異常)
在Spring 的環境下,@Inject和@Autowired 是相同的,因為它們的依賴注入都是使用AutowiredAnnotationBeanPostProcessor這個后置處理器來處理的。
這兩個的區別,首先@Inject是Java EE包里的,在SE環境需要單獨引入。另一個區別在于@Autowired可以設置required=false而@Inject并沒有這個屬性。也有的說@Inject是spring的干兒子。
@Resource是JSR-250定義的注解。Spring 在 CommonAnnotationBeanPostProcessor實現了對JSR-250的注解的處理,其中就包括@Resource。
這個@Resource有2個屬性name和type。在spring中name屬性定義為bean的名字,type這是bean的類型。
如果屬性上加@Resource注解那么他的注入流程是:
如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常。
如果指定了name,則從上下文中查找名稱匹配的bean進行裝配,找不到則拋出異常。
如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或是找到多個,都會拋出異常。
如果既沒有指定name,又沒有指定type,則默認按照byName方式進行裝配;如果沒有匹配,按照byType進行裝配。
上面的內容都了解了之后我們接下來看為啥IDEA會有個warning提醒。IDEA 提示 Field injection is not recommended。warning提醒的注入方式就是第一種使用屬性注解的方式進行注入。
屬性注入優點
代碼看起來很簡單,通俗易懂。你的類可以專注于業務而不被依賴注入所污染。你只需要把@Autowired扔到變量之上就好了方便開發人員進行代碼的編寫。
屬性注入可能出現的問題
問題1
基于 field 的注入,雖然不是絕對禁止使用,但是它可能會帶來一些隱含的問題。來我們舉個例子:
@Autowired private Person person; private String company; public UserServiceImpl(){ this.company = person.getCompany(); }
初看起來好像沒有什么問題,Person 類會被作為一個依賴被注入到當前類中,同時這個類的 company 屬性將在初始化時通過person.getCompany() 方法來獲得值。我們嘗試運行一下就會發現報錯了。其實類似這個問題有人在stackoverflow上提問過,點我跳轉。
Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Constructor threw exception; nested exception is java.lang.NullPointerException
在執行this.company = person.getCompany();這段代碼的時候報了空指針。
出現這個問題的原因是,Java 在初始化一個類時,是按照靜態變量或靜態語句塊 –> 實例變量或初始化語句塊 –> 構造方法 -> @Autowired 的順序。所以在執行這個類的構造方法時,person 對象尚未被注入,它的值還是 null。
問題2
使用這種基于 field 注入的方式,添加依賴是很簡單的,就算你的類中有十幾個依賴你可能都覺得沒有什么問題,如果你一個類注入非常多的其它的對象,擁有太多的依賴通常意味著你的類要承擔更多的責任,明顯違背了單一職責原則。順便我看了一下我們現在的業務代碼這個問題在我們的項目代碼中真的很常見。
問題3
這種注入形式就會造成你的類不能繞過反射(例如單元測試的時候)進行實例化,必須通過依賴容器才能實例化。也就是類和依賴容器強耦合,不能在容器外使用。
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.
其實大概的意思就是2句話
強制依賴就用構造器方式
可選、可變的依賴就用setter注入
spring對采用構造方法注入的說明
Spring 團隊提倡使用基于構造方法的注入,因為這樣一方面可以將依賴注入到一個不可變的變量中 (注:final 修飾的變量),另一方面也可以保證這些變量的值不會是 null。此外,經過構造方法完成依賴注入的組件 (注:比如各個 service),在被調用時可以保證它們都完全準備好了。與此同時,從代碼質量的角度來看,一個巨大的構造方法通常代表著出現了代碼結構問題,這個類可能承擔了過多的責任。
spring對采用setter方法注入的說明
基于 setter 的注入,則只應該被用于注入非必需的依賴,同時在類中應該對這個依賴提供一個合理的默認值。如果使用 setter 注入必需的依賴,那么將會有過多的 null 檢查充斥在代碼中。使用 setter 注入的一個優點是,這個依賴可以很方便的被改變或者重新注入。
到此,相信大家對“@Autowired注入依賴失敗的問題如何解決”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。