web-dev-qa-db-ja.com

Spring @Autowiredフィールドがnullになっているのはなぜですか?

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

@Serviceフィールド(MileageFeeCalculator)を持つSpring @Autowiredクラス(rateService)がありますが、使用しようとするとこのフィールドはnullです。ログにはMileageFeeCalculator BeanとMileageRateService Beanの両方が作成されていることが示されていますが、サービスBeanでNullPointerExceptionメソッドを呼び出そうとすると必ずmileageChargeが返されます。 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で自動配線されるべきサービス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)
    ...
512
chrylis

Springがnullで作成したMileageFeeCalculatorのコピーを認識せず、それを自動配線することも知らないため、@Autowiredというアノテーションが付けられたフィールドはnewです。

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

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で作成したオブジェクトを自動生成する必要がある場合は、オブジェクトをインジェクトするために AspectJのコンパイル時のウィービングとともにSpring @Configurableアノテーションを使用 _を実行できます。このアプローチでは、Springが新しいインスタンスを設定できるように、作成中であることをSpringに警告するコードをオブジェクトのコンストラクタに挿入します。これには、ビルドに多少の設定(ajcを使用したコンパイルなど)および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

手動Bean検索:推奨されていません

この方法は、特殊な状況でレガシーコードとやり取りする場合にのみ適しています。 Springが自動配線し、レガシーコードが呼び出すことができるシングルトンアダプタクラスを作成することがほぼ常に望ましいですが、SpringのアプリケーションコンテキストにBeanを直接要求することは可能です。

これを行うには、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()を呼び出して、それが必要とするビーンを検索することができます:

@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

559
chrylis

Webアプリケーションをコーディングしていない場合は、@ Autowiringが行われているクラスがSpring Beanであることを確認してください。通常、Springコンテナは、Spring Beanと考えることができるクラスを認識しません。 SpringクラスについてはSpringコンテナに伝えなければなりません。

これは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();

    }

}
50

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

MileageFeeCalculator calc = new MileageFeeCalculator();

そのようには動作しません。

このソリューションは、このMileageFeeCalculatorをController自体の自動配線オブジェクトとして作成します。

以下のようにControllerクラスを変更してください。

@Controller
public class MileageFeeController {

    @Autowired
    MileageFeeCalculator calc;  

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

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

根本的な原因は、SpringのIoCコンテナによって管理されている自動作成されたBean(その@Autowiredフィールドが正しく注入されたindeed)を使用する代わりに、そのBeanタイプの私自身のインスタンスであるnewingです。もちろん、Springがそれをインジェクトする機会がないので、この人の@Autowiredフィールドはnullです。

23
smwikipedia

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

MileageFeeCalculator calc = new MileageFeeCalculator();

アノテーション@Service@Component@Configurationが作成されます。
サーバー起動時のSpringのアプリケーションコンテキスト。しかし、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());
        }
    }
}

}
19
Deepak

それはまれなケースのようですが、ここに私に起こったことがあります:

SpringでサポートされているJava標準である@Injectの代わりに@Autowiredを使用しました。一箇所ではなく、あらゆる場所でうまく機能し、豆が正しく注入されました。豆の注入は同じようです

@Inject
Calculator myCalculator

最後に、エラーは私たち(実際にはEclipseのオートコンプリート機能)がcom.opensymphony.xwork2.Injectではなくjavax.inject.Injectをインポートしたことであることがわかりました。

要約すると、注釈(@Autowired@Inject@Service、...)に正しいパッケージがあることを確認してください。

9
Alireza Fattahi

私はSpringが初めてですが、この解決策を発見しました。それが廃止予定の方法であれば教えてください。

私はこのBeanにSpringにapplicationContextを注入させます。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class SpringUtils {

    public static ApplicationContext ctx;

    /**
     * Make Spring inject the application context
     * and save it on a static variable,
     * so that it can be accessed from any point in the application. 
     */
    @Autowired
    private void setApplicationContext(ApplicationContext applicationContext) {
        ctx = applicationContext;       
    }
}

必要に応じて、このコードをメインアプリケーションクラスにも入れることができます。

他のクラスはこのようにそれを使うことができます:

MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);

このように、アプリケーション内の任意のオブジェクトから任意のBeanを取得できます(これもnewでインスタンス化されています)および静的な方法で _です。

