Get bean from ApplicationContext by qualifier - spring

Given this code:
public interface Service {}
#Component
#Qualifier("NotWanted")
public class NotWantedService implements Service {}
#Component
#Qualifier("Wanted")
public class WantedService implements Service {}
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(NotWantedService.class);
ctx.register(WantedService.class);
ctx.refresh()
How do I now do:
ctx.getBean(Service.class)
in a way that will only get the one with #Qualifier("Wanted") and not the one with #Qualifier("NotWanted")? I'm specifically asking if it's possible to do it using getBean, not injecting to a class, then using that one as a kind of proxy.

You can use
BeanFactoryAnnotationUtils.qualifiedBeanOfType(ctx.getBeanFactory(), Service.class, "Wanted")
It's important to use ctx.getBeanFactory(), not ctx itself, because the 'qualifiedBeanOfType' method can resolve qualifiers only for ConfigurableListenableBeanFactory.

It's not the #Qualifier annotation's purpose to use it when getting beans via ApplicationContext. But since you need such or similar functionality for some reasons, I suggest a workaround.
Create #Wanted and #NotWanted annotation:
#Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Wanted {
}
and
#Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface NotWanted {
}
Annotate your bean classes with these new annotations:
#Component
#NotWanted
public class NotWantedService implements Service {}
and
#Component
#Wanted
public class WantedService implements Service {}
Then you should add 2 methods somewhere where you have access to the ApplicationContext :
ApplicationContext applicationContext;
private <T> Collection<T> getBeansByTypeAndAnnotation(Class<T> clazz, Class<? extends Annotation> annotationType){
Map<String, T> typedBeans = applicationContext.getBeansOfType(clazz);
Map<String, Object> annotatedBeans = applicationContext.getBeansWithAnnotation(annotationType);
typedBeans.keySet().retainAll(annotatedBeans.keySet());
return typedBeans.values();
}
private <T> Optional<T> getBeanByTypeAndAnnotation(Class<T> clazz, Class<? extends Annotation> annotationType) {
Collection<T> beans = getBeansByTypeAndAnnotation(clazz, annotationType);
return beans.stream().findFirst();
}
And now you can use them to get beans or one bean by annotation and type like this:
Collection<Service> services = getBeansByTypeAndAnnotation(Service.class, Wanted.class);
or
Service service = getBeanByTypeAndAnnotation(Service.class, Wanted.class);
Possibly it's not the best way to deal with the problem. But since we are unable to get beans from ApplicationContext by qualifier and type 'out of box', that's one of the ways to do this.

If you want to get bean from context not injecting, better way to define bean name in #Component annotation and get it by name from context. In most cases #Qualifier is used for injections.

In my case, I have two qualified bean of one class, e.g
#Configuration
public class AConfig {
#Bean(name = "a")
public Hello hello1() {
return new Hello();
}
#Bean(name = "b")
public Hello hello2() {
return new Hello();
}
}
Then I can get a specific bean by
ApplicationContext context = SpringApplication.run(AutoConfiguration.class);
var aHello = context.getBean("a", Hello.class);
var bHello = context.getBean("b", Hello.class);
This is the simplest way of doing this. Or you can do the same thing as below:
var aHello = context.getBeansOfType(Hello.class).getBean("a");
var bHello = context.getBeansOfType(Hello.class).getBean("b");
Or you can also do as #Dmitry Ovchinnikov says:
BeanFactoryAnnotationUtils.qualifiedBeanOfType(ctx.getBeanFactory(), Service.class, "Wanted")
In this case, ctx.getBeanFactory() can be replaced by context.getAutowireCapableBeanFactory().

The closest canonical way to do this in Spring is with the utility class BeanFactoryAnnotationUtils ... but this sadly only works with #Qualifier annotation value argument directly (hence why the argument is a string).
What #Rozart is recommending is the best approach and really something like that should be in BeanFactoryAnnotationUtils. I only included the above answer in the case someone lands here and does want to use #Qualifier directly (and all the bean aliasing that comes along with it).
I recommend filing a feature request in Spring (I would but I think they might be tired of me bugging them :-) ).

