为什么运行这个简单的Spring配置需要一个no-args构造函数?

时间:2014-09-19 13:13:02

标签: java spring constructor

我正试图在Mkyong关于Spring的教程。我创建了两个简单的类,Customer和Person,看起来像这样:

客户

public class Customer {

    private Person person;

    public Customer() {
    }

    //Getters and setters for person here

    public String toString() {
        return "Customer [person=" + person +"]";
    }
}

public class Person {

    private String name;
    private String address;
    private int age;

    public Person() {

    }

    public Person(String name, String address, int age) {
        this.name = name;
        this.address = address;
        this.age = age;
    }

    //All getters and setters

    public String toString() {
        return "Person [address=" + address + ", age=" + age + ", name=" + name + "]";
    }
}

然后我创建了一个Bean配置文件,使用内部bean(这就是我参加教程的原因),看起来像这样:

// Standard bean declarations

    <bean id="CustomerBean" class="com.andrew.SpringInnerBeans.Customer">
        <property name="person">
            <bean class="com.andrew.SpringInnerBeans.Person">
                <property name="name" value="Andrew" />
                <property name="address" value="Address" />
                <property name="age" value="27" />
            </bean>
        </property>
    </bean>
</beans>

最后,我创建了一个MainApp来运行此代码:

public static void main (String[] args) {

    ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {
            "NewBean.xml"});

    Customer cust = (Customer) context.getBean("CustomerBean");
    System.out.println(cust);

}

现在,根据我的理解,这就是正在发生的事情:

加载xml文件,ID为CustomerBean的bean存储在引用cust(它是Customer对象)中。 CustomerBean接受一个参数&#34; person&#34;,并且创建Person的三个参数的详细信息作为customerBean的内部bean提供。

在MainApp中,调用引用cust的toString方法并显示正确的输出。

由于我在我的示例中专门提供了引用,为什么运行此代码需要无参数构造函数?如果我删除空Customer或空Person构造函数,代码将失败。为什么它在运行时失败,当我设置的bean不需要无参数构造函数时?

编辑:

代码传递示例:

Customer [person=Person [address=4 Ascot House, age=27, name=Andrew]]

代码失败的示例:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'CustomerBean' defined in class path resource [NewBean.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.andrew.SpringInnerBeans.Customer]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.andrew.SpringInnerBeans.Customer.<init>()
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1095)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1040)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:505)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:725)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:93)
    at com.andrew.SpringInnerBeans.MainApp.main(MainApp.java:10)
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.andrew.SpringInnerBeans.Customer]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.andrew.SpringInnerBeans.Customer.<init>()
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:85)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1088)
    ... 13 more
Caused by: java.lang.NoSuchMethodException: com.andrew.SpringInnerBeans.Customer.<init>()
    at java.lang.Class.getConstructor0(Unknown Source)
    at java.lang.Class.getDeclaredConstructor(Unknown Source)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:80)
    ... 14 more

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'CustomerBean' defined in class path resource [NewBean.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.andrew.SpringInnerBeans.Customer]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.andrew.SpringInnerBeans.Customer.<init>()
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1095)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1040)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:505)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:725)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:93)
    at com.andrew.SpringInnerBeans.MainApp.main(MainApp.java:10)
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.andrew.SpringInnerBeans.Customer]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.andrew.SpringInnerBeans.Customer.<init>()
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:85)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1088)
    ... 13 more
Caused by: java.lang.NoSuchMethodException: com.andrew.SpringInnerBeans.Customer.<init>()
    at java.lang.Class.getConstructor0(Unknown Source)
    at java.lang.Class.getDeclaredConstructor(Unknown Source)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:80)
    ... 14 more

4 个答案:

答案 0 :(得分:2)

因为它使用字段注入而不是您的Person(String name,String address,int age)构造函数。请尝试类似:

 ... 
<property name="person">
        <bean class="com.andrew.SpringInnerBeans.Person">
            <constructor-arg index="0" value="Andrew"/>
            <constructor-arg index="1" value="Address"/>
            <constructor-arg index="2" value="27"/>
       </bean>
 </property>
... 

代替。这是Person类的解决方案,对于客户 - 我不知道; )

答案 1 :(得分:2)

再看一下你从Spring容器的角度提出的要求。

  • 找到了一个类Customer的bean,只有属性:ok使用no-arg构造函数创建它为new Customer()并暂时保留它。
  • 发现了一个只有Person类的bean,只有属性:ok创建它为new Person(),仍然使用no-arg构造函数,给它一个任意名称并暂时保留它
  • 使用setter填充Person bean及其属性:ok it ready
  • 使用setter和Customer bean作为参数填充Person bean的唯一属性:ok it ready
  • 很好,应用程序上下文现已完全填充

因此,您从应用程序上下文中获取bean并且在您自己的代码中没有new,但是Spring已经为您构建了它们,并且确实使用了无参数构造函数。

你可以改为举例:

public class Customer {

    private Person person;

    public Customer(Person person) {
        this.person = person;
    }
    ...
}

你的XML必须是:

<bean id="CustomerBean" class="com.andrew.SpringInnerBeans.Customer">
    <constructor-arg>
        <bean class="com.andrew.SpringInnerBeans.Person">
            <property name="name" value="Andrew" />
            <property name="address" value="Address" />
            <property name="age" value="27" />
        </bean>
    </constructor-arg>
</bean>

这样,Spring将首先构建Person bean,并使用一个arg构造函数创建Customer bean作为new Customer(person)

答案 2 :(得分:1)

这是XML文件设置的问题。您没有提供构造函数参数,而是告诉Spring实例化具有某些属性的新对象。

看到这个堆栈溢出帖子: Does Spring require all beans to have a default constructor?

您必须提供构造函数参数,以便通知Spring您希望将构造函数与参数一起使用。

即: <bean id="CustomerBean" class="com.andrew.SpringInnerBeans.Customer"> <property name="person"> <bean class="com.andrew.SpringInnerBeans.Person"> <constructor-arg index="0" value="Andrew"/> <constructor-arg index="1" value="Address"/> <constructor-arg index="2" value="27"/> </bean> </property> </bean>

这将告诉Spring你想要使用提供的构造函数来接受参数而不是no-arg构造函数。从技术上讲,您根本不需要提供默认构造函数。

答案 3 :(得分:1)

  

为什么在运行时,当我设置的bean不需要无参数构造函数时,它会失败?

您设置的bean 需要一个无参数构造函数。我不是Spring专家(事实上一直困在他们身上)但是你要求Spring做的事情:

  • 使用no-arg构造函数创建客户对象
  • 使用no-arg构造函数创建Person对象
  • 使用setter
  • 在person对象中设置属性
  • 再次使用setters
  • 将客户对象中的人物属性设置为上面创建的人物对象

freakman's answer展示了如何使用构造函数参数创建Person对象,我想你也可以类似地创建客户对象。那么你不应该需要无法治疗的人。