7
bluish

別の解決策は、電話をかけることでしょう:SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
MileageFeeCalculatorコンストラクターには、次のようになります。

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- will be autowired when constructor is called

    public MileageFeeCalculator() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
    }

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

注釈付きのクラスをスキャンするようにspringに指示していないのではないでしょうか。

Springにスキャンを指示するには、Springアプリケーションの設定クラスで@ComponentScan("packageToScan")を使用します。

@Service, @Component etcアノテーションはメタ記述を追加します。

Springは、Beanとして作成されたかアノテーションが付けられたクラスのインスタンスのみを注入します。

アノテーションでマークされたクラスは、注入する前にspringで識別される必要があります。@ComponentScanは、アノテーションでマークされたクラスをspringで探すように指示します。 Springが@Autowiredを見つけると、関連するBeanを検索し、必要なインスタンスを注入します。

注釈を追加するだけで、依存性注入を修正または促進するわけではないため、Springはどこを探すべきかを知っておく必要があります。

4
msucil

これがテストクラスで発生している場合は、クラスに注釈を付けるのを忘れていないことを確認してください。

たとえば、 Spring Boot の場合:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
    ....
3
nobar

サービスクラスで@Serviceアノテーションを使用し、必要なBean classAを他のBeanのclassBコンストラクタへのパラメータとして渡し、classBのコンストラクタに@Autowiredでアノテーションを付けることでこの問題を解決することもできます。サンプルスニペットはこちら:

@Service
public class ClassB {

    private ClassA classA;

    @Autowired
    public ClassB(ClassA classA) {
        this.classA = classA;
    }

    public void useClassAObjectHere(){
        classA.callMethodOnObjectA();
    }
}
2
apandey846

更新: 本当に頭の良い人たちはすぐに指摘しました これ 答え。

元の答え:

それが誰かに役立つかどうかはわかりませんが、物事を正しく行っている間でも同じ問題にはまっていました。私のMainメソッドでは、私はこのようなコードがあります:

ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {
        "common.xml",
        "token.xml",
        "pep-config.xml" });
    TokenInitializer ti = context.getBean(TokenInitializer.class);

そしてtoken.xmlファイルで私は行を持っていました

<context:component-scan base-package="package.path"/>

私はpackage.pathがもう存在しないことに気づいたので、私は単に行を落としました。

その後、NPEが登場し始めました。pep-config.xmlには、たった2つのBeanしかありませんでした。

<bean id="someAbac" class="com.pep.SomeAbac" init-method="init"/>
<bean id="settings" class="com.pep.Settings"/>

someAbacクラスには、次のように宣言されたプロパティがあります。

@Autowired private Settings settings;

<context:component-scan/>要素がまったく存在しない場合、設定はinit()のnullですが、basePackageとしてbsがある場合は、すべてうまくいきます。この行は今このようになります:

<context:component-scan base-package="some.shit"/>

そしてそれはうまくいきます。誰かが説明を提供できるかもしれませんが、私にとっては今では十分です)

2
62mkv

また、何らかの理由で@Service内のメソッドをfinalとして作成した場合、そのメソッドからアクセスする自動配線Beanは常にnullになります。

0
yglodt

ここで言及されていないことは、「実行順序」の段落の this の記事で説明されています。

クラスに@Componentまたは派生物@Serviceまたは@Repository(もっとあると思います)で注釈を付ける必要があることを「学習」した後、それらの他のコンポーネントを自動配線するために、これらの他のコンポーネントがコンストラクタ内でまだnullであることに気付きました親コンポーネントの。

@PostConstructを使用すると、次のことが解決されます。

@SpringBootApplication
public class Application {
    @Autowired MyComponent comp;
}

そして:

@Component
public class MyComponent {
    @Autowired ComponentDAO dao;

    public MyComponent() {
        // dao is null here
    }

    @PostConstruct
    public void init() {
        // dao is initialized here
    }
}
0
JackLeEmmerdeur

これがNullPointerExceptionを与える原因ですMileageFeeCalculator calc = new MileageFeeCalculator();私たちはSpringを使っています - 手動でオブジェクトを作成する必要はありません。オブジェクトの作成はIoCコンテナによって行われます。

0
Atul Jain