Spring注解之@Autowired:裝配構造函數和屬性


       在User類中創建一個構造函數,傳入參數student:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.Serializable; /** * @author Wiener */ @Component public class User implements Serializable { private static final long serialVersionUID = 6089103683553156328L; private Long id; private Student student; public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Autowired // 構造函數注入參數的方式
 User(Student student) { this.student = student; System.out.println("------ 為構造器裝配Bean成功 ---------"); } public void isStu() { student.studentStudy(); System.out.println("------ 裝配Bean成功 ---------"); } }

       其中,Student類如下:

import lombok.Getter;
import lombok.Setter;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Date;

/**
 * @author Wiener
 */
@Getter
@Setter
@Component
public class Student implements Serializable {

    private static final long serialVersionUID = -5246589941647210011L;

    //姓名
    private String name;
  
    public Student() {
        System.out.println("A default student constructor." );
    }
    public void studentStudy() {
        System.out.println("A student is studying." );
    }
}

改造Spring Boot項目啟動類:

import com.east7.bean.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

/**
 * @author Wiener
 */
@SpringBootApplication
public class East7Application {

    public static void main(String[] args) {
       ApplicationContext act = SpringApplication.run(East7Application.class, args);
        User user = (User) act.getBean("user");
        user.isStu();
    }

}

執行測試函數后,控制台打印信息如下:

A default student constructor. ------ 為構造器裝配Bean成功 --------- A student is studying. ------ 裝配Bean成功 ---------

       說明成員變量已經注入。此處需要注意一點,如果有兩個自定義構造方法,而且都沒加@Autowired注解,則會報錯,因為Spring不知道你要用哪個構造方法初始化;如果只有一個構造方法加了@Autowired注解,那么就會用這個構造方法初始化;同理,如果有多個構造方法都加了@Autowired注解,那么還是會報錯提示Only one constructor can have @Autowired annotation。

       可以在屬性中使用@Autowired 注解來省略 setter 方法。當使用@Autowired為自動連接屬性傳遞的時候,Spring 會將這些傳遞過來的值或者引用自動分配給那些屬性。像下面這樣寫在屬性上並直接引用會報空指針異常:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author Wiener */ @Component public class User { @Autowired //變量注入方式
    private Student student; private Long id = student.getId(); public Long getId() { return id; } public void setId(Long id) { this.id = id; } public void isStu() { id = student.getId(); student.studentStudy(); System.out.println("------ 裝配Bean成功 ---------"); } }

異常信息如下:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'user' defined in file [xxxxxx\User.class]: 
Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [com.east7.bean.User]:
Constructor threw exception; nested exception is java.lang.NullPointerException

報錯信息提示創建Bean時出錯,因為實例化bean時構造方法拋出了空指針異常。如果僅僅使用函數isStu()初始化變量id, 並且把【private Long id = student.getId();】改為 【private Long id;】,則屬於方法調用的時候初始化,此時,bean已經注入,不會拋異常。 

       其實這兩種方式都可以使用,但報錯的原因是加載順序的問題,@autowired寫在變量上的注入要等到類完全加載后,才會將相應的bean注入,而變量是在加載類的時候按照相應順序加載的,所以變量的加載要早於被@autowired注解的變量,那么給變量prefix 賦值的時候所使用的a,其實還沒有被注入,所以報空指針,而使用構造器則會在加載類的時候將a加載,這樣在內部使用a給prefix 賦值就完全沒有問題。 

       如果不使用構造器,那么也可以不給prefix 提前賦值,而是在系統啟動后,變量prefix被使用的地方,通過a.getExcelPrefix()進行賦值,此時對a的使用是在類完全加載之后,即a被注入后,所以也是可以的。 

       總之,@Autowired一定要等本類構造完成后,才能從外部引用設置屬性,所以@Autowired的注入時間一定會晚於構造函數的執行時間。但在初始化變量的時候就使用了還沒注入的bean,所以導致了NPE。若在初始化其它變量時不使用這個要注入的bean,而是在后期調用方法的時候去初始化,是可以使用這個bean的,因為那時構造函數已經執行完畢,即已注入bean了。一言以蔽之,Java變量的初始化順序為:靜態變量或靜態語句塊–>實例變量或初始化語句塊–>構造函數–>@Autowired 注入Bean

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM