注:これは一般的な問題に対する標準的な回答を目的としています。
@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)
...
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
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
この方法は、特殊な状況でレガシーコードとやり取りする場合にのみ適しています。 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
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();
}
}
実際には、メソッドを呼び出すには、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);
}
}
私はthe life in the IoC world
に慣れていなかったときに同じ問題に遭遇しました。私のBeanの1つの@Autowired
フィールドは実行時にnullです。
根本的な原因は、SpringのIoCコンテナによって管理されている自動作成されたBean(その@Autowired
フィールドが正しく注入されたindeed
)を使用する代わりに、そのBeanタイプの私自身のインスタンスであるnewing
です。もちろん、Springがそれをインジェクトする機会がないので、この人の@Autowired
フィールドはnullです。
あなたの問題は新しいものです(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());
}
}
}
}
それはまれなケースのようですが、ここに私に起こったことがあります:
SpringでサポートされているJava標準である@Inject
の代わりに@Autowired
を使用しました。一箇所ではなく、あらゆる場所でうまく機能し、豆が正しく注入されました。豆の注入は同じようです
@Inject
Calculator myCalculator
最後に、エラーは私たち(実際にはEclipseのオートコンプリート機能)がcom.opensymphony.xwork2.Inject
ではなくjavax.inject.Inject
をインポートしたことであることがわかりました。
要約すると、注釈(@Autowired
、@Inject
、@Service
、...)に正しいパッケージがあることを確認してください。
私は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
でインスタンス化されています)および静的な方法で _です。
別の解決策は、電話をかけることでしょう: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());
}
}
注釈付きのクラスをスキャンするようにspringに指示していないのではないでしょうか。
Springにスキャンを指示するには、Springアプリケーションの設定クラスで@ComponentScan("packageToScan")
を使用します。
@Service, @Component
etcアノテーションはメタ記述を追加します。
Springは、Beanとして作成されたかアノテーションが付けられたクラスのインスタンスのみを注入します。
アノテーションでマークされたクラスは、注入する前にspringで識別される必要があります。@ComponentScan
は、アノテーションでマークされたクラスをspringで探すように指示します。 Springが@Autowired
を見つけると、関連するBeanを検索し、必要なインスタンスを注入します。
注釈を追加するだけで、依存性注入を修正または促進するわけではないため、Springはどこを探すべきかを知っておく必要があります。
これがテストクラスで発生している場合は、クラスに注釈を付けるのを忘れていないことを確認してください。
たとえば、 Spring Boot の場合:
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
....
サービスクラスで@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();
}
}
更新: 本当に頭の良い人たちはすぐに指摘しました これ 答え。
元の答え:
それが誰かに役立つかどうかはわかりませんが、物事を正しく行っている間でも同じ問題にはまっていました。私の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"/>
そしてそれはうまくいきます。誰かが説明を提供できるかもしれませんが、私にとっては今では十分です)
また、何らかの理由で@Service
内のメソッドをfinal
として作成した場合、そのメソッドからアクセスする自動配線Beanは常にnull
になります。
ここで言及されていないことは、「実行順序」の段落の 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
}
}
これがNullPointerExceptionを与える原因ですMileageFeeCalculator calc = new MileageFeeCalculator();
私たちはSpringを使っています - 手動でオブジェクトを作成する必要はありません。オブジェクトの作成はIoCコンテナによって行われます。