Related

Does spring inject any beans provided in the #Configuration class

If I have a #Configuration class where I have a bean like below, will the dataMap be resolved in the constructor of the DataService class. What type of dependency injection is this? Is it by type because the name for sure doesn't match?
#Bean
public Map<String, List<Data>> data() {
final Map<String, List<Data>> dataMap = new HashMap<>();
readings.put("1", new Data());
return dataMap;
}
and a class
#Service
public class DataService {
private final Map<String, List<Data>> information;
public DataService(Map<String, List<Data>> information) {
this.information = information;
}
}
#Configuration annotation serves as a placeholder to mention that whichever classes annotated with #Configuration are holding the bean definitions!
When Spring application comes up, spring framework will read these definitions and create beans (or simply objects) in IOC (Inversion of control) container These would be Spring managed objects/beans !
To answer your question, it should create a bean and this is a setter based injection!
However, your #Bean must be some user defined or business entity class in ideal scenarios!
Few links for you to refer to:
https://www.codingame.com/playgrounds/2096/playing-around-with-spring-bean-configuration
https://www.linkedin.com/pulse/different-types-dependency-injection-spring-kashif-masood/

How to dynamically inject a service using a runtime "qualifier" variable?

I can't find a simple way to inject a component/service given a runtime value.
I started reading # Spring's doc: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-autowired-annotation-qualifiers
but I can't find there how to variabilize the values passed to the #Qualifier annotation.
Let's say I've got a model entity with such interface:
public interface Case {
String getCountryCode();
void setCountryCode(String countryCode);
}
In my client code, I would do something like:
#Inject
DoService does;
(...)
Case myCase = new CaseImpl(); // ...or whatever
myCase.setCountryCode("uk");
does.whateverWith(myCase);
... with my service being:
#Service
public class DoService {
#Inject
// FIXME what kind of #$#& symbol can I use here?
// Seems like SpEL is sadly invalid here :(
#Qualifier("${caze.countryCode}")
private CaseService caseService;
public void whateverWith(Case caze) {
caseService.modify(caze);
}
}
I expect the caseService to be the UKCaseService (see related code below).
public interface CaseService {
void modify(Case caze);
}
#Service
#Qualifier("uk")
public class UKCaseService implements CaseService {
}
#Service
#Qualifier("us")
public class USCaseService implements CaseService {
}
So how do I "fix" all of this in the most simple / elegant / efficient way by using either/all Spring feature(s), so essentially NO .properties, NO XML, only annotations.
However I already suspect something is wrong in my DoService because Spring would need to know the "case" before injecting the caseService... but how to achieve this without the client code knowing about the caseService?!
I can't figure this out...
I already read several issues here on SO, but most of the times either they don't really have the same needs and/or config as I have, or the posted answers aren't enough satisfying to me (look like they're essentially workarounds or (old) usage of (old) Spring features).
How does Spring autowire by name when more than one matching bean is found?
=> only refers to component-like classes
Dynamically defining which bean to autowire in Spring (using qualifiers)
=> really interesting but the most elaborated answer (4 votes) is... almost 3 1/2 years-old?! (July 2013)
Spring 3 - Dynamic Autowiring at runtime based on another object attribute
=> quite similar problem here, but the answer really look like a workaround rather a real design pattern (like factory)? and I don't like implementing all the code into the ServiceImpl as it's done...
Spring #Autowiring, how to use an object factory to choose implementation?
=> 2nd answer seems interestingly but its author does not expand, so altough I know (a bit) about Java Config & stuff, I'm not really sure what he's talking about...
How to inject different services at runtime based on a property with Spring without XML
=> interesting discussion, esp. the answer, but the user has properties set, which I don't have.
Also read this:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-bean-references
=> I can't find expanded examples about the use of "#" in expressions. Does someone know about this?
Edit:
Found other related-to-similar issues, no one got a proper answer:
How to use #Autowired to dynamically inject implementation like a factory pattern
Spring Qualifier and property placeholder
Spring: Using #Qualifier with Property Placeholder
How to do conditional auto-wiring in Spring?
Dynamic injection in Spring
SpEL in #Qualifier refer to same bean
How to use SpEL to inject result of method call in Spring?
Factory Pattern might be a solution?
How to use #Autowired to dynamically inject implementation like a factory pattern
You can obtain your bean from the context by name dynamically using a BeanFactory:
#Service
public class Doer {
#Autowired BeanFactory beans;
public void doSomething(Case case){
CaseService service = beans.getBean(case.getCountryCode(), CaseService.class)
service.doSomething(case);
}
}
A side note. Using something like country code as bean name looks a bit odd. Add at least some prefix or better consider some other design pattern.
If you still like to have bean per country, I would suggest another approach. Introduce a registry service to get a required service by country code:
#Service
public class CaseServices {
private final Map<String, CaseService> servicesByCountryCode = new HashMap<>();
#Autowired
public CaseServices(List<CaseService> services){
for (CaseService service: services){
register(service.getCountryCode(), service);
}
}
public void register(String countryCode, CaseService service) {
this.servicesByCountryCode.put(countryCode, service);
}
public CaseService getCaseService(String countryCode){
return this.servicesByCountryCode.get(countryCode);
}
}
Example usage:
#Service
public class DoService {
#Autowired CaseServices caseServices;
public void doSomethingWith(Case case){
CaseService service = caseServices.getCaseService(case.getCountryCode());
service.modify(case);
}
}
In this case you have to add String getCountryCode() method to your CaseService interface.
public interface CaseService {
void modify(Case case);
String getCountryCode();
}
Alternatively, you can add method CaseService.supports(Case case) to select the service. Or, if you cannot extend the interface, you can call CaseServices.register(String, CaseService) method from some initialiser or a #Configuration class.
UPDATE: Forgot to mention, that Spring already provides a nice Plugin abstraction to reuse boilerplate code for creating PluginRegistry like this.
Example:
public interface CaseService extends Plugin<String>{
void doSomething(Case case);
}
#Service
#Priority(0)
public class SwissCaseService implements CaseService {
void doSomething(Case case){
// Do something with the Swiss case
}
boolean supports(String countryCode){
return countryCode.equals("CH");
}
}
#Service
#Priority(Ordered.LOWEST_PRECEDENCE)
public class DefaultCaseService implements CaseService {
void doSomething(Case case){
// Do something with the case by-default
}
boolean supports(String countryCode){
return true;
}
}
#Service
public class CaseServices {
private final PluginRegistry<CaseService<?>, String> registry;
#Autowired
public Cases(List<CaseService> services){
this.registry = OrderAwarePluginRegistry.create(services);
}
public CaseService getCaseService(String countryCode){
return registry.getPluginFor(countryCode);
}
}
According to this SO answer, using #Qualifier isn't going to help you much: Get bean from ApplicationContext by qualifier
As for an alternative strategy:
if you are spring boot, you could use #ConditonalOnProperty or another Conditional.
a lookup service, as #aux suggests
just name your beans consistently and look them up by name at runtime.
Note that your use case also appears to revolve around the scenario where beans are created on application startup, but the bean chosen needs to be resolved after the applicationContext has finished injecting the beans.

Is #Autowired taking care of the nested autowiring?

I have the following components, in two different files:
#Component
public class Chauffeur {
Car car;
public Chauffeur(Car car){
this.car = car;
}
public void go(){
System.out.println("Chauffeur");
car.drive();
}
}
#Component
public class Car{
public void drive() {
System.out.println("Drive car");
}
}
the following configuration file:
#Configuration
#ComponentScan
public class DriveableConfiguration {
}
and the following test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=DriveableConfiguration.class)
public class DriveableTest {
#Autowired
Chauffeur chauffeur;
#Test
public void chauffeurTest(){
chauffeur.go();
}
}
All the classes above are in the same package and the test is passing.
In the test I annotated chauffer with #Autowired, which should mean that the Spring container looks after the creation of the instance of Chauffeur without the developer needing to explicitly instantiate it.
Now, the constructor for Chauffer needs an instance of Car, so there is no default constructor for that class. Nonetheless the container creates it, injecting the required instance in the constructor.
Is the #Autowired saying to the container to instantiate the element with whatever (Components, Beans) it can provide, included parameters in the constructor? If so, in what case is it needed to use #Autowired to annotate a constructor?
Only if you use Spring 4.3+. In such a case #Autowired on constructor is optional if you have one non default constructor.
You can check the example here.
So as of 4.3, you no longer need to specify an explicit injection annotation in such a single-constructor scenario. This is particularly elegant for classes which otherwise do not carry any container annotations at all, for example when programmatically registered
For versions lower than 4.3 you will an exception will be thrown:
the container will throw an exception looking for a default
constructor, unless you explicitly indicate autowire mode
‘constructor’ in your bean definition setup (e.g. in an XML )

