Programatically find the qualifier annotation value of a bean - spring

I've a class where I've autowired a list of type
Class A{
#Autowired
List<Super> supers;
}
#Qualifier("B")
Class BSuperImpl implements Super{....}
#Qualifier("C")
Class CSuperImpl implements Super{....}
#Qualifier("D")
Class DSuperImpl implements Super{....}
Now I want to write a code in Class A, where I can look upon the bean, based upon the qualifier like following, Without making my bean aware of the Context. Using Spring MVC 4.3
Class A {
#Autowired
List<Super> supers;
void process(String str){
// Code to do supers.applyMethod(getTheBeanWithQualifierAs(str));
}
}

For what it's worth
#Service
public class A {
#Autowired
List<Super> supers;
void process(String str) {
for(Super sup : supers) {
if(str.equals((sup.getClass().getAnnotation(Qualifier.class).value()))){
// executes when the bean qualifier name and str matches.
}
}
}
}
---- Another attempt ----
Interface
public interface Super {
String getQualifier();
}
Sample Implemenation
#Service
#Qualifier(BSuperImpl.QUALIFIER)
public class BSuperImpl implements Super {
static final String QUALIFIER = "B";
#Override
public String getQualifier() {
return QUALIFIER;
}
}
A
#Service
public class A {
#Autowired
Map<String,Super> supers;
void process(String str) {
System.out.println(supers);
for(String beanName : supers.keySet()) {
if(str.equals(supers.get(beanName).getQualifier())){
// execute the logic
}
}
}
}
When a bean is required from the container not through dependency injection , one way or another you will need to refer the application context.
In this approach , the usage of #Qualifier is not required infact . Usage of map is to demonstrate a possiblity that the beanName can also be passed as parameter to the method .

Related

Spring boot autowiring an interface with multiple implementations

