寫在前面得話
學習@Autowired之前建議先學會使用byType和byName
Spring的自動裝配
https://hgm.vercel.app/post/63755f3a/
@Autowired詳解
首先要知道另一個東西,default-autowire,它是在xml檔案中進行配置的,可以設定為byName、byType、constructor和autodetect;比如byName,不用顯式的在bean中寫出依賴的對象,它會自動的比對其它bean中id名與本bean的set**相同的,并自動裝載。
@Autowired是用在JavaBean中的注解,通過byType形式,用來給指定的字段或方法注入所需的外部資源。
兩者的功能是一樣的,就是能減少或者消除屬性或構造器參數的設定,隻是配置地方不一樣而已。
autowire四種模式的差別
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAjM2EzLcd3LcJzLcJzdllmVldWYtl2Pn5GcuMDOiFjM3MzY3cDOyE2NzQGO1Q2MlFGZiJWZ0QDMkRzYvwlN1cjMwczNtUGall3LcVmdhNXLwRHdo9CXt92YucWbpRWdvx2Yx5yazF2Lc9CX6MHc0RHaiojIsJye.png)
先看一下bean執行個體化和@Autowired裝配過程:
一切都是從bean工廠的getBean方法開始的,一旦該方法調用總會傳回一個bean執行個體,無論目前是否存在,不存在就執行個體化一個并裝配,否則直接傳回。(Spring MVC是在什麼時候開始執行bean的執行個體化過程的呢?其實就在元件掃描完成之後)
執行個體化和裝配過程中會多次遞歸調用getBean方法來解決類之間的依賴。
Spring幾乎考慮了所有可能性,是以方法特别複雜但完整有條理。
@Autowired最終是根據類型來查找和裝配元素的,但是我們設定了後會影響最終的類型比對查找。因為在前面有根據BeanDefinition的autowire類型設定PropertyValue值得一步,其中會有新執行個體的建立和注冊。就是那個autowireByName方法。
下面通過@Autowired來說明一下用法
Setter 方法中的 @Autowired
你可以在 JavaBean中的 setter 方法中使用 @Autowired 注解。當 Spring遇到一個在 setter 方法中使用的 @Autowired 注解,它會在方法中執行 byType 自動裝配。
這裡是 TextEditor.java 檔案的内容:
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
private SpellChecker spellChecker;
@Autowired
public void setSpellChecker( SpellChecker spellChecker ){
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker( ) {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
複制
下面是另一個依賴的類檔案 SpellChecker.java 的内容:
package com.tutorialspoint;
public class SpellChecker {
public SpellChecker(){
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling(){
System.out.println("Inside checkSpelling." );
}
}
複制
下面是 MainApp.java 檔案的内容:
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
}
}
複制
下面是配置檔案 Beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<!-- Definition for textEditor bean without constructor-arg -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
</beans>
複制
一旦你已經完成的建立了源檔案和 bean 配置檔案,讓我們運作一下應用程式。如果你的應用程式一切都正常的話,這将會輸出以下消息:
Inside SpellChecker constructor.
Inside checkSpelling.
屬性中的 @Autowired
你可以在屬性中使用 @Autowired 注解來除去 setter 方法。當時使用 為自動連接配接屬性傳遞的時候,Spring 會将這些傳遞過來的值或者引用自動配置設定給那些屬性。是以利用在屬性中 @Autowired 的用法,你的 TextEditor.java 檔案将變成如下所示:
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
@Autowired
private SpellChecker spellChecker;
public TextEditor() {
System.out.println("Inside TextEditor constructor." );
}
public SpellChecker getSpellChecker( ){
return spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
複制
下面是配置檔案 Beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
</beans>
複制
一旦你在源檔案和 bean 配置檔案中完成了上面兩處改變,讓我們運作一下應用程式。如果你的應用程式一切都正常的話,這将會輸出以下消息:
Inside TextEditor constructor.
Inside SpellChecker constructor.
Inside checkSpelling.
構造函數中的 @Autowired
你也可以在構造函數中使用 @Autowired。一個構造函數 @Autowired 說明當建立 bean 時,即使在 XML 檔案中沒有使用 元素配置 bean ,構造函數也會被自動連接配接。讓我們檢查一下下面的示例。
這裡是 TextEditor.java 檔案的内容:
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
private SpellChecker spellChecker;
@Autowired
public TextEditor(SpellChecker spellChecker){
System.out.println("Inside TextEditor constructor." );
this.spellChecker = spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
複制
下面是配置檔案 Beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<!-- Definition for textEditor bean without constructor-arg -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
</beans>
複制
一旦你在源檔案和 bean 配置檔案中完成了上面兩處改變,讓我們運作一下應用程式。如果你的應用程式一切都正常的話,這将會輸出以下消息:
Inside TextEditor constructor.
Inside SpellChecker constructor.
Inside checkSpelling.
@Autowired 的(required=false)選項
預設情況下,@Autowired 注解意味着依賴是必須的,它類似于 @Required 注解,然而,你可以使用 @Autowired 的 (required=false) 選項關閉預設行為。
即使你不為 age 屬性傳遞任何參數,下面的示例也會成功運作,但是對于 name 屬性則需要一個參數。你可以自己嘗試一下這個示例,因為除了隻有 Student.java 檔案被修改以外,它和 @Required 注解示例是相似的。
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class Student {
private Integer age;
private String name;
@Autowired(required=false)
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
@Autowired
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
複制
其他
@Autowired注釋應用于具有任意名稱和多個參數的方法:
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
複制
您也可以将@Autowired應用于字段,或者将其與構造函數混合,如以下示例所示
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
private MovieCatalog movieCatalog;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
複制
将@Autowired注釋添加到需要該類型數組的字段或方法,則spring會從ApplicationContext中搜尋符合指定類型的所有bean,如以下示例所示:
public class MovieRecommender {
@Autowired
private MovieCatalog[] movieCatalogs;
// ...
}
複制
數組可以,我們可以馬上舉一反三,那容器也可以嗎,答案是肯定的,下面是set以及map的例子:
public class MovieRecommender {
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
複制
public class MovieRecommender {
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
複制
以上就是@Autowired注解的主要使用方式,經常使用spring的話應該對其中常用的幾種不會感到陌生。
@Autowired裝配不成功的幾種情況?
沒有加@Component注解
在類上面忘了加@Controller、@Service、@Component、@Repository等注解,spring就無法完成自動裝配的功能,例如:
public class UserService {
@Autowired
private IUser user;
public void test() {
user.say();
}
}
複制
這種情況應該是最常見的錯誤了,不會因為你長得帥,就不會犯這種低級的錯誤。
注入Filter或Listener
web應用啟動的順序是:listener->filter->servlet。
接下來看看這個案例:
public class UserFilter implements Filter {
@Autowired
private IUser user;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
user.say();
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
}
@Override
public void destroy() {
}
}
複制
public class FilterConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean() {
bean = new FilterRegistrationBean();
bean.setFilter(new UserFilter());
bean.addUrlPatterns("/*");
return bean;
}
}
複制
程式啟動會報錯,tomcat也無法正常啟動???什麼原因??
衆所周知,springmvc的啟動是在DisptachServlet裡面做的,而它是在listener和filter之後執行。如果我們想在listener和filter裡面@Autowired某個bean,肯定是不行的,因為filter初始化的時候,此時bean還沒有初始化,無法自動裝配。
如果工作當中真的需要這樣做,我們該如何解決這個問題呢?
public class UserFilter implements Filter {
private IUser user;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(
filterConfig.getServletContext());
this.user = ((IUser)(applicationContext.getBean("user1")));
user.say();
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
}
@Override
public void destroy() {
}
}
複制
答案是使用 WebApplicationContextUtils.getWebApplicationContext擷取目前的ApplicationContext,再通過它擷取到bean執行個體。
注解未被@ComponentScan掃描
通常情況下,@Controller、@Service、@Component、@Repository、@Configuration等注解,是需要通過@ComponentScan注解掃描,收集中繼資料的。
但是,如果沒有加@ComponentScan注解,或者@ComponentScan注解掃描的路徑不對,或者路徑範圍太小,會導緻有些注解無法收集,到後面無法使用@Autowired完成自動裝配的功能。
有個好消息是,在springboot項目中,如果使用了@SpringBootApplication注解,它裡面内置了@ComponentScan注解的功能。
循環依賴問題
如果A依賴于B,B依賴于C,C又依賴于A,這樣就形成了一個死循環。
spring的bean預設是單例的,如果單例bean使用@Autowired自動裝配,大多數情況,能解決循環依賴問題。
但是如果bean是多例的,會出現循環依賴問題,導緻bean自動裝配不了。
還有有些情況下,如果建立了代理對象,即使bean是單例的,依然會出現循環依賴問題。
@Autowired和@Resouce的差別
@Autowired功能雖說非常強大,但是也有些不足之處。比如:比如它跟spring強耦合了,如果換成了JFinal等其他架構,功能就會失效。而@Resource是JSR-250提供的,它是Java标準,絕大部分架構都支援。
除此之外,有些場景使用@Autowired無法滿足的要求,改成@Resource卻能解決問題。接下來,我們重點看看@Autowired和@Resource的差別。
- @Autowired預設按byType自動裝配,而@Resource預設byName自動裝配。
- @Autowired隻包含一個參數:required,表示是否開啟自動準入,預設是true。而@Resource包含七個參數,其中最重要的兩個參數是:name 和 type。
- @Autowired如果要使用byName,需要使用@Qualifier一起配合。而@Resource如果指定了name,則用byName自動裝配,如果指定了type,則用byType自動裝配。
- @Autowired能夠用在:構造器、方法、參數、成員變量和注解上,而@Resource能用在:類、成員變量和方法上。
- @Autowired是spring定義的注解,而@Resource是JSR-250定義的注解。
此外,它們的裝配順序不同。
@Autowired的裝配順序如下:
@Resource的裝配順序如下:
1.如果同時指定了name和type:
如果指定了name:
如果指定了type:
如果既沒有指定name,也沒有指定type: