user2472968
user2472968

Reputation: 395

Spring is picking an interface implementation out of many, on its own?

Friends below is my code, I am trying to run dependency Injection with Spring

I have an interface, two class implementations of that interface.

One bean.xml and One main method class.

Interface IWriter.java

package DI;
    public interface IWriter {
    public void writer(String s);
}  

Class Writer.java

     package DI;

     import org.springframework.stereotype.Service;

     @Service
     public class Writer implements IWriter {
        public void writer (String s){
            System.out.println(s);
        }
     } 

Class NiceWriter.java

package DI;

import org.springframework.stereotype.Service;

@Service
public class NiceWriter implements IWriter {
    public void writer (String s){
        System.out.println("The string is " + s);
    }
} 

Another class

package DI;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class MySpringBeanWithDependency {

    @Autowired
    private IWriter writer;

    public void run() {
        String s = "This is my test";
        writer.writer(s);
    }
}

Main.java

package DI;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import DI.MySpringBeanWithDependency;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        BeanFactory factory = context;
        MySpringBeanWithDependency test = (MySpringBeanWithDependency) factory.getBean("mySpringBeanWithDependency");
        test.run();
    }
}

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

       <context:component-scan base-package="DI" />

       </beans> 

When I run the code Spring container gives the output of the method of Writer.java class. I haven't anywhere specified which implementation to pick. How is Spring picking up the implementation of Writer.java??

Upvotes: 11

Views: 12069

Answers (5)

rnofenko
rnofenko

Reputation: 9533

I'd like to show one more option using application.properties.

Benefits:

  • You don't need to change code when you add/change implementation of the interface
  • Works well with unit tests and other environments

Sample code based on @ConditionalOnProperty attribute

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

@Service
@ConditionalOnProperty(value = "writer.type", havingValue = "default")
public class Writer implements IWriter {
    @Override
    public void writer(String s) {
        System.out.println("The string is " + s);
    }
}

And

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

@Service
@ConditionalOnProperty(value = "writer.type", havingValue = "nice")
public class NiceWriter implements IWriter {
    @Override
    public void writer(String s) {
        System.out.println("Nice string is " + s);
    }
}

When application.properties contains writer.type=nice NiceWriter will be instantiated for IWriter interface.

Instead of @ConditionalOnProperty there are other options like Conditional, @ConditionalOnExpression.

Upvotes: 1

Sherif Abdelkhaliq
Sherif Abdelkhaliq

Reputation: 150

It depends on the use-case, you can select implementation of the interface using spring profiles, custom annotations or as mentioned by other answers using @Qualifier (injection by name) which is equivalent to JSR-330's @Named annotation

Upvotes: 0

user2606366
user2606366

Reputation:

Try this one.

Class Writer.java

  package DI;

     import org.springframework.stereotype.Service;

     @Service("writer")
     public class Writer implements IWriter {
     public void writer (String s){
      System.out.println(s);
     }
    } 

Class NiceWriter.java

    package DI;

    import org.springframework.stereotype.Service;

   @Service("niceWriter")
   public class NiceWriter implements IWriter {
   public void writer (String s){
    System.out.println("The string is " + s);
   }
  } 

Another class

     package DI;

      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.stereotype.Service;

      @Service
      public class MySpringBeanWithDependency {

     @Autowired
     private IWriter writer;

     @Autowired
    private IWriter niceWriter


       public void run() {
       String s = "This is my test";
        writer.writer(s);
      }
    }

Upvotes: 1

Chathuranga Tennakoon
Chathuranga Tennakoon

Reputation: 2189

change your code as follows.

Class Writer.java

  package DI;

     import org.springframework.stereotype.Service;

     @Service("writer")
     public class Writer implements IWriter {
     public void writer (String s){
      System.out.println(s);
     }
    } 

Class NiceWriter.java

    package DI;

    import org.springframework.stereotype.Service;

   @Service("niceWriter")
   public class NiceWriter implements IWriter {
   public void writer (String s){
    System.out.println("The string is " + s);
   }
  } 

Another class

     package DI;

      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.stereotype.Service;

      @Service
      public class MySpringBeanWithDependency {

     @Autowired
     @Qualifier("writer")//if you need to autowire Writer service   
     private IWriter writer;

     @Autowired
    @Qualifier("niceWriter")//if you need to autowire NiceWriter service
    private IWriter niceWriter


       public void run() {
       String s = "This is my test";
        writer.writer(s);
      }
    }

Upvotes: 10

Ashish Aggarwal
Ashish Aggarwal

Reputation: 3026

When there is more than one implementation of interface and you use @Autowired in that case spring bind any of the class. but if you want to autowire specific implementation then you can use

@Qualifier( "<implementing class name>" ) 

@Qualifier documentation

Few things that you must know about Spring is

  • All spring beans are managed - they "live" inside a container, called "application context".
  • Each application has an entry point to that context. Also, there is a place where the application context is bootstrapped and all beans - autowired. In web applications this can be a startup listener.

Autowiring happens by placing an instance of one bean into the desired field in an instance of another bean. Both classes should be beans, i.e. they should be defined to live in the application context.

Upvotes: 10

Related Questions