2

I am working in Spring Boot, and in MyService class I got a class name as String and i want to initialize that object and get back returned values.

but I have no much more idea about it, I think it achieved by Dependency injection. but, how ?

Suppose I have classes A.java, B.java, C.java and Service class MyService.java

@Component
public class A{
    public String wish(int timeHr){
       //some logic of time based wish
    return "Hello A"+" good morning";
}
}

@Component
public class B{
    public String wish(int timeHr){
       //some logic of time based wish
    return "Hello B"+" good morning";
}
}

@Component
public class C{
    public String wish(int timeHr){
       //some logic of time based wish
    return "Hello C"+" good morning";
}
}

@Service
public class MyService{
   // i get here A class name as String like,
   String classNameString = "A"; // or "B", or "C"
   int timrHr =  new Date().getHours();
   //how to here i will initialize above class and get method wist(param) returned wish msg.
   //like, a.wish(timeHr); and it gives "Hello A good morning"
}

I expect the output of returned by wish().

Can any one suggest me how do I achieved it ?

Thanks.

Sana
  • 360
  • 3
  • 13
programmer420
  • 47
  • 1
  • 11
  • 1
    Why dont you use `@Autowire` in your `MyService`? Like `@Autowire A a;` – arnonuem Aug 02 '19 at 10:49
  • @arnonuem but how to we decide to autowired when we have String of class name like,` String classNameString = "A" or "B" or "C"`. thanks to reply. – programmer420 Aug 02 '19 at 11:05
  • Might be helpful https://stackoverflow.com/questions/5725222/how-to-use-autowired-to-dynamically-inject-implementation-like-a-factory-patter – Romil Patel Aug 02 '19 at 11:08

3 Answers3

3

I can think of two ways here. First way is to name the class (after @Component) and using context, try to get the bean. You can also name the bean using @Qualifier

@Component("A")
public class A{
    public String wish(int timeHr){
       //some logic of time based wish
    return "Hello A"+" good morning";
}


@Component("B")
public class B{
    public String wish(int timeHr){
       //some logic of time based wish
    return "Hello B"+" good morning";
}
}

@Component("C")
public class C{
    public String wish(int timeHr){
       //some logic of time based wish
    return "Hello C"+" good morning";
}
}


@Service
public class MyService{
   @Autowired
   private ApplicationContext context;

  void myMethod() {
    A a = (A) context.getBean("A");


     System.out.println(a.wish(123));
   } 
    }

Second way is to make all your wisher classes implement an interface and iterate through all the implementing beans of this interface and find the correct bean

@Component
public class A implements Wisher{
    public String wish(int timeHr){
       //some logic of time based wish
    return "Hello A"+" good morning";
}


@Component
public class B implements Wisher{
    public String wish(int timeHr){
       //some logic of time based wish
    return "Hello B"+" good morning";
}
}

@Component
public class C implements Wisher{
    public String wish(int timeHr){
       //some logic of time based wish
    return "Hello C"+" good morning";
}
}

public interface Wisher{ public String wish(int time); }

@Service
public class MyService{
   @Autowired
   private List<Wisher> wishers;

void myMethod() {
    String requiredClassName = "A";
    Wisher requiredWisher = null;
    for (int i = 0; i < wishers.length(); i++) {
        Wisher w = wishers.get(i);
        if (w.getClass().getName().equals(requiredClassName)) {
            requiredWisher = w;
            break;
        }

        System.out.println(w.wish(123));
    }
}



    }
Praveen E
  • 926
  • 8
  • 14
  • Thanks your 1st way is work for me. But i have a doubt, Is it a good practice to Autowired ApplicationContext in service class and it have any disadvantage of it ? – programmer420 Aug 02 '19 at 13:04
  • 1
    @programmer420 I don't see any adv/disadv of using it. We are using it in production code itself. You can try using ApplicationContextAware interface as said in https://stackoverflow.com/questions/4914012/how-to-inject-applicationcontext-itself – Praveen E Aug 02 '19 at 13:10
1

String is not a manageable bean by Spring IoC framework, but instances of classes A, B and C are your beans.

What you should do is to declare the classes A, B and C as implement of the common interface and inject a corresponding bean by this type:

interface Wisher {
   String wish(int timeHr);
}

@Component
public class A implements Wisher {
  public String wish(int timeHr){
     //some logic of time based wish
    return "Hello A"+" good morning";
  }
}

@Component
public class B implements Wisher {
  public String wish(int timeHr){
     //some logic of time based wish
  return "Hello B"+" good morning";
  }
}

@Component
public class C implements Wisher {
  public String wish(int timeHr){
     //some logic of time based wish
  return "Hello C"+" good morning";
  } 
}

@Service
public class MyService{
   @Autowired
   private Wisher a; // instance of "A" or "B", or "C"

   void myMethod() {
        int timrHr =  new Date().getHours();
        wisher.wish(timrHr);
   } 
 }
star67
  • 1,505
  • 7
  • 16
  • Thanks to replying here, But to we instance here A, B, or C . when i have String of Component class like String s ="A" or "B" or "C". – programmer420 Aug 02 '19 at 11:02
  • you can declare your strings as beans: `@Bean String stringA() { return "Hello A"+" good morning";}` in a class annotated with `@Configuration` and inject them, but does it make any sense?))) – star67 Aug 02 '19 at 11:11
  • i get class name as a String from request to Controller and Service, it must initialise this class here and returned the particular wish(). – programmer420 Aug 02 '19 at 11:21
  • 1
    what about having a `Map wishers` in `MyService` where names are mapped to `A`, `B` and `C`? when you receive a request with a name you can retrieve a corresponding `Wisher` from the map and call `wisher.wish()`. – star67 Aug 02 '19 at 11:24
  • 2
    @star67 , your autowiring of Wisher interface will not work. Spring will not understand which bean among A or B or C to Autowire. In such cases, you have to use Qualifier annotation. Please see my answer below. – Praveen E Aug 02 '19 at 12:35
1

Considering you are using Spring for implementation all your beans will be Singleton and these will be initialized at the start of App(when ApplicationContext is loaded) and then at that time application has no way to check which implementation to inject.

So there is no way you can conditionally inject bean at runtime with Dependency injection

instead you can use other design like below -

@Service
public class MyService{

  private Wisher wisher;

  public Wisher setupWisher(String class){

   if (class.equals("A")) {
        wisher = new A();
    }else if(class.equals("B")){
        wisher = new B();
    }else if(class.equals("C")){
        wisher = new C();
    }
 }

 void myMethod(String requestString) {
    int timrHr =  new Date().getHours();
    setupWisher(requestString).wish(timrHr);       
 }

}
psi
  • 269
  • 2
  • 13
  • thanks for replying your answer is a good idea . i have a question , what is disadvantage when we go here with ApplicationContext (above answer by Praveen E) ? – programmer420 Aug 02 '19 at 13:08
  • 1
    there is no such disadvantage.. nowadays developers use applicationcontext by autowired just as below. it all boils down to design which you think should be selected for implementation. I will prefer the one i suggested as i don't want A,B,C to be spring bean within applicationcontext because it does nothing extra to qualify as spring bean – psi Aug 03 '19 at 09:45