技術メモ

技術メモ

ラフなメモ

org.springframework.beans.factory.NoSuchBeanDefinitionException が出たときの対処

TL;DR

どういうときに起こるかというと、端的に言えばコンポーネントを注入しているけれども、コンポーネントがDIコンテナに存在しない/参照できていない場合に発生します。具体的な例を見てみます。

具体例1:DIコンテナに注入できていない場合

パッケージ構成は以下のようになっているとします。

└─src
   └─main
      ├─java
      │  └─sample
      │          Hoge.java
      │          Main.java
      │
      └─resources
          └─spring
                  applicationContext.xml

spring/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<!--    <bean id="hoge" class="sample.Hoge">
       <constructor-arg value="defaultName" />
   </bean> -->

</beans>

sample.Main.java

package sample;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Main {

    public static void main(String[] args) {

        String[] xmlConfig = new String[] { "classpath:/spring/applicationContext.xml" };
        ApplicationContext context = new FileSystemXmlApplicationContext(xmlConfig);
        Hoge hoge = (Hoge) context.getBean("hoge");
        hoge.say();

    }
}

Hoge.java

package sample;

public class Hoge {

    String name;

    public Hoge(String name) {
        this.name = name;
    }

    void say() {
        System.out.println(this.name);
    }

}

あえて applicationContext.xml のbean定義をコメントアウトして実行すると以下のようになります。要は hoge の名前のbeanがないよ、ということです。

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'hoge' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:775)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1221)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:294)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105)
    at sample.Main.main(Main.java:12)

ということで applicationContext.xmlコメントアウトを外すと以下のように <constructor-arg value="defaultName" /> で注入している値が取得できます。

defaultName

具体例2:@AutowiredしているcomponentがDIコンテナに注入されていない場合

別の例を見てみます。以下のような構成になっているとします。 DummyDao.java を追加しています。疑似的にDBアクセスするようなパッケージを構成していたとします。(今回の場合はStringの文字列を返却するだけです)

└─src
   └─main
      ├─java
      │  └─sample
      │      │  Hoge.java
      │      │  Main.java
      │      │
      │      └─dao
      │              DummyDao.java
      │
      └─resources
          └─spring
                  applicationContext.xml

Hoge.java

package sample;

import org.springframework.beans.factory.annotation.Autowired;

import sample.dao.DummyDao;

public class Hoge {

    String name;

    @Autowired
    DummyDao dummyDao;

    public Hoge(String name) {
        this.name = name;
    }

    void say() {
        System.out.println(dummyDao.find());
    }

}

DummyDao.java

package sample.dao;

import org.springframework.stereotype.Repository;

@Repository
public class DummyDao {

    public String find() {
        return "something";
    }

}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="sample.dao.*" />

    <bean id="hoge" class="sample.Hoge">
        <constructor-arg value="defaultName" />
    </bean>

</beans>

上記は <context:component-scan base-package="sample.dao.*" /> としているので実質 DummyDao.java をscanできていません。

よって実行すると以下のような例外 org.springframework.beans.factory.UnsatisfiedDependencyException が発生します。Hoge.java@Autowired しているクラスがDIコンテナから取得できないためです。

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'hoge': Unsatisfied dependency expressed through field 'dummyDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'sample.dao.DummyDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} [土 4 20 19:38:08 JST 2019]
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'hoge': Unsatisfied dependency expressed through field 'dummyDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'sample.dao.DummyDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1395)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
    at org.springframework.context.support.FileSystemXmlApplicationContext.<init>(FileSystemXmlApplicationContext.java:142)
    at org.springframework.context.support.FileSystemXmlApplicationContext.<init>(FileSystemXmlApplicationContext.java:95)
    at sample.Main.main(Main.java:13)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'sample.dao.DummyDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1654)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1213)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
    ... 15 more

これは applicationContext.xml<context:component-scan base-package="sample.dao.*" /><context:component-scan base-package="sample.dao" /> とすれば @Repository を付与している DummyDao.java をDIコンテナから取得できるようになるので実行できます。

something

参考