Announcement Announcement Module
Collapse
No announcement yet.
how to autowire a bean without default constructor Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • how to autowire a bean without default constructor

    How to use @Autowired on a field whose constructor need argument?

    if the class RelationalDataSource has default constructor, the @Autowired function properly as follow

    Class CustomerService
    Code:
    package com.cybelink.ioc;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Controller;
    
    
    /**
     *
     * This class uses the DataSource to load Customer data 
     */
    
    @Component
    public class CustomerService {
    	@Autowired
    	// can switch between relationDataSource or xmlDataSource
    	@Qualifier("relationalDataSource")
    	//@Qualifier("xmlDataSource")
    	private DataSource dataSource;
    	private Customer customer;
    	
    	public static void main(String[] args){
    		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    		// don't create the customerService using new, should use spring method to make
    		// customerService a spring managed bean!!
    		//CustomerService customerService = new CustomerService();
    		
    		CustomerService customerService = (CustomerService)ctx.getBean("customerService");
    		customerService.method1();		
    	}
    	
    	public void method1(){		
    		if (dataSource == null)
    			System.out.println("dataSource == null");
    		customer=(Customer)dataSource.retrieveObject();
    		System.out.println(getCustomerName());
    	}
    	
    	
    	public CustomerService() {
    		super();
    		System.out.println("CustomerService Constructor is called");
    	}
    // unrelated code here
    	
    }
    Interface DataSource
    Code:
    package com.cybelink.ioc;
    
    /**
     *
     * Interface that must be implemented by all types of DataSource 
     */
    public interface DataSource {
    	public Object retrieveObject();
    	public void setDataSourceName(String name);
    	public String getDataSourceName();
    	public void storeObject(Object object);
    }
    Class implement interface DataSource
    Code:
    package com.cybelink.ioc;
    
    import org.springframework.stereotype.Component;
    
    /**
     *
     * This class will be used to load the Customer from a Relational Database. 
     */
    
    @Component("relationalDataSource")
    public class RelationalDataSource implements DataSource {
    	private String name;	
    	
    	public RelationalDataSource() {
    		super();
    	}
    
    	/**
    	 * Using the DataSource retrieve data for Customer and build a 
    	 * Customer object to return it to the caller
    	 */
    	public Object retrieveObject() {
    		//get data for Customer object from DB and create a 
    		//Customer object
    		return new Customer("Relational",10);
    	}
    
    	/**
    	 * Set the DataSource name
    	 */
    	public void setDataSourceName(String name) {
    		this.name=name;		
    	}
    
    	/**
    	 * Return the name of the DataSource
    	 */
    	public String getDataSourceName() {
    		return name;
    	}
    
    	/**
    	 * Store Customer into relatioanl DB
    	 */
    	public void storeObject(Object object) {
    		//store the customer data into Relatioanl DB
    		
    	}
    
    }
    the applicationContext.xml is as follow
    HTML Code:
    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
    		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    		xmlns:context="http://www.springframework.org/schema/context"
    		xmlns:aop="http://www.springframework.org/schema/aop"
    		xmlns:tx="http://www.springframework.org/schema/tx"
    		xsi:schemaLocation="
    			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
    			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    
    
    	<!-- ========================= GENERAL DEFINITIONS ========================= -->	
    	<context:annotation-config/>
    	
    	
    	<!-- To allow autodetect and register of beans under com.cybelink.ioc package -->
    	<context:component-scan base-package="com" />
    	
    	
     
    	<!-- Velocity Engine for email template -->
    	<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
          <property name="velocityProperties">
             <value>
             		input.encoding=UTF-8
                resource.loader=class
                class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
             </value>
          </property>
       </bean>
      
    </beans>
    but if I change the no-args constructor of relationalDataSource to
    Code:
    public RelationalDataSource(int a) {
    		super();
    	}
    error occur as
    Code:
    Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerService': Autowiring of fields failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.cybelink.ioc.DataSource com.cybelink.ioc.CustomerService.dataSource; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'relationalDataSource' defined in file [C:\workspace\workspace1\Test\bin\com\cybelink\ioc\RelationalDataSource.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.cybelink.ioc.RelationalDataSource]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.cybelink.ioc.RelationalDataSource.<init>()

  • #2
    already solved

    see previous thread
    http://forum.springsource.org/showthread.php?t=52810

    Comment


    • #3
      Originally posted by Justin Botelle View Post
      but the problem is how to assign value to the int a (the constructor parameter) ?

      I add @Autowired in the constructor
      Code:
      @Autowired
      	public RelationalDataSource(int a) {
      		super();
      	}

      now, I get the error as follow:
      Code:
      Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerService': Autowiring of fields failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.cybelink.ioc.DataSource com.cybelink.ioc.CustomerService.dataSource; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'relationalDataSource' defined in file [C:\workspace\workspace1\Test\bin\com\cybelink\ioc\RelationalDataSource.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [int]: : No unique bean of type [int] is defined: Unsatisfied dependency of type [int]: expected at least 1 matching bean; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [int] is defined: Unsatisfied dependency of type [int]: expected at least 1 matching bean

      Comment


      • #4
        if using xml method (not annotation method), there is something like
        Code:
        <constructor-arg> <value>100</value>
            	</constructor-arg>
        but how to do this using @Autowired annotation?

        Comment


        • #5
          don't use a primitive as constructor parameter

          So, I modified your constructor to take in another object (and that way you can switch out the value of a in your example by modifying this passed in object).

          Code:
          @Repository("relationalDataSource")
          public class DataSource {
          
              private int a;
              
              @Autowired
              public DataSource(DataSourceSupplier d) {
                  super();
                  a = d.getA();
              }
          and then introducing this new object:

          Code:
          @Component("dataSupplier")
          public class DataSourceSupplier {
             
              public int getA() {return 1; }
              
          
          }
          Presumably you'd make DataSourceSupplier an interface and use different implementations per your pattern, distinguishing them by bean name.

          Comment


          • #6
            your example of using another object DataSourceSupplier d work.
            But when I try to use Integer as constructor args (not primitive this time)

            Code:
            @Autowired
            public RelationalDataSource(Integer d) {
            	super();
            	a = d.intValue();
            }
            I got the following exception
            Code:
            Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [java.lang.Integer] is defined: Unsatisfied dependency of type [class java.lang.Integer]: expected at least 1 matching bean
            so is it not allow to use java standard class as constructor args?

            I try to add the following statement in applicationContext.xml
            HTML Code:
            <bean id="Integer" class="java.lang.Integer"/>
            and then the following exception prompted:
            Code:
            Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [java.lang.Integer]: No default constructor found; nested exception is java.lang.NoSuchMethodException: java.lang.Integer.<init>()
            Also, another problem, in annotation like
            @Component("relationalDataSource")
            what is the exact meaning of the ("relationalDataSource") after @Component?
            I use it because there are 2 class (named RelationalDataSource and XMLDataSource) implement the same interface named DataSource and so I use ("relationalDataSource") as @Qualifier, but if fact I do not know the exact meaning of ("relationDataSource") after @Component, especially if there is only one class inplement the interface DataSource.

            Comment


            • #7
              If you want to do it all with annotations, then you couldn't have the ctor take an Integer e.g. (how would you specify the value). The name after Qualifier is to disambiguate different classes of the same type.

              If you autowired based on an interface name (as is proper), and you had two different beans that implemented that interface, if you give them a name which you put in quotes after the service declaration, etc., then you can refer to that specific name with a qualifier declaration.

              Comment

              Working...
              X