In a Java 11/Spring REST API project I have an interface with multiple implementations. I want to choose the implementation in the configuration (in this case, application.yml file):
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target(TYPE)
public @interface PickableImplementation {
// This id will match an entry in the config file
public String id() default "";
}
So I have the two possible "pickable" implementations:
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.mycompany.api.util.PickableImplementation;
@Component
@Service
@PickableImplementation(id = "s3")
public class BatchProcessServiceS3 implements BatchProcessService {
// Some implementation biz logic
}
// In another file, with the same imports:
@Component
@Service
@PickableImplementation(id = "azure")
public class BatchProcessServiceAzure implements BatchProcessService {
// Another implementation biz logic
}
In the consumer class which in this case is a Controller, I try to pick the desired implementation:
import com.mycompany.api.util.PickableImplementation;
import java.util.List;
@RestController
@RequestMapping("/batch")
public class BatchController {
@Autowired private Environment env;
private BatchProcessService batchProcessService;
private final List<BatchProcessService> batchProcessImplementations;
public BatchController(List<BatchProcessService> batchProcessImplementations,
Environment environment){
this.env = environment;
var activeSvcImplementationId = env.getRequiredProperty("buckets-config.active");
this.batchProcessImplementations = batchProcessImplementations;
for (var batchProcessService : this.batchProcessImplementations) {
if(batchProcessService.getClass().isAnnotationPresent(PickableImplementation.class)) {
// Verify the id, compare with the one from config file, etc.
}
}
}
}
Expected behavior: inside the loop, I expected to get the annotations of each implementation, traverse the list, verify if it matches with the one with the application.yml and if it does, pick it to populate the service layer (private BatchProcessService batchProcessService).
Actual behavior: not only the isAnnotationPresent() method returns false, but also if I try getAnnotations() I get an empty array, like there are no annotations in the class. And besides my custom one, there are at least two additional annotations (Component, Service and others related to logging and the like). As another puzzling detail, if I run getAnnotations() on the qualified name of the class in the middle of a debugging session, the annotations are present. But in that very moment, running that method on the elements on the list return 0 annotations.
I've run out of ideas, has anyone tried this same combination of autowiring several implementations and at the same time, relying in custom annotations?
Additional references:
- Custom Annotations in Java: https://www.baeldung.com/java-custom-annotation
- Autowired: https://www.baeldung.com/spring-autowire
- Useful answer on autowiring multiple implementations: https://stackoverflow.com/a/51778396/6315428