1. 程式人生 > >Spring使用@Resource、@Autowired注入時出現空指標問題的原因

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!