Results 1 to 7 of 7

Thread: how to autowire a bean without default constructor

  1. #1
    Join Date
    Mar 2011
    Posts
    4

    Default 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. #2

    Default already solved


  3. #3
    Join Date
    Mar 2011
    Posts
    4

    Default

    Quote 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

  4. #4
    Join Date
    Mar 2011
    Posts
    4

    Default

    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?

  5. #5

    Default 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.

  6. #6
    Join Date
    Mar 2011
    Posts
    4

    Default

    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.

  7. #7

    Default

    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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •