Spring @Autowired フィールドが null なのはなぜですか? 質問する

Spring @Autowired フィールドが null なのはなぜですか? 質問する

注: これは、一般的な問題に対する標準的な回答となることを目的としています。

フィールド( ) を持つSpring@Serviceクラス( ) がありますが、使用しようとするとフィールド が表示されます。ログには、 Bean とBean の両方が作成されていることが示されていますが、サービス Bean で メソッドを呼び出そうとすると、が表示されます。Spring がフィールドを自動配線しないのはなぜですか?MileageFeeCalculator@AutowiredrateServicenullMileageFeeCalculatorMileageRateServiceNullPointerExceptionmileageCharge

コントローラークラス:

@Controller
public class MileageFeeController {    
    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        MileageFeeCalculator calc = new MileageFeeCalculator();
        return calc.mileageCharge(miles);
    }
}

サービスクラス:

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- should be autowired, is null

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); // <--- throws NPE
    }
}

自動接続される必要があるMileageFeeCalculatorが、そうではないサービス Bean:

@Service
public class MileageRateService {
    public float ratePerMile() {
        return 0.565f;
    }
}

しようとするとGET /mileage/3、次の例外が発生します:

java.lang.NullPointerException: null
    at com.chrylis.example.spring_autowired_npe.MileageFeeCalculator.mileageCharge(MileageFeeCalculator.java:13)
    at com.chrylis.example.spring_autowired_npe.MileageFeeController.mileageFee(MileageFeeController.java:14)
    ...

ベストアンサー1

フィールドに注釈が付けられているのは、Spring が、作成したのコピーを認識しておらず、それを自動接続することも知らなかったため@Autowiredです。nullMileageFeeCalculatornew

Spring の制御反転 (IoC) コンテナには、アプリケーションで使用できるコンポーネント (Bean) のレジストリ ( と呼ばれるApplicationContext)、コンテキスト内の Bean と依存関係を一致させることによってオブジェクトの依存関係を注入するコンフィギュラ システム、およびさまざまな Bean の構成を調べて、それらを必要な順序でインスタンス化および構成する方法を決定できる依存関係ソルバという 3 つの主要な論理コンポーネントがあります。

IoC コンテナは魔法ではありません。何らかの方法で Java オブジェクトを通知しない限り、Java オブジェクトについて知る方法はありません。 を呼び出すとnew、JVM は新しいオブジェクトのコピーをインスタンス化し、それを直接渡します。構成プロセスを経ることはありません。Bean を構成する方法は 3 つあります。

このコードはすべてSpring Bootを使用して起動し、このGitHubプロジェクト; 各アプローチの完全な実行中のプロジェクトを見て、それを機能させるために必要なすべてのものを確認できます。次のタグを付けますNullPointerException:nonworking

豆を注入する

最も望ましいオプションは、Spring ですべての Bean を自動配線することです。これにより、必要なコードの量が最も少なくなり、メンテナンス性も最も高くなります。自動配線を希望どおりに動作させるには、MileageFeeCalculator次のように自動配線します。

@Controller
public class MileageFeeController {

    @Autowired
    private MileageFeeCalculator calc;

    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        return calc.mileageCharge(miles);
    }
}

異なるリクエストに対してサービスオブジェクトの新しいインスタンスを作成する必要がある場合は、次のようにしてインジェクションを使用できます。Spring Beanスコープ

サービス オブジェクトを挿入することで機能するタグ@MileageFeeCalculator:working-inject-bean

@Configurableを使用する

で作成されたオブジェクトnewを自動配線する必要がある場合は、Spring@ConfigurableアノテーションとAspectJコンパイル時ウィービングを併用するを使用してオブジェクトを注入します。このアプローチでは、オブジェクトのコンストラクタにコードが挿入され、Spring が新しいインスタンスを構成できるように、オブジェクトが作成されていることを Spring に通知します。これには、ビルドでの構成 ( を使用したコンパイルなどajc) と、Spring のランタイム構成ハンドラ ( @EnableSpringConfiguredJavaConfig 構文を使用) の有効化が少し必要になります。このアプローチは、Roo Active Record システムによって使用され、newエンティティのインスタンスに必要な永続化情報が注入されるようにします。

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile());
    }
}

@Configurableサービス オブジェクトで使用することで機能するタグ:working-configurable

手動Bean検索: 非推奨

このアプローチは、特別な状況でレガシー コードとインターフェイスする場合にのみ適しています。ほとんどの場合、Spring が自動接続し、レガシー コードが呼び出すことができるシングルトン アダプター クラスを作成することが望ましいですが、Bean を Spring アプリケーション コンテキストに直接要求することも可能です。

これを行うには、Spring がオブジェクトへの参照を提供できるクラスが必要ですApplicationContext

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;   
    }

    public static ApplicationContext getContext() {
        return context;
    }
}

その後、レガシー コードはgetContext()必要な Bean を呼び出して取得できます。

@Controller
public class MileageFeeController {    
    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        MileageFeeCalculator calc = ApplicationContextHolder.getContext().getBean(MileageFeeCalculator.class);
        return calc.mileageCharge(miles);
    }
}

Spring コンテキストでサービス オブジェクトを手動で検索することによって機能するタグ:working-manual-lookup

おすすめ記事