在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。