Friday, 27 May 2016

Bean definition inheritance in Spring

In object-oriented programming there is a parent-child relationship among classes where child class inherits properties and methods of the parent class through inheritance. Same concept of inheritance is provided in Spring also where bean definition is inherited.

A bean definition can contain a lot of configuration information, including constructor arguments, property values, and container-specific information such as initialization method, static factory method name and so on. A child bean definition inherits configuration data from a parent definition.

That way common things can be moved to one bean and other beans can use it by inheriting that parent bean. In addition to that child definition can override some values, or add others, as needed.

Bean definition inheritance configuration

In XML-based configuration metadata, you indicate a child bean definition by using the parent attribute, specifying the parent bean as the value of this attribute.

A child bean definition uses the bean class from the parent definition if none is specified, but can also override it. In the latter case, the child bean class must be compatible with the parent, that is, it must accept the parent's property values.

A child bean definition inherits scope, constructor argument values, property values, and method overrides from the parent, with the option to add new values. Any scope, initialization method, destroy method, and/or static factory method settings that you specify will override the corresponding parent settings.

The remaining settings are always taken from the child definition: depends on, autowire mode, dependency check, singleton, lazy init.

Example Code

Let's see an example where we have a Payment class with two fields in it paymentType and amount. If you want to have a configuration where by default paymentType is "cash" then it can be done using bean definition inheritance.

Payment class

public class Payment {
    private String paymentType;
    private double amount;
    public String getPaymentType() {
        return paymentType;
    }
    public void setPaymentType(String paymentType) {
        this.paymentType = paymentType;
    }
    public double getAmount() {
        return amount;
    }
    public void setAmount(double amount) {
        this.amount = amount;
    }
    
    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("Payment Type ").append(getPaymentType());
        sb.append(" Amount ").append(getAmount());
        return sb.toString();
    }
}

XML Configuration

<?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:aop="http://www.springframework.org/schema/aop"
    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
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
 
<!--  <context:component-scan base-package="org.netjs.prog" /> --> 

    <bean id="basePaymentBean" class="org.netjs.prog.Payment">
        <property name="paymentType" value="cash"/>
    </bean> 
    
    <bean id="paymentBean" parent="basePaymentBean">
        <property name="amount" value="50.15" />
        <!-- paymentType value will be inherited from parent -->
    </bean>
 
</beans>

You can run it using the following program.

import org.netjs.prog.Payment;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {

 public static void main(String[] args) {
  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
        Payment bean = (Payment) context.getBean("paymentBean");
        System.out.println("Values " + bean);
        context.close();
 }
}

Output

Values Payment Type cash Amount 50.15

Overriding the value

As already stated child bean definition can override the properties inherited from the parent bean. As example if you want to override the default option of "cash" provided through parent bean definition and have the value as "credit card" for the paymentType then it can be done as shown in below XML configuration.

<bean id="basePaymentBean" class="org.netjs.prog.Payment" abstract="true">
    <property name="paymentType" value="cash"/>
</bean> 
    
<bean id="paymentBean" parent="basePaymentBean">
    <property name="paymentType" value="credit card"/>
    <property name="amount" value="50.15" />
</bean>

Output

Values Payment Type credit card Amount 50.15

If you have observed in the XML configuration the parent class has an attribute "abstract" as true. Marking the bean as abstract means that it can't be instantiated.

Actually that's how inheritance with in the bean definition must be used. Parent class must be defined with common property values and the child beans should define values for the other values.

Since parent definition has values for common values so it is more of a template which should be inherited and then values for the other values should be provided by the child bean. In that case it doesn't make sense to instantiate the parent class itself as it is just a template.

In fact parent bean can be defined as a pure template itself let's see how it can be done.

Using parent bean as Template

If you want to use parent bean as a template then you don't need to have a class defined for the parent bean and it has to have abstract attribute as true. In that case XML configuration will look like this -

<?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:aop="http://www.springframework.org/schema/aop"
    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
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="basePaymentBean" abstract="true">
        <property name="paymentType" value="cash"/>
    </bean> 
    
    <bean id="ccPaymentBean" parent="basePaymentBean" class="org.netjs.prog.CCPayment">
        <property name="paymentType" value="credit card"/>
        <property name="amount" value="50.15" />
    </bean>
 
</beans>

CCPayment class

public class CCPayment {
 private String paymentType;
 private double amount;
 public String getPaymentType() {
  return paymentType;
 }
 public void setPaymentType(String paymentType) {
  this.paymentType = paymentType;
 }
 public double getAmount() {
  return amount;
 }
 public void setAmount(double amount) {
  this.amount = amount;
 }
 
 @Override
 public String toString() {
  StringBuffer sb = new StringBuffer();
  sb.append("Payment Type ").append(getPaymentType());
  sb.append(" Amount ").append(getAmount());
  return sb.toString();
 }
}

You can run it using the following class -

public class App {

 public static void main(String[] args) {
  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
  CCPayment bean = (CCPayment) context.getBean("ccPaymentBean");
        System.out.println("Values " + bean);
        context.close();

 }

}

Output

Values Payment Type credit card Amount 50.15

That's all for this topic Bean definition inheritance in Spring. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Spring bean life cycle
  2. Different bean scopes in Spring
  3. Autowiring using XML configuration in Spring
  4. Autowiring using annotations in Spring
  5. Difference between component-scan and annotation-config in Spring

You may also like -

>>>Go to Spring tutorial page

No comments:

Post a Comment