Spring: Why do we autowire the interface and not the implemented class?

stackoverflow picture stackoverflow · Oct 15, 2012 · Viewed 181.1k times · Source

Example

interface IA
{
  public void someFunction();
}

@Resource(name="b")
class B implements IA
{
  public void someFunction()
  {
    //busy code block
  }
  public void someBfunc()
  {
     //doing b things
  }
}

@Resource(name="c")
class C implements IA
{
  public void someFunction()
  {
    //busy code block
  }
  public void someCfunc()
  {
     //doing C things
  }
}

class MyRunner
{

  @Autowire
  @Qualifier("b") 
  IA worker;

  worker.someFunction();
}

Can someone explain this to me.

  • How does spring know which polymorphic type to use.
  • Do I need @Qualifier or @Resource?
  • Why do we autowire the interface and not the implemented class?

Answer

Vikdor picture Vikdor · Oct 15, 2012

How does spring know which polymorphic type to use.

As long as there is only a single implementation of the interface and that implementation is annotated with @Component with Spring's component scan enabled, Spring framework can find out the (interface, implementation) pair. If component scan is not enabled, then you have to define the bean explicitly in your application-config.xml (or equivalent spring configuration file).

Do I need @Qualifier or @Resource?

Once you have more than one implementation, then you need to qualify each of them and during auto-wiring, you would need to use the @Qualifier annotation to inject the right implementation, along with @Autowired annotation. If you are using @Resource (J2EE semantics), then you should specify the bean name using the name attribute of this annotation.

Why do we autowire the interface and not the implemented class?

Firstly, it is always a good practice to code to interfaces in general. Secondly, in case of spring, you can inject any implementation at runtime. A typical use case is to inject mock implementation during testing stage.

interface IA
{
  public void someFunction();
}


class B implements IA
{
  public void someFunction()
  {
    //busy code block
  }
  public void someBfunc()
  {
     //doing b things
  }
}


class C implements IA
{
  public void someFunction()
  {
    //busy code block
  }
  public void someCfunc()
  {
     //doing C things
  }
}

class MyRunner
{
     @Autowire
     @Qualifier("b") 
     IA worker;

     ....
     worker.someFunction();
}

Your bean configuration should look like this:

<bean id="b" class="B" />
<bean id="c" class="C" />
<bean id="runner" class="MyRunner" />

Alternatively, if you enabled component scan on the package where these are present, then you should qualify each class with @Component as follows:

interface IA
{
  public void someFunction();
}

@Component(value="b")
class B implements IA
{
  public void someFunction()
  {
    //busy code block
  }
  public void someBfunc()
  {
     //doing b things
  }
}


@Component(value="c")
class C implements IA
{
  public void someFunction()
  {
    //busy code block
  }
  public void someCfunc()
  {
     //doing C things
  }
}

@Component    
class MyRunner
{
     @Autowire
     @Qualifier("b") 
     IA worker;

     ....
     worker.someFunction();
}

Then worker in MyRunner will be injected with an instance of type B.