Reputation: 5761
I was trying to follow a tutorial over at Mkyong regarding Spring. I created two simple classes, Customer and Person, which look like so:
Customer:
public class Customer {
private Person person;
public Customer() {
}
//Getters and setters for person here
public String toString() {
return "Customer [person=" + 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 + "]";
}
}
I then created a Bean configuration file, using an inner bean (which is why I was taking the tutorial) that looks like so:
// 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>
Finally, I created a MainApp to run this code:
public static void main (String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {
"NewBean.xml"});
Customer cust = (Customer) context.getBean("CustomerBean");
System.out.println(cust);
}
Now, to my understanding, this is what is happening:
The xml file is loaded and the bean with id CustomerBean is stored in the reference cust (which is a Customer object). CustomerBean takes an argument, "person", and the details of the three parameters which create the Person are supplied as the inner bean of customerBean.
In the MainApp, the toString method of the reference cust is called and the proper output is displayed.
As I am specifically providing references in my examples, why is a no argument constructor required to run this code? If I remove either the empty Customer or empty Person constructors, the code fails. Why does it fail, at run-time, when the bean I've set up doesn't require a no argument constructor?
Edit:
Example of code pass:
Customer [person=Person [address=4 Ascot House, age=27, name=Andrew]]
Example of code fail:
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
Upvotes: 8
Views: 8467
Reputation: 5004
because it uses fields injection instead your Person(String name, String address, int age) constructor. Please try something like:
...
<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>
...
instead. Thats solution for Person class, for Customer - I dunno ; )
Upvotes: 2
Reputation: 28761
Why does it fail, at run-time, when the bean I've set up doesn't require a no argument constructor?
The bean you have set up does require a no-arg constructor. I am no Spring expert (get stuck in them all the time as a matter of fact) but what you are asking Spring to do it:
freakman's answer shows how to create the Person object using constructor argument, I imagine you can similarly create customer object as well. Then you should not need the no-arg ctors.
Upvotes: 1
Reputation: 149185
Well look again at what you are asking from the Spring container point of view.
Customer
, with only properties : ok create it as new Customer()
using the no-arg constructor and keep it for the moment.Person
with only properties : ok create it as new Person()
, still using the no-arg constructor, give it an arbitrary name and keep it for the momentPerson
bean with its properties using the setters : ok it is readyCustomer
bean with its only property using the setter and the Person
bean as parameter : ok it is readySo you get the beans from the application context and have no new
in your own code, but Spring has constructed them for you, and did use the no-arg constructors.
You could instead have for example :
public class Customer {
private Person person;
public Customer(Person person) {
this.person = person;
}
...
}
and your XML would have to be :
<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>
That way, Spring would have first constructed Person
bean as above and would have created Customer
bean as new Customer(person)
using the one arg constructor.
Upvotes: 5
Reputation: 376
It is an issue with the XML file setup. You are not providing constructor arguments, but rather telling Spring to instantiate a new object with certain properties.
See this stack overflow post: Does Spring require all beans to have a default constructor?
You have to provide the constructor arguments in order to inform Spring that you want to use the constructor with the arguments.
ie:
<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>
This will tell Spring that you want to use the constructor provided that accepts arguments instead of the no-arg constructor. Technically speaking, you do not need to provide the default constructor at all.
Upvotes: 1