2013-08-26 72 views
2

我有一個Spring控制器的定義如下:的Spring bean創建失敗豆類沒有「id」屬性

@Controller 
@RequestMapping("/queue") 
public class QueueController { 

    QueuesService queueService; 

    public QueueController(QueuesService queueService) { 
     if (queueService == null) { 
      throw new IllegalArgumentException("QueueService cannot be null"); 
     } 
     this.queueService = queueService; 
    } 
} 

在我的情況下,配置文件中的相應條目如下(其中bean定義沒有任何 「id」 屬性):現在

<bean class="com.xy.web.controllers.QueueController"> 
<constructor-arg ref="queueServiceImpl"></constructor-arg> 
</bean> 

,在應用程序啓動,春天拋出異常如下:

Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.xy.web.controllers.QueueController]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.xy.web.controllers.QueueController.<init>() 

但是,當我將「id」屬性添加到「bean定義」(如下所示)時,它的正確創建。

<bean id="queueController" class="com.xy.web.controllers.QueueController"> 
    <constructor-arg ref="queueServiceImpl"></constructor-arg> 
</bean> 

對此的任何解釋還是我在這裏丟失的東西?

+1

作爲一個提示,嘗試改變'id'屬性'asdasd'。 –

回答

5

我打算假設你在你的配置文件中有一個<content:component-scan ...>。這將嘗試實例化任何@Component帶註釋的類。 @Controller@Component,所以Spring將嘗試使用類默認的空構造函數實例化QueueController。在你的情況下,這樣的構造函數不存在。因此它會拋出你看到的異常。

您需要添加一個空的構造

public QueueController() {} 

出現這種情況,無論你的bean聲明

<bean class="com.xy.web.controllers.QueueController"> 
    <constructor-arg ref="queueServiceImpl"></constructor-arg> 
</bean> 

你要去兩個QueueController情況下結束了。這可能不是你想要的。

至於你,因爲ID的看到的行爲:

當應用程序上下文讀取component-scan它會註冊一個BeanComponentDefinition與名稱queueController。上下文然後轉到您的bean聲明。由於您已指定一個等於先前定義的id,它將覆蓋它。你最終只會爲你的QueueController類定義一個bean定義。由於bean聲明需要一個具有特定參數的構造函數,因此它不會發出抱怨並將創建該bean。

如果指定了不同的id,說abcd,你的應用程序上下文會註冊兩個的BeanDefinitions:一個名字queueController(以下默認名稱生成策略)從component-scan和一個從<bean>聲明名稱abcdqueueController需要您沒有的默認構造函數。你將因此得到一個例外。

更詳細的

如果您使用的是ClassPathXmlApplicationContext,看看在ClassPathBeanDefinitionScanner#doScan(String...)方法的以下調用

String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); 

beanNameGeneratorAnnotationBeanNameGenerator一個實例。它最終調用

// Fallback: generate a unique default bean name. 
return buildDefaultBeanName(definition, registry); 

這就要求

String shortClassName = ClassUtils.getShortName(definition.getBeanClassName()); 
return Introspector.decapitalize(shortClassName); 

返回默認名稱queueController。這就是你要覆蓋的那個。

實際上,你可以在日誌中看到這一點:

Mon Aug 26 12:12:15 EDT 2013 [main] INFO o.s.b.f.s.DefaultListableBeanFactory - Overriding bean definition for bean 'queueController': replacing [Generic bean: class [org.test.QueueController]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [C:\Users\sotirios.delimanolis\git\content-store\target\test-classes\org\test\QueueController.class]] with [Generic bean: class [org.test.QueueController]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [app.xml]] 
+0

謝謝@Sotirios Delimanolis對這個很好的解釋。 –