Spring使用@Resource、@Autowired注入時出現空指標問題的原因
Spring java配置及註解注入方法出現空指標異常的原因
在使用Spring進行自動注入的過程中,只會對通過讀取Spring的配置檔案或者配置類後產生的例項進行自動注入。
手動new出來的例項是無法獲得在Spring中註冊過得例項,這是 因為手動new 的例項並不是Spring 在初始化過程中註冊的例項。
通過下面的例子來解釋一下問題所在:
定義一個介面類MessageService
package hello;
public interface MessageService {
String getMessage();
}
定義一個實現類MessagePrinter,使用@Autowired通過Type的方式在這個Bean中自動裝配service
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
public class MessagePrinter {
final private MessageService service;
@Autowired
public MessagePrinter(MessageService service) {
this.service = service;
}
public void printMessage() {
System.out.println(this.service.getMessage());
}
}
最後是測試類,使用@Configuration告訴Spring這個類是一個配置類,並且通過@ComponentScan自動掃描同一個包內的其他檔案,將符合條件的類自動註冊到Spring的容器中。然後使用@Bean實現了一個MessageService的介面。
package hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan
public class Application {
@Bean
MessageService mockMessageService() {
return new MessageService() {
public String getMessage() {
return "Hello World!";
}
};
}
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext(Application.class);
System.out.println(context.containsBean("messageService"));
MessagePrinter printer = new MessagePrinter();
printer.printMessage();
}
}
通過這個測試類的main方法,使用AnnotationConfigApplicationContext()方法載入了Application.class用來初始化Spring完成對所有元件的註冊。
此時我們containsBean()的方法驗證了一下是否所有例項都已經註冊在Spring中了。然後新建一個MessagePrinter並列印結果,執行結果如下:
true
java.lang.NullPointerException
at hello.MessagePrinter.show(MessagePrinter.java:16)
at hello.Application.main(Application.java:24)
發生了空指標異常。
根據異常的反饋發現錯誤在這一句:
System.out.println(this.service.getMessage());
也就是沒有找到MessageService service 這個例項。
這就很奇怪了,從列印的true中可以看出MessageService 已經成功註冊到Spring容器中,可是卻出現了空指標異常這是為什麼呢?
經過仔細看Spring官方的例子發現了問題所在:
當通過new的方式建立一個MessagePrinter物件的時候,雖然期望使用了註解@Autowired對這個物件進行裝配,但是Spring是不會這麼做的,因為Spring不會對任意一個MessagePrinter進行自動裝配,只有MessagePrinter也是一個在Spring中註冊過的Bean,才會獲得自動裝配的功能。
理解了這個問題,修改一下前面的程式碼:
在MessagePrinter前面增加一個@Component來註冊一個Bean
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MessagePrinter {
final private MessageService service;
@Autowired
public MessagePrinter(MessageService service) {
this.service = service;
}
public void printMessage() {
System.out.println(this.service.getMessage());
}
}
執行後發現結果依然異常:
true
java.lang.NullPointerException
at hello.MessagePrinter.show(MessagePrinter.java:17)
at hello.Application.main(Application.java:24)
這是因為,Spring預設都是單例的,new出來的物件,Spring依然不會對它進行裝配,只有通過Spring建立的物件才會獲得自動裝配的功能,所以再改一下最後的測試程式碼:
把new的printer改為從ApplicationContext物件中建立對例項:
package hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan
public class Application {
@Bean
MessageService mockMessageService() {
return new MessageService() {
public String getMessage() {
return "Hello World!";
}
};
}
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext(Application.class);
System.out.println(context.containsBean("messageService"));
MessagePrinter printer = context.getBean(MessagePrinter.class);
printer.printMessage();
}
}
最後來看看結果,終於看到了久違的“Hello World!”
true
Hello World!