我有一个Spring控制器定义,如下所示:
@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>
现在,在应用程序启动期间,Spring抛出以下异常:
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>
有什么解释吗?还是我在这里遗漏了什么?
我将假设您<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
使用name注册一个queueController
。然后上下文将移至您的bean声明。由于您指定的值id
等于先前的定义,因此它将覆盖它。最后,您的QueueController
类只有一个bean定义。由于bean声明需要一个带有特定参数的构造函数,而您拥有该参数,因此它不会抱怨,并且会创建bean。
如果您指定一个不同的id
,例如abcd
,您的应用程序上下文将注册两个BeanDefinitions:一个来自,带有名称queueController
(遵循默认的名称生成策略)component-scan
,另一个abcd
来自于<bean>
声明中的名称。将queueController
需要一个你没有默认的构造函数。因此,您将获得一个例外。
更详细
如果您使用ClassPathXmlApplicationContext
,请查看该ClassPathBeanDefinitionScanner#doScan(String...)
方法的后续调用
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
该beanNameGenerator
是一个实例AnnotationBeanNameGenerator
。最终它呼唤
// Fallback: generate a unique default bean name.
return buildDefaultBeanName(definition, registry);
哪个电话
String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
return Introspector.decapitalize(shortClassName);
返回默认名称queueController
。那就是您要id
覆盖的那个。
您实际上可以在日志中看到:
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]]
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句