Search code examples
javaspringspring-annotations

How use an annotation's value to initialize a bean


I have below annotation.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Import(MyBeanInitializer.class)
public @interface MyAnnotation {

String clientType() default "" ;

}

And I have a Bean initialiser component as below

@Configuration
public class MyBeanInitializer {

@Bean() // trigger this when annoattion's value == "A"
public CommonBean firstBean() {
    return new BeanA;

}

@Bean() // trigger this when annoattion's value == "B"
public CommonBean firstBean() {
    return new BeanB;

}
}

My Commoin interface for BeanA and BeanB

public interface CommonBean {
void doSomething();
}

And my Two Implementations are

@Component()
public class BeanA implements CommonBean {

 @Overrid
 public void doSomething (){
 // implementation here
 }
}



@Component()
public class BeanB implements CommonBean {

 @Overrid
 public void doSomething (){
 // implementation here
 }
}

I need to use above as an library for another Spring Boot project. In that project I annotate Application.java with @MyAnnotation(clientType="web") and then I inject BeanA or BeanB to a class inside that project by using constructor Injection.

What is the mechanism to initialise beans by looking at the values passed through the annotation?


Solution

  • Don't use an annotation value for this.

    Annotation values are hard-coded at compile time and cannot be dynamically changed. Plus, it would look and feel incredibly awkward in the face of @Conditional, which already exists and ties into the ability to get dynamic properties.

    What you want to do is use either a combination of @Conditional which allows you to define what you want to do given a specific environment variable, or use the @ConditionalOnProperty annotation found in Spring Boot to simply provide the ability to wire in a bean based on the presence of a specific value in a specific property.

    Here's how that'd look. Let's assume that you have properties called common.basicImpl and common.advancedImpl.

    @Component
    @ConditionalOnProperty(prefix = "common", value = "basicImpl")
    public class BeanA implements CommonBean {
    
     @Override
     public void doSomething (){
     // implementation here
     }
    }
    
    
    
    @Component
    @ConditionalOnProperty(prefix = "common", value = "advancedImpl")
    public class BeanB implements CommonBean {
    
     @Override
     public void doSomething (){
     // implementation here
     }
    }
    

    Note that this alone wouldn't resolve a circumstance in which both properties were present, and you can't do multiple @ConditionalOnProperty statements. Adding @ConditionalOnMissingBean to be sure you don't accidentally wire up both of them at the same time would help you out there.