Spring中常見的bean創建異常

Spring中常見的bean創建異常

1. 概述
    本次我們將討論在spring中BeanFactory創建bean實例時經常遇到的異常
org.springframework.beans.factory.BeanCreationException,下面我們將討論並再現這些異常,同時給出解決方案。

2. Cause:org.springframework.beans.factory.NoSuchBeanDefinitionException
    到目前爲止最常見的導致BeanCreationException 莫過於注入一個上下文中不存在的bean。
例如:BeanA嘗試注入BeanB

@Component
public class BeanA {

    @Autowired
    private BeanB dependency;
    ...
}

如果spring上下文中不存在BeanB,那麼下面的異常將會拋出

Error creating bean with name 'beanA': Injection of autowired dependencies failed; 
nested exception is org.springframework.beans.factory.BeanCreationException: 
Could not autowire field: private org.baeldung.web.BeanB org.baeldung.web.BeanA.dependency; 
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type [org.baeldung.web.BeanB] found for dependency: 
expected at least 1 bean which qualifies as autowire candidate for this dependency. 
Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

解決這種類型的問題——首先, 確保bean已經聲明瞭
  在XML配置文件使用< bean / >元素
  或通過@bean註釋Java @ configuration類
  或註釋:@Component,@Repository,@Service,@Controller和類路徑是否掃描包中。
同時檢查配置文件或類文件是否真的已經被spring裝載到上下文中。

3. Cause:org.springframework.beans.factory.NoUniqueBeanDefinitionException
另一個比較類似的異常情況,當你嘗試讓spring使用接口名來時創建一個bean時,但是發現它有兩個或多個實現
例如:BeanB1 和BeanB2都實現了同一個接口

@Component
public class BeanB1 implements IBeanB { ... }
@Component
public class BeanB2 implements IBeanB { ... }

@Component
public class BeanA {

    @Autowired
    private IBeanB dependency;
    ...
}

這將導致BeanFactory拋出一下異常:

Error creating bean with name 'beanA': Injection of autowired dependencies failed; 
nested exception is org.springframework.beans.factory.BeanCreationException: 
Could not autowire field: private org.baeldung.web.IBeanB org.baeldung.web.BeanA.b; 
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
No qualifying bean of type [org.baeldung.web.IBeanB] is defined: 
expected single matching bean but found 2: beanB1,beanB2

4. Cause:org.springframework.beans.BeanInstantiationException
4.1. Custom Exception

下面例子是拋出在其創建過程異常;簡化的樣例很容易體現和理解問題出現構造函數中,並拋出一個異常:

@Component
public class BeanA {

    public BeanA() {
        super();
        throw new NullPointerException();
    }
    ...
}

不出所料,spring很快的拋出以下異常:

Error creating bean with name 'beanA' defined in file [...BeanA.class]: 
Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: 
Could not instantiate bean class [org.baeldung.web.BeanA]: 
Constructor threw exception; 
nested exception is java.lang.NullPointerException

4.2. java.lang.InstantiationException

另一種可能發生BeanInstantiationException是在xml配置中使用抽象類定義bean。
這種只可能發生在xml配置文件中,
因爲沒有辦法在Java@Configuration文件和classpath掃描會忽略抽象類來做到這一點:

@Component
public abstract class BeanA implements IBeanA { ... }
<bean id="beanA" class="org.baeldung.web.BeanA" />
org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'beanA' defined in class path resource [beansInXml.xml]: 
Instantiation of bean failed; 
nested exception is org.springframework.beans.BeanInstantiationException: 
Could not instantiate bean class [org.baeldung.web.BeanA]: 
Is it an abstract class?; 
nested exception is java.lang.InstantiationException

4.3. java.lang.NoSuchMethodException

如果一個bean類沒有默認的構造方法,spring在創建bean實例時將會拋出如下運行時異常:

@Component
public class BeanA implements IBeanA {

    public BeanA(final String name) {
        super();
        System.out.println(name);
    }
}

如果該類的類路徑在掃描路徑下,將會拋出如下失敗:

Error creating bean with name 'beanA' defined in file [...BeanA.class]: Instantiation of bean failed; 
nested exception is org.springframework.beans.BeanInstantiationException: 
Could not instantiate bean class [org.baeldung.web.BeanA]: 
No default constructor found; 
nested exception is java.lang.NoSuchMethodException: org.baeldung.web.BeanA.<init>()

類似的異常,但難以診斷,當類路徑的Spring的依賴並不具有相同的版本,可能會發生異常;這種版本不兼容,可能會導致因API變化的NoSuchMethodException。這種問題的解決方案是確保所有Spring庫在該項目中的精確相同的版本。
5. Cause:org.springframework.beans.NotWritablePropertyException
然而,另一種可能性是你定義了一個bean:BeanA同時引用了另一個bean:BeanB但是BeanA中卻沒有相應的setter方法

@Component
public class BeanA {
    private IBeanB dependency;
    ...
}
@Component
public class BeanB implements IBeanB { ... }

配置文件

<bean id="beanA" class="org.baeldung.web.BeanA">
    <property name="beanB" ref="beanB" />
</bean>

此外需要說明的是,這種情況只可能發生在xml配置文件,因爲當你使用@Configuration時,spring容器會避免這種情況的發生。
當然,爲了解決這個問題,需要爲IBeanB添加setter方法

@Component
public class BeanA {
    private IBeanB dependency;

    public void setDependency(final IBeanB dependency) {
        this.dependency = dependency;
    }
}

6. Cause:org.springframework.beans.factory.CannotLoadBeanClassException
當spring加載不到bean對應的類文件時,這種異常將會被拋出。這種情況很有可能發生在當配置文件中的類路徑全稱找不到對應文件時。

<bean id="beanZ" class="org.baeldung.web.BeanZ" />

拋出ClassNotFoundException的根本原因

nested exception is org.springframework.beans.factory.BeanCreationException: 
...
nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: 
Cannot find class [org.baeldung.web.BeanZ] for bean with name 'beanZ'
defined in class path resource [beansInXml.xml]; 
nested exception is java.lang.ClassNotFoundException: org.baeldung.web.BeanZ

7. Children of BeanCreationException
7.1. The org.springframework.beans.factory.BeanCurrentlyInCreationException
BeanCurrentlyInCreationException是BeanCreationException的一個子類,經常在發生在錯誤的使用構造方法注入bean。
例如:循環依賴的情況

@Component
public class BeanA implements IBeanA {
    private IBeanB beanB;

    @Autowired
    public BeanA(final IBeanB beanB) {
        super();
        this.beanB = beanB;
    }
}
@Component
public class BeanB implements IBeanB {
    final IBeanA beanA;

    @Autowired
    public BeanB(final IBeanA beanA) {
        super();
        this.beanA = beanA;
    }
}

Spring將不能夠解決這種場景,最終導致

org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'beanA': 
Requested bean is currently in creation: Is there an unresolvable circular reference?

完整的異常信息非常豐富

org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'beanA' defined in file [...BeanA.class]: 
Unsatisfied dependency expressed through constructor argument with index 0 
of type [org.baeldung.web.IBeanB]: : 
Error creating bean with name 'beanB' defined in file [...BeanB.class]: 
Unsatisfied dependency expressed through constructor argument with index 0 
of type [org.baeldung.web.IBeanA]: : 
Error creating bean with name 'beanA': Requested bean is currently in creation: 
Is there an unresolvable circular reference?; 
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'beanA': 
Requested bean is currently in creation: 
Is there an unresolvable circular reference?; 
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'beanB' defined in file [...BeanB.class]: 
Unsatisfied dependency expressed through constructor argument with index 0 
of type [org.baeldung.web.IBeanA]: : 
Error creating bean with name 'beanA': 
Requested bean is currently in creation: 
Is there an unresolvable circular reference?; 
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'beanA': 
Requested bean is currently in creation: Is there an unresolvable circular reference?

7.2. The org.springframework.beans.factory.BeanIsAbstractException

public abstract class BeanA implements IBeanA {
   ...
}

在xml配置中聲明如下:

<bean id="beanA" abstract="true" class="org.baeldung.web.BeanA" />

現在,如果試圖從spring上線文中獲取BeanA實例,例如:

@Configuration
public class Config {
    @Autowired
    BeanFactory beanFactory;

    @Bean
    public BeanB beanB() {
        beanFactory.getBean("beanA");
        return new BeanB();
    }
}

將會拋出以下異常:

org.springframework.beans.factory.BeanIsAbstractException: 
Error creating bean with name 'beanA': Bean definition is abstract

全部異常棧信息:

org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'beanB' defined in class path resource 
[org/baeldung/spring/config/WebConfig.class]: Instantiation of bean failed; 
nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: 
Factory method 
[public org.baeldung.web.BeanB org.baeldung.spring.config.WebConfig.beanB()] threw exception; 
nested exception is org.springframework.beans.factory.BeanIsAbstractException: 
Error creating bean with name 'beanA': Bean definition is abstract

8. 總結
  最後,我們應該有一個清晰的認識:什麼情況會導致什麼樣的BeanCreationException,同時知道如何很好的解決該問題。
這些異常的再現實例可以在 the github project 上找到,下載後用eclipse導入即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章