Search This Blog

Sunday, 11 March 2012

Autowiring - By Name

In the posts so far we have been defining our beans and configuring them using configuration files. However this results in very large XML files. Spring allows the properties of beans to be automatically wired based on the name of the property or some other conditions.
Spring supports four kind of autowiring:
  1. autowire by name
  2. autowire by type
  3. autowire by constructor
  4. auto detect
 For trying out the above methods I created a simple car interface:
public interface ICar {
    public String describe();
}
I also created a couple of other simple classes to use as properties for the Car implementations:
public class Engine {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Engine - " + super.toString() + " and name " + this.getName();
    }
}

package com.car;

public class Seating {
    private String color;

    public void setColor(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    @Override
    public String toString() {
        return "seating - " + super.toString() + " of color " + this.getColor();
    }
}
autowire by name:
All the beans that we create have names(or ids). If for a bean, the name of the property matches that of the bean, then the property will be wired with that bean reference.Consider the sample car implementation:
public class CarByName implements ICar {
    private Engine combustEngine;
    private Seating leatherSeating;

    public Engine getCombustEngine() {
        return combustEngine;
    }

    public void setCombustEngine(Engine combustEngine) {
        this.combustEngine = combustEngine;
    }

    public Seating getLeatherSeating() {
        return leatherSeating;
    }

    public void setLeatherSeating(Seating leatherSeating) {
        this.leatherSeating = leatherSeating;
    }

    @Override
    public String describe() {        
        return "Car is " + this + ", engine is " + this.getCombustEngine() 
+ " and seating is " + this.getLeatherSeating();
    }
}
The CarByName class has two properties a combustEngine and leatherSeating. To ensure that the class is autowired by name:
<bean id="carByName" class="com.car.CarByName" autowire="byName"/>
<bean id="combustEngine" class="com.car.Engine" p:name="Combusto" />
<bean id="leatherSeating" class="com.car.Seating" p:color="red" />
As can be seen, we have created two beans with the names that match the properties. Also for the "carByName" bean we have removed the property/constructor-arg elements. Instead we have added an autowire property with the value "byName".
We have used the p-namespace to further shorten the XML.
I created the below sample code to test the above bean:
public static void main(String[] args) {
    final BeanFactory beanFactory  = new XmlBeanFactory(new ClassPathResource("autowired-beans.xml"));
    CarByName carByName = (CarByName) beanFactory.getBean("carByName");
    System.out.println("Bean received is " + carByName.describe());
}
The output is as below:
Bean received is Car is com.car.CarByName@872380, engine is Engine - 
com.car.Engine@2bb514 and name Combusto and seating is seating - 
com.car.Seating@17d5d2a of color red
The Spring Container instantiates the carByName bean. It then looks for beans with names that match the names of the properties. It instantiates the beans if necessary and configures the carByName bean using setter injection.
I changed the type of leatherSeating bean to Engine class
<bean id="leatherSeating" class="com.car.Engine" p:name="Combusto" />
On trying to execute the above method, we now get the exception:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'carByName' defined in class path resource 
[autowired-beans.xml]: Initialization of bean failed; nested exception is 
org.springframework.beans.ConversionNotSupportedException: Failed to convert 
property value of type 'com.car.Engine' to required type 'com.car.Seating' for 
property 'leatherSeating'; nested exception is java.lang.IllegalStateException: 
Cannot convert value of type [com.car.Engine] to required type [com.car.Seating] 
for property 'leatherSeating': no matching editors or conversion strategy found
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
doCreateBean(AbstractAutowireCapableBeanFactory.java:527)
Spring needs to find Beans of the same type as the property and with the same name, else the bean creation fails.
Also if we have multiple objects of CarByName class and all are autowired by name, then all the objects will end up with the same engine and seating objects configured.

No comments:

Post a Comment