In normal Spring, when we want to autowire an interface, we define it's implementation in Spring context file.
What about Spring boot?
how can we achieve this?
currently we only autowire classes that are not interfaces.
Another part of this question is about using a class in a Junit class inside a Spring boot project.
If we want to use a CalendarUtil for example, if we autowire CalendarUtil, it will throw a null pointer exception. What can we do in this case? I just initialized using "new" for now...
Use #Qualifier annotation is used to differentiate beans of the same interface
Take look at Spring Boot documentation
Also, to inject all beans of the same interface, just autowire List of interface
(The same way in Spring / Spring Boot / SpringBootTest)
Example below:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
public interface MyService {
void doWork();
}
#Service
#Qualifier("firstService")
public static class FirstServiceImpl implements MyService {
#Override
public void doWork() {
System.out.println("firstService work");
}
}
#Service
#Qualifier("secondService")
public static class SecondServiceImpl implements MyService {
#Override
public void doWork() {
System.out.println("secondService work");
}
}
#Component
public static class FirstManager {
private final MyService myService;
#Autowired // inject FirstServiceImpl
public FirstManager(#Qualifier("firstService") MyService myService) {
this.myService = myService;
}
#PostConstruct
public void startWork() {
System.out.println("firstManager start work");
myService.doWork();
}
}
#Component
public static class SecondManager {
private final List<MyService> myServices;
#Autowired // inject MyService all implementations
public SecondManager(List<MyService> myServices) {
this.myServices = myServices;
}
#PostConstruct
public void startWork() {
System.out.println("secondManager start work");
myServices.forEach(MyService::doWork);
}
}
}
For the second part of your question, take look at this useful answers first / second
You can also make it work by giving it the name of the implementation.
Eg:
#Autowired
MyService firstService;
#Autowired
MyService secondService;
Assume that you have a GreetingService
public interface GreetingService {
void doGreetings();
}
And you have 2 implementations HelloService
#Service
#Slf4j
public class HelloService implements GreetingService{
#Override
public void doGreetings() {
log.info("Hello world!");
}
}
and HiService
#Slf4j
#Service
public class HiService implements GreetingService{
#Override
public void doGreetings() {
log.info("Hi world!");
}
}
Then you have another interface, which is BusinessService to call some business
public interface BusinessService {
void doGreetings();
}
There are some ways to do that
#1. Use #Autowired
#Component
public class BusinessServiceImpl implements BusinessService{
#Autowired
private GreetingService hiService; // Spring automatically maps the name for you, if you don't want to change it.
#Autowired
private GreetingService helloService;
#Override
public void doGreetings() {
hiService.doGreetings();
helloService.doGreetings();
}
}
In case you need to change your implementation bean name, refer to other answers, by setting the name to your bean, for example #Service("myCustomName") and applying #Qualifier("myCustomName")
#2. You can also use constructor injection
#Component
public class BusinessServiceImpl implements BusinessService {
private final GreetingService hiService;
private final GreetingService helloService;
public BusinessServiceImpl(GreetingService hiService, GreetingService helloService) {
this.hiService = hiService;
this.helloService = helloService;
}
#Override
public void doGreetings() {
hiService.doGreetings();
helloService.doGreetings();
}
}
This can be
public BusinessServiceImpl(#Qualifier("hiService") GreetingService hiService, #Qualifier("helloService") GreetingService helloService)
But I am using Spring Boot 2.6.5 and
public BusinessServiceImpl(GreetingService hiService, GreetingService helloService)
is working fine, since Spring automatically get the names for us.
#3. You can also use Map for this
#Component
#RequiredArgsConstructor
public class BusinessServiceImpl implements BusinessService {
private final Map<String, GreetingService> servicesMap; // Spring automatically get the bean name as key
#Override
public void doGreetings() {
servicesMap.get("hiService").doGreetings();
servicesMap.get("helloService").doGreetings();
}
}
List also works fine if you run all the services. But there is a case that you want to get some specific implementation, you need to define a name for it or something like that. My reference is here
For this one, I use #RequiredArgsConstructor from Lombok.
As mentioned in the comments, by using the #Qualifier annotation, you can distinguish different implementations as described in the docs.
For testing, you can use also do the same. For example:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyClassTests {
#Autowired
private MyClass testClass;
#MockBean
#Qualifier("default")
private MyImplementation defaultImpl;
#Test
public void givenMultipleImpl_whenAutowiring_thenReturnDefaultImpl() {
// your test here....
}
}
There are 2 approaches when we have autowiring of an interface with multiple implementations:
Spring #Primary annotation
In short it tells to our Spring application whenever we try to autowire our interface to use that specific implementation which is marked with the #Primary annotation. It is like a default autowiring setting. It can be used only once per cluster of implementations of an interface. → #Primary Docs
Spring #Qualifier annotation
This Spring annotation is giving us more control to select the exact implementation wherever we define a reference to our interface choosing among its options. → #Qualifier Docs
For more details follow the links to their documentation.
public interface SomeInterfaces {
void send(String message);
String getType();
}
kafka-service
#Component
public class SomeInterfacesKafkaImpl implements SomeInterfaces {
private final String type = "kafka";
#Override
public void send(String message) {
System.out.println(message + "through Kafka");
}
#Override
public String getType() {
return this.type;
}
}
redis-service
#Component
public class SomeInterfacesRedisImpl implements SomeInterfaces {
private final String type = "redis";
#Override
public void send(String message) {
System.out.println(message + "through Redis");
}
#Override
public String getType() {
return this.type;
}
}
master
#Component
public class SomeInterfacesMaster {
private final Set<SomeInterfaces> someInterfaces;
public SomeInterfacesMaster(Set<SomeInterfaces> someInterfaces) {
this.someInterfaces = someInterfaces;
}
public void sendMaster(String type){
Optional<SomeInterfaces> service =
someInterfaces
.stream()
.filter(service ->
service.getType().equals(type)
)
.findFirst();
SomeInterfaces someService =
service
.orElseThrow(() -> new RuntimeException("There is not such way for sending messages."));
someService .send(" Hello. It is a letter to ....");
}
}
test
#SpringBootTest
public class MultiImplementation {
}
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeInterfacesMasterTest extends MultiImplementation {
#Autowired
private SomeInterfacesMaster someInterfacesMaster;
#Test
void sendMaster() {
someInterfacesMaster.sendMaster("kafka");
}
}
Thus, according to the Open/Closed principle, we only need to add an implementation without breaking existing code.
#Component
public class SomeInterfacesRabbitImpl implements SomeInterfaces {
private final String type = "rabbit";
#Override
public void send(String message) {
System.out.println(message + "through Rabbit");
}
#Override
public String getType() {
return this.type;
}
}
test-v2
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeInterfacesMasterTestV2 extends MultiImplementation {
#Autowired
private SomeInterfacesMaster someInterfacesMaster;
#Test
void sendMasterV2() {
someInterfacesMaster.sendMaster("rabbit");
}
}
If we have multiple implementations of the same interface, Spring needs to know which one it should be autowired into a class. Here is a simple example of validator for mobile number and email address of Employee:-
Employee Class:
public class Employee {
private String mobileNumber;
private String emailAddress;
...
/** Getters & Setters omitted **/
}
Interface EmployeeValidator:
public interface EmployeeValidator {
public Employee validate(Employee employee);
}
First implementation class for Mobile Number Validator:
#Component(value="EmployeeMobileValidator")
public class EmployeeMobileValidator implements EmployeeValidator {
#Override
public Employee validate(Employee employee) {
//Mobile number Validation logic goes here.
}
}
Second implementation class for Email address Validator:
#Component(value="EmployeeEmailValidator")
public class EmployeeEmailValidator implements EmployeeValidator {
#Override
public Employee validate(Employee employee) {
//Email address validation logic goes here.
}
}
We can now autowired these above validators individually into a class.
Employee Service Interface:
public interface EmployeeService {
public void handleEmployee(Employee employee);
}
Employee Service Implementation Class
#Service
public class EmployeeServiceImpl implements EmployeeService {
/** Autowire validators individually **/
#Autowired
#Qualifier("EmployeeMobileValidator") // Autowired using qualifier for mobile validator
private EmployeeValidator mobileValidator;
#Autowired
#Qualifier("EmployeeEmailValidator") // Autowired using qualifier for email valodator
private EmployeeValidator emailValidator;
#Override
public void handleEmployee(Employee employee) {
/**You can use just one instance if you need**/
employee = mobileValidator.validate(employee);
}
}

Spring MVC instantiate object on request attribute

Assume I am creating a PrinterService class that has a AbstractPrinter object. AbstractPrinter is subclassed by classes such as HPPrinter, FilePrinter etc.
The exact kind of printer object to be used is mentioned in the RequestParam object passed to my Controller (it is a request attribute).
Is there any way I can inject the right kind of concrete printer class using Spring?
All the other dependencies are injected using #Autowired annotation. How to inject this one?
You can create and load a factory of AbstractPrinter objects during container startup as shown below and dynamically call the respective the AbstractPrinter's print() (or your own method) based on the input parameter (comes from controller) to the service.
In the below code for PrinterServiceImpl class, the main point is that all of the List<AbstractPrinter> will be injected by Spring container (depends upon how many implementation classes you provide like HPPrinter, etc..). Then you will load those beans into a Map during container startup with printerType as key.
#Controller
public class YourController {
#Autowired
private PrinterService printerService;
public X myMethod(#RequestParam("input") String input) {
printerService.myServiceMethod(input);
//return X
}
}
PrinterServiceImpl class:
public class PrinterServiceImpl implements PrinterService {
#Autowired
private List<AbstractPrinter> abstractPrinters;
private static final Map<String,AbstractPrinter> myPrinters = new HashMap<>();
#PostConstruct
public void loadPrinters() {
for(AbstractPrinter printer : abstractPrinters) {
myPrinters.put(printer.getPrinterType(), printer);
}
}
//Add your other Autowired dependencies here
#Override
public void myServiceMethod(String input){//get input from controller
AbstractPrinter abstractPrinter= myPrinters.get(input);
abstractPrinter.print();//dynamically calls print() depending on input
}
}
HPPrinter class:
#Component
public class HPPrinter implements AbstractPrinter {
#Override
public String getPrinterType() {
return "HP";
}
#Override
public void print() {
// Your print code
}
}
FilePrinter class:
#Component
public class FilePrinter implements AbstractPrinter {
#Override
public String getPrinterType() {
return "FILE";
}
#Override
public void print() {
// Your print code
}
}
You could create a dedicated PrinterService instance per AbstractPrinter concrete class. For example you could achieve this using Spring configuration which follow the factory pattern:
#Configuration
public class PrinterServiceConfiguration {
#Autowired
private HPPrinter hpPrinter;
#Autowired
private FilePrinter filePrinter;
#Bean
public PrinterService hpPrinterService() {
return new PrinterService(hpPrinter);
}
#Bean
public PrinterService filePrinterService() {
return new PrinterService(filePrinter);
}
public PrinterService findPrinterService(PrinterType type){
if (type == HP)
return hpPrinterService();
....
}
}
Then in your controller, inject PrinterServiceConfiguration then call findPrinterService with the right printer type.
Don't forget to add PrinterServiceConfiguration at your configuration #Import.
If the list of printer is dynamic you could switch to prototype bean :
#Configuration
public class PrinterServiceConfiguration {
#Autowired
private List<AbstractPrinter> printers;
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public PrinterService createPrinterService(PrinterType type){
return new PrinterService(findPrinterByType(type));
}
private Printer findPrinterByType(PrinterType type) {
// iterate over printers then return the printer that match type
// throw exception if no printer found
}
}

how to autowire spring beans from a non-bean object created at runtime?

I have some Jpa repositories and several Entity class. I need to create a helper object for one of my Entity. Inside that helper I use #Autowire to access the Jpa repositories.
#Entity
class A {
#Transient
Helper helper;
...
}
class Helper {
A a;
#Autowired
CRepository repo;
public Helper(A a) {
this.a = a;
}
}
However, the repo is always null. I've tried using SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this) and #Configurable, but both of them failed. Can anybody provide some hint for me?
BTW, A is instantiated inside a rest controller.
Thanks!.
You can use a BeanUtil class to get any bean that created in Springl
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Then you can get the bean.
MyBean obj = BeanUtil.getBean(MyBean.class);
Use constructor injection instead of field injection; this is a best practice all the time anyway. Then it's trivial to inject your A into the controller and pass it as a constructor argument.
#Configurable annotation works fine, but you need to use #EnableSpringConfigured annotation in any configuration class in order to make it work. Read my answer in other stackoverflow post: spring autowiring not working from a non-spring managed class
Entity class should not contain any helpers, even if transient. For a clean design you need to separate concerns, so the entity should not be aware of your business logic. I cannot help you more since I don't know which is the goal of that helper, but here you have other alternatives:
ALTERNATIVE 1 (based on your description seems that helper is an stateful bean, so it is not candidate to be a #Service, which I personally think it should be)
#Controller
public MyController {
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
Helper helper = new Helper(a); // CRepository is successfully autowired
}
}
#Configurable(autowire = Autowire.BY_TYPE)
public class Helper {
A a;
#Autowired
CRepository repo;
}
#Configuration
#EnableSpringConfigured
public Application {
...
}
ALTERNATIVE 2 (make your Helper class stateless so that spring is aware of your beans without the need of extra stuff like #Confgurable/#EnableSpringConfigured)
#Controller
public MyController {
#Autowired Helper helper; // CRepository is correctly autowired
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
helper.doSomething(a);
}
}
#Service
public class Helper {
// A a; remove dependency to A to make it stateless
#Autowired
CRepository repo;
public Helper() {
}
public void doSomething(A a) {
...
repo.save(a);
}
}
You cannot autowire nothing in your Helper class because it isn't managed by Spring.
You can use this approach:
public class HelperManager {
#Autowired
private ApplicationContext context;
public Helper getHelper(A a) {
return context.getBean(Helper.class, a);
}
Configure Helper to be a prototype bean:
#Configuration
public class MyConfiguration {
#Bean
public HelperManager helperManager() {
return new HelperManager();
}
#Bean
#Scope("prototype")
public Helper helper(A a) {
return new Helper(a);
}
}
And finally in your controller:
#Controller
public class MyController {
#Autowired
private HelperManager helperManager;
public someMethodWhereToInstanceYourHelper(A a) {
...
Helper helper = helperManager.getHelper(a);
...
}
}

Autowiring in implementation classes of an interface in Spring

I'm having a Spring boot application in which based on a variable, I need to call corresponding implementation classes of an interface. This is what I have right now:
public interface Parent{
public void call();
}
public class ABC implements Parent{
public void call(){
System.out.println("Called ABC");
}
}
public class XYZ implements Parent{
public void call(){
System.out.println("Called XYZ");
}
}
#Service("caller")
public class Caller{
#Autowired
protected OrderInfoRepository orderInfoRepository;
AnnotationConfigApplicationContext context;
public Caller(){
context = new AnnotationConfigApplicationContext(Config.class);
}
public void callMethod(String param){
Parent p = (Parent) context.getBean(param+"_Caller");
p.call();
}
}
#Configuration
public class Config{
#Bean(name="ABC_Caller")
public Parent getABC(){
return new ABC();
}
#Bean(name="XYZ_Caller")
public Parent getXYZ(){
return new XYZ();
}
}
#Repository
public interface MyRepo extends Repository<MyDAO, Long> {
// ....
}
Basically what I want to do is, based on the param passed to Caller.callMethod(), I want to add "_Caller" to the param, and call the corresponding implementation class. So, I'm defining a #Configuration class, where I define which implementation class to return. And then using the AnnotationConfigApplicationContext, I get the corresponding bean. This works fine.
The problem I'm having is, when I try to Autowire anything in the implementation classes, I get a NoSuchBeanDefinitionException. For instance, when I autowire in the implementation class ABC
public class ABC implements Parent{
#Autowired
MyRepo myRepo;
public void call(){
System.out.println("Called ABC");
}
}
I get Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.persistence.repositories.MyRepo] when I try to start the application. However, when I do the Autowiring in the Caller class instead, it works fine. I had asked a similar question a while back, but was not able to resolve it. Any ideas?

Spring: Injecting property as a value into an annotation

I am trying to inject a value into a Custom Annotation but Spring doesn't seem to be evaluating.
Here is my annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyCustomAnno {
String var1();
String var2();
}
Here is my Bean (it is created in a Spring Configuration file) with Annotation:
public class MyClass {
#MyCustomAnno(var1 = "${some.property.one}",
var2 = "${some.property.two}")
public void someMethod() {
// do something here
}
}
Here is the Aspect where I am trying to use the values passed into the annotation:
#Aspect
public class MyAop {
#Around(value="#annotation(myCustomAnno)",argNames="myCustomAnno")
public Object aroundMethod(MyCustomAnno myCustomAnno) {
int intVar1 = Integer.parseInt(myCustomAnno.var1());
int intVar2 = Integer.parseInt(myCustomAnno.var2());
// ....
}
}
In the around method I am receiving a NumberFormatException: For input string: ${some.property.one}. This means that Spring didn't inject the value for some reason.
In case you are wondering, in the same class I can do the normal #Value annotation and the value gets injected properly:
#Value("${some.property.one}")
private propertyOne; // This works
Is it possible to do what I want to and if so, how?
AFAIK, placeholders are not resolved in custom annotations. However you could resolve them in the Aspect itself.
For example:
#Aspect
class MyAop implements EmbeddedValueResolverAware {
private StringValueResolver resolver;
#Around(value="#annotation(myCustomAnno)",argNames="myCustomAnno")
public void play(MyCustomAnno ann) {
System.out.println(resolver.resolveStringValue(ann.var1()));
}
#Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
}

Resources