Sunday, 1 November 2015

Spring dependency injection using annotation based Autowiring

Background



In last post you would have seen what dependency injection is and how is it generally achieved in Spring. It is done using beans that you define in you xml file using tags bean , constructor-arg and property.

But as your project size grows it becomes difficult to keep adding bean in your XML files with its dependencies as the size of xml file continues to grow. So Spring has provided a feature called autowiring where bean dependencies are automatically injected based on various criteria like name, type etc (you can find all types in the previous post). Going a step forward Spring also provides annotations based injection in which case you don't event have to define beans. Spring will scan annotations and inject dependencies on it's own Ofcourse there are rules by which this is governed. We will see this autowiring feature in this post with and without annotations.

Lets continue with the Cricket example we saw in Last post.

Models

Lets rewrite our model classes - 

Bat.java

package models;



public class Bat {    

    private String name;

    public Bat() {}

    

    public Bat(String name) {
        this.name= name;
    }

    public String getName() {
        return name;
    }

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

    
    public void play() {
        System.out.println("Bat is swung");
    }

}


Ball.java

package models;

public class Ball {
    

    private String name;


    public Ball() {} 
    

    public Ball(String name) {
        this.name= name;
    }

    public String getName() {
        return name;
    }

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

    public void play() {
        System.out.println("Ball is bowled");
    }

}

And finally Cricket.java

package models;



public class Cricket {

    private Bat bat;
    private Ball ball;

    public Bat getBat() {
        return bat;
    }

    public void setBat(Bat bat) {
        this.bat = bat;
    }

    public Ball getBall() {
        return ball;]
    }

    public void setBall(Ball ball) {
        this.ball = ball;
    }

    

    public void play() {
        ball.play();
        bat.play();
    }

    

}

now lets configure Spring for these models. 

Spring configuration

Under src folder create a file called beans.xml and add following beans to it

beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    

   <bean id="bat" class="models.Bat" />
   <bean id="ball" class="models.Ball" /> 
   <bean id="cricket" class="models.Cricket" autowire="byType"/>

</beans>

Note :  Notice the attribute autowire of tag bean which is set to byType. This means Cricket has dependency on Bat instance so find a bean of type Bat and inject into cricket. You can also set it as byName in which case it will match bean name and inject dependency, However be careful in using this - if spring finds two beans of type Bat it will throw exception.

Now lets run this setup

Main.java

import models.Cricket;



import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {

    
    public static void main(String args[]) {
       
        ApplicationContext context =  new ClassPathXmlApplicationContext("beans.xml");
        Cricket cricket = (Cricket) context.getBean("cricket");
        cricket.play();
    }
    
}
Output :
Ball is bowled
Bat is swung
Now lets see the same thing with annotations.

Note : Note that spring first created beans that are dependencies and then injects in into objects that need them. Also all beans by default are singleton.

Spring dependency injection using annotation based Autowiring

Changes needed
  1. In your beans.xml file remove autowire="byType" from the cricket tag.
  2. In your Cricket class use @Autowired annotation over your Bat and Ball instance variables.
 beans.xml

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

    <context:annotation-config/>
    
   <bean id="bat" class="models.Bat" />
   <bean id="ball" class="models.Ball" /> 
   <bean id="cricket" class="models.Cricket" />

</beans>


Cricket.java

public class Cricket {
    
    @Autowired
    private Bat bat;
    @Autowired
    private Ball ball;
    //more code 
} 



Now run the same program as above and you should again get the same output. Few point to note -
  1.  Using @Autowired annotation is same as saying find the bean by type.If Spring finds no bean of injected type for auto-wiring, then it will throw NoSuchBeanDef Exception. If you think that this dependency is not a must than you can use @Autowired(required=false).
  2. As mentioned before @Autowired annotation is similar to autowiting by type. If there are two beans of same type you land in trouble. In such cases you can use resolve by name. For this you can use @Qualifier(value="beanName")
  3. Combination of using @Autowired(required=false) with @Qualifier(value="beanName") is very flexible as you can use this to specify you dependency is not a mandate and you can also specify bean name to resolve with.

To use Qualifier annotation Change your beans.xml as follows

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

    <context:annotation-config/>

    
   <bean id="myBat" class="models.Bat" />
   <bean id="myBall" class="models.Ball" /> 
   <bean id="cricket" class="models.Cricket" />

</beans>


and your cricket class as follows

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Cricket {

    
    @Autowired(required=false)
    @Qualifier(value="myBat") 
    private Bat bat

    @Autowired(required=false)
    @Qualifier(value="myBall") 
    private Ball ball;

     //more code 

}


and run the Main class again you should see same result.


Note : If you want to resolve by name then you can use @Resource annotation. Sample usage would be @Resource(name="myBat") . But note here that you loose the flexibility of marking it as non mandatory as you did with @Autowired annotation. All dependencies become mandatory.


Change the Cricket class as

public class Cricket {
    

    @Resource(name="myBat") 
    private Bat bat;

    @Resource(name="myBall") 
    private Ball ball;
   
    //More code
} 



and rerun Main methods. You should see same output.


Related Links

No comments:

Post a comment

t> UA-39527780-1 back to top