Spring can not register spring hateoas resource assembler

I am using spring hateoas in spring and got the problem is spring could not instance hateoas resource assembler , here is my snippet code:
UserHateoasResourceAssembler.java:
#Service
public class UserHateoasResourceAssembler extends ResourceAssemblerSupport<UserDTO, UserHateoasResource> {
public UserHateoasResourceAssembler() {
super(UserController.class, UserHateoasResource.class);
}
#Override
public UserHateoasResource toResource(UserDTO entity) {
UserHateoasResource resource = createResourceWithId(entity.getId(), entity);
return resource;
}
#Override
protected UserHateoasResource instantiateResource(UserDTO entity) {
return new UserHateoasResource(entity);
}
}
UserController.java:
#RestController
#RequestMapping("/api/")
public class UserController {
#Inject
private UserHateoasResourceAssembler userAssembler ;
....
}
The exception was thrown is "No qualifying bean of type [UserHateoasResourceAssembler] found for dependency. I know this root cause is can not create instance of assembler.
I tried to use #Service or #Component but both does not work. I also tried to use #Autowire instead, but did not work too. I have to fix that by adding #Scope( proxyMode = ScopedProxyMode.TARGET_CLASS). But I wonder if there is any another solution to resolve it instead of using #Scope ?
Thanks.
I found the elegant solution. Due to my application using generated code and it used #EnableAspectJAutoProxy, this annotation default set auto-proxy = false and using JDK proxy, so almost the instance of class that implementing an interface was not allowed. We have to #inject the interface instead. So to inject the implementation class, have 2 options here:
Set #EnableAspectJAutoProxy(proxyTargetClass = true )
Remove this annotation if we does not really need that.

Passing parameters to #Configuration in Spring

I have a requirement of creating a prototype bean that's stateful, i.e. take parameters in constructor.
I tried to use #Configuration to create that bean, but found it doesn't work if I use a parameterized constructor...
Note that the parameters I want to pass are NOT spring beans...they are simple POJOs...so I can't autowire them.
So this is what I want to do -
#Configuration
public class MyClassFactory {
#Bean
public MyClass getMyClass(Pojo1 pojo1, Pojo2 pojo2) {
return new MyClass (pojo1, pojo2);
}
}
#Scope("PROTOTYPE")
public class MyClass {
public MyClass(Pojo1 pojo1, Pojo2 pojo2) {
...
}
#Autowired SomeService1 service1;
#Autowired SomeService1 service2;
...
}
Of course I can make MyClass applicationContextAware, and pick up services from it, rather than making it a prototype bean...but was wondering why above pattern is not allowed...

Resources