2
votes

This is BeanClass.java

package com.practice.spring;

import org.springframework.beans.factory.annotation.Required;

import com.apress.springrecipes.sequence.Mandatory;

public class BeanClass {

    private int count;
    private String prefix;

    public BeanClass() {
        System.out.println("Default Constructor");
    }

    public BeanClass(int count, String prefix) {
        this.count = count;
        this.prefix = prefix;
        System.out.println(prefix+count);
    }

    @Required
    public void setCount(int count) {
        this.count = count;
    }

    @Mandatory
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    @Override
    public String toString() {
        return prefix+count;
    }

}

This is beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config />

    <bean id="beanClass1"
          class="com.practice.spring.BeanClass">
        <property name="count" value="1" />
        <property name="prefix" value="Bean" />
    </bean>
    <bean id="beanClass2"
          class="com.practice.spring.BeanClass">
        <constructor-arg value="2" />
        <constructor-arg value="Bean" />
    </bean>
    <bean id="beanClass3"
          class="com.practice.spring.BeanClass">
        <constructor-arg>
            <value>3</value>
        </constructor-arg>
        <constructor-arg>
            <value>Bean</value>
        </constructor-arg>
    </bean>
</beans>

And this is how I instantiate the context.

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

Problem:

count property is required & prefix is mandatory. If you see the beanClass2 I am setting the properties via constructor and same in beanClass3

But when I execute the code, it throws exception. The important thing that's confusing me is:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanClass2' defined in class path resource [beans.xml]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanInitializationException: Property 'count' is required for bean 'beanClass2'

It prints Default Constructor & Bean2. I am also pasting the whole stacktrace here:

Dec 09, 2014 8:47:33 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1a71e93: startup date [Tue Dec 09 20:47:33 PKT 2014]; root of context hierarchy Dec 09, 2014 8:47:33 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [beans.xml] Dec 09, 2014 8:47:33 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@4aed64: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,beanClass1,beanClass2,beanClass3]; root of factory hierarchy

Default Constructor

Bean2

Dec 09, 2014 8:47:33 PM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@4aed64: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,beanClass1,beanClass2,beanClass3]; root of factory hierarchy Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanClass2' defined in class path resource [beans.xml]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanInitializationException: Property 'count' is required for bean 'beanClass2' at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:563) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:872) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:423) at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:83) at com.practice.spring.Main.main(Main.java:13) Caused by: org.springframework.beans.factory.BeanInitializationException: Property 'count' is required for bean 'beanClass2' at org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.postProcessPropertyValues(RequiredAnnotationBeanPostProcessor.java:149) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1064) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517) ... 11 more

1
The issue may be that you are requiring that your setters get invoked, but in your constructor, you're setting the properties manually. What happens if you change your constructor to call the setters?Josh Edwards
What's the point then? We can inject via setters or constructors, no?SSC
Yes. But you have told the code that setCount HAS to be called, and you're not calling it. Spring doesn't know what you're going to do in your setter - you might be doing things other than just setting the property; for instance, you may be calculating the property. So, it has no way to know that what you are doing in the constructor is functionally equivalent to what you said is required.Josh Edwards
This makes no sense @ all. May be you can provide me with the spring ioc docs?SSC
Additionally, based on the current docs, @ Required "Marks a method (typically a JavaBean setter method) as being 'required': that is, the setter method must be configured to be dependency-injected with a value." docs.spring.io/spring/docs/current/javadoc-api/org/… So, you're marking it as having to be dependency injected - not constructor injected.Josh Edwards

1 Answers

3
votes

@Required implies that you are using setter injection, instead of constructor injection. These are designed to be two options - not to do both options at the same time.

Note this from Spring's blog -

@ Required allows you to instruct Spring to check required dependencies for you. In case you are not in the position to use constructor injection, or for whatever other reasons, you prefer setter injection, @ Required is the way to go.

Setter Injection vs Constructor Injection .

More recently,

@ Required Marks a method (typically a JavaBean setter method) as being 'required': that is, the setter method must be configured to be dependency-injected with a value.

RequiredAnnotation

So, you're marking it as having to be dependency injected - not constructor injected.