なぜ私の Spring @Autowired フィールドは null になるのか?

java spring null nullpointerexception autowired


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

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

コントローラクラス。

@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で 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)
    ...



Answer 1 chrylis -on strike-


アノテーション付きのフィールド @Autowirednull です。これは、Springが new で作成した MileageFeeCalculator のコピーを認識しておらず、自動配線することを知らなかったためです。

Spring Inversion of Control(IoC)コンテナには、3つの主要な論理コンポーネントがあります。アプリケーションで使用できるコンポーネント(beans)のレジストリ( ApplicationContext と呼ばれます)、マッチングによってオブジェクトの依存関係をそれらに注入する構成システムコンテキスト内のBeanの依存関係、および多くの異なるBeanの構成を調べ、必要な順序でそれらをインスタンス化および構成する方法を決定できる依存関係ソルバー。

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

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

あなたの豆を注入する

最も好ましいオプションは、SpringにすべてのBeanを自動ワイヤリングさせることです。これは必要なコードの量が最も少なく、最も保守しやすいものです。自動配線を希望どおりに MileageFeeCalculator せるには、次のように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

設定可能な

自動でワイヤリングされる new で作成されたオブジェクトが本当に必要な場合は、Spring @Configurable アノテーションをAspectJコンパイル時ウィービングとともに使用して、オブジェクトを挿入できます。このアプローチでは、オブジェクトのコンストラクターにコードが挿入され、Springが新しいインスタンスを構成できるように、作成中のコードがSpringに通知されます。これには、ビルドでの ajc 設定( @EnableSpringConfigured でのコンパイルなど)と、Springのランタイム設定ハンドラー(JavaConfig構文での@EnableSpringConfigured)をオンにする必要があります。このアプローチは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

手動での豆の検索:推奨されません

このアプローチは、特殊な状況でレガシーコードとのインターフェイスにのみ適しています。Springが自動配線し、レガシーコードが呼び出すことができるシングルトンアダプタクラスを作成することが望ましいですが、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 ことによって機能するタグ:working-manual-lookup




Answer 2 Shirish Coolkarni


Webアプリケーションをコーディングしていない場合は、@Autowiringが行われているクラスがspring beanであることを確認してください。通常、春のコンテナは春の豆と思われるようなクラスを意識していません。私たちは、スプリングコンテナに私たちのスプリングクラスを伝えなければなりません。

これは、appln-contxtで設定することで実現できます。または、より良い方法は、クラスに@Componentとして注釈を付けることです。新しい演算子を使用して注釈付きクラスを作成しないでください。以下のようにAppln-contextから取得してください。

@Component
public class MyDemo {


    @Autowired
    private MyService  myService; 

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
            System.out.println("test");
            ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
            System.out.println("ctx>>"+ctx);

            Customer c1=null;
            MyDemo myDemo=ctx.getBean(MyDemo.class);
            System.out.println(myDemo);
            myDemo.callService(ctx);


    }

    public void callService(ApplicationContext ctx) {
        // TODO Auto-generated method stub
        System.out.println("---callService---");
        System.out.println(myService);
        myService.callMydao();

    }

}



Answer 3 Ravi Durairaj


実際には、メソッドを呼び出すには、JVM管理オブジェクトまたはスプリング管理オブジェクトを使用する必要があります。上記のコントローラクラスのコードから、自動配線されたオブジェクトを持つサービスクラスを呼び出すための新しいオブジェクトを作成しています。

MileageFeeCalculator calc = new MileageFeeCalculator();

ということで、そのようにはいかないでしょう。

このソリューションでは、このMileageFeeCalculatorをコントローラ自体の自動配線オブジェクトにしています。

以下のようにControllerクラスを変更します。

@Controller
public class MileageFeeController {

    @Autowired
    MileageFeeCalculator calc;  

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



Answer 4 smwikipedia


the life in the IoC world 慣れていないときに、同じ問題にかつて遭遇しました。いずれかのBean の @Autowired フィールドが実行時にnullになります。

根本的な原因は、Spring IoCコンテナ( @Autowired フィールドが indeed 適切に挿入されている)によって維持されている自動作成Beanを使用する代わりに、そのBeanタイプの独自のインスタンスを newing て使用していることです。もちろん、このフィールドの @Autowired フィールドはnullです。これは、Springがそれを挿入する機会がないためです。




Answer 5 Deepak


あなたの問題は新しいものです(javaスタイルでのオブジェクト作成)

MileageFeeCalculator calc = new MileageFeeCalculator();

アノテーションで @Service@Component@Configuration 豆がで作成されます
のアプリケーションコンテキストに登録されます。しかし、new演算子を使ってオブジェクトを作成しても、既に作成されているアプリケーションコンテキストには登録されません。例Employee.javaクラスの場合、私は使用しています。

これをチェックしてみてください。

public class ConfiguredTenantScopedBeanProcessor implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    String name = "tenant";
    System.out.println("Bean factory post processor is initialized"); 
    beanFactory.registerScope("employee", new Employee());

    Assert.state(beanFactory instanceof BeanDefinitionRegistry,
            "BeanFactory was not a BeanDefinitionRegistry, so CustomScope cannot be used.");
    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
        if (name.equals(definition.getScope())) {
            BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, true);
            registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition());
        }
    }
}

}