天天看點

Spring注解-@Autowired注解使用

寫在前面得話

學習@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四種模式的差別

Spring注解-@Autowired注解使用

先看一下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。

Spring注解-@Autowired注解使用

接下來看看這個案例:

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注解-@Autowired注解使用

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的裝配順序如下:

Spring注解-@Autowired注解使用

@Resource的裝配順序如下:

1.如果同時指定了name和type:

Spring注解-@Autowired注解使用

如果指定了name:

Spring注解-@Autowired注解使用

如果指定了type:

Spring注解-@Autowired注解使用

如果既沒有指定name,也沒有指定type:

Spring注解-@Autowired注解使用