Monday, 4 April 2016

Autowiring using XML configuration in Spring

Spring framework provides an option to autowire the beans. In that case you don't need to explicitly wire the bean properties (using ref attribute) but Spring will do it automatically by using the "autowire" attribute.

In Spring you can use autowiring with XML configuration or use the annotations to autowire the dependencies. This post talks about autowiring with XML configuration.

Kinds of autowiring

Spring provides four kind of autowiring modes (updated as per Spring 4.x where autodetect option has been removed).

  • no - The traditional Spring default. No automagical wiring. Bean references must be defined in the XML file via the <ref/> element (or "ref" attribute). autowire=”default” is same as autowire=”no”

  • byName - Autowiring by name means properties of the autowired bean will be wired by searching for bean with same name/id in the configuration file. If such a bean is found it is injected, properties for which no matching bean is found will remain unwired which may later result in error.

  • byType - Autowiring by type means properties of the autowired bean will be wired by searching for bean whose type is compatible with the property's type. If such a bean is found it is injected, properties for which no matching bean is found will remain unwired which may later result in error.

  • constructor - In this case wiring will be done using the constructor of the autowired bean by looking for the bean whose type is compatible with the constructor argument.

  • autodetect - In case of autodetect auto wiring using constructor will be tried first, if default constructor only is there and autowiring using constructor can't be done then autowiring byType is used. Note that this option was available till Spring 3.x (deprecated) or older versions. In Spring 4.x it is removed and this option is not available anymore. In this post autodetect example is not there.

Autowiring in configuration file

First thing to note here is that by default Spring doesn't autowire and you have to provide explicit wiring using ref attribute for the properties or for constructor-args which are to be dependency injected.

Auto wiring using byname

When you are autowring by name the auto wired bean will look for the bean with the same name/id in the configuration file.

Let's see it with an example -

Here we have a class PayServiceImpl which has a field payment of type IPayment which we have to autowire.

interface IPayService

public interface IPayService {
 void performPayment();
}

PayServiceImpl class

public class PayServiceImpl implements IPayService {
    private IPayment payment;
    private int amount;
    
    public void performPayment() {
            // calling payment class executePayment method
        payment.executePayment(amount);
    }
    
    public IPayment getPayment() {
        return payment;
    }

    
    public void setPayment(IPayment payment) {
        this.payment = payment;
    }

    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }

}

interface IPayment

public interface IPayment {
 void executePayment(int amount);
}

CashPayment class

public class CashPayment implements IPayment{
 public void executePayment(int amount) {
  System.out.println("Perform Cash Payment for amount ... " + amount);
  
 }
}

Configuration file

<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
 
 <!-- defining CashPayment bean -->
 <bean id="payment" class="org.netjs.springexp.prog.CashPayment" />
 
 <!-- Defining PayServiceImpl bean and injecting payment bean -->
 <bean id="payServiceBean" class="org.netjs.springexp.prog.PayServiceImpl" autowire="byName">
     <property name="amount" value="20" />
  </bean>
</beans>

Here notice that for the bean PayServiceImpl autowire=byname is used. In that case Spring will see that PayServiceImpl has one property named as payment so it will look for the bean with same name/id in the config file. Since there is a bean with id as payment so that bean will be injected.

You can run it using the following program -

public class App {
 public static void main(String[] args) {
  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
        IPayService bean = (IPayService) context.getBean("payServiceBean");
        bean.performPayment();
        context.close();

 }
}

Output

Perform Cash Payment for amount ... 20

Auto wiring using byType

When you are autowring by type the property's type in auto wired bean will be used for searching the bean with the compatible type in the configuration file.

You can use the same example as above only change will be in the configuration file to change the autowire=byType.

<bean id="payServiceBean" class="org.netjs.springexp.prog.PayServiceImpl" autowire="byType">
     <property name="amount" value="20" />
</bean>

In the config file there is a bean of type Payment which matches the type of the payment field in the PayServiceImpl class. Thus autowiring by type will work.

Limitation with autowire="byType"

There is one problem with using autowiring as byType. What if there are more than one bean in the configuration file whose type is compatible with the autowired property. In that case Spring won't be able to decide which bean to use, it will throw an exception instead. To see an example of this problem you need to create one more class which implements IPayment interface. Let's say with CashPayment there is also an option for CreditPayment.

CreditPayment class

public class CreditPayment implements IPayment {
 public void executePayment(int amount) {
  System.out.println("Perform Credit Payment for amount ... " + amount);
 }
}

In the config file also the definition is added.

<!-- defining CreditPayment bean -->
<bean id="creditPayment" class="org.netjs.springexp.prog.CreditPayment" />

Now there are two beans of type IPayment in the config file. In that case if we run the program Spring won't be able to decide which one to inject either CashPayment or CreditPayment so it will throw UnsatisfiedDependencyException instead.

Output

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 
'payServiceBean' defined in class path resource [appcontext.xml]: Unsatisfied dependency expressed 
through bean property 'payment': : No qualifying bean of type [org.netjs.springexp.prog.IPayment] is defined: 
expected single matching bean but found 2: payment,creditPayment; nested exception is 
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 
[org.netjs.springexp.prog.IPayment] is defined: expected single matching bean but found 2: payment,creditPayment
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType
 (AbstractAutowireCapableBeanFactory.java:1307)

To overcome this problem there are two options -

  • primary candidate - Identify a primary candidate for auto wiring bean and set that as primary="true"
    <bean id="payment" class="org.netjs.springexp.prog.CashPayment" primary="true"/>
    
  • Exclude a bean from autowiring - You can exclude a bean from being autowired. For that set the autowire-candidate attribute of <bean> tag to false.
    <bean id="creditPayment" class="org.netjs.springexp.prog.CreditPayment" autowire-candidate="false" />
    

Autowiring by constructor

Autowiring by constructor is same like byType in this case type of the constructor argument in the auto wired bean is used to search the bean with the same type.

If you change the PayServiceImpl class to have a constructor which has a IPayment type as a parameter.

public class PayServiceImpl implements IPayService {
 private IPayment payment;
 private int amount;
 
 // Constructor
 PayServiceImpl(IPayment payment){
   this.payment = payment;
 }
  
 public void performPayment() {
  // calling payment class executePayment method
  payment.executePayment(amount);
 }


 public int getAmount() {
  return amount;
 }

 public void setAmount(int amount) {
  this.amount = amount;
 }
}

And change the configuration file as-

<bean id="payServiceBean" class="org.netjs.springexp.prog.PayServiceImpl" autowire="constructor">
     <property name="amount" value="20" />
</bean>

default-autowire attribute

If you are using the same autowire attribute with most of the bean in your config file, you can define that autowire attribute as default for all the beans with in the config file. That can be done by adding a default-autowire attribute to the root </beans> element.

<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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd" default-autowire="byType">

Here default auto wiring is set to byType which means all the beans in this config file will use auto wiring as byType by default. Note that by default default-autowire is set to none.

You can still set autowire attribute for individual beans which will override the default-autowire attribute.

That's all for this topic Autowiring using XML configuration in Spring. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Autowiring using annotations in Spring
  2. What is Dependency Injection in Spring
  3. Spring example program using XML configuration
  4. Spring example program using JavaConfig and Annotations
  5. How to inject prototype scoped bean in singleton bean
  6. Different bean scopes in Spring
  7. Autodiscovery of bean using componenent-scan in Spring

You may also like -

>>>Go to Spring tutorial page

1 comment:

  1. Please post some more topics on Spring

    ReplyDelete