In my project I created the following class annotation:
#CustomController //created by me, it is an alias for spring's #Controller
public class AController{
}
The custom controller being defined like this:
#Target({ElementType.TYPE, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Qualifier
#Controller
public #interface CustomController{
#AliasFor(annotation = Controller.class)
String value() default "";
Now, I'd like to instantiate a bean if at least one #CustomController has been defined.
I couldn't find any #ConditionalOn* annotation that could apply to this specific case, should I create a custom Condition? How to do that?
Spring Boot provides the annotation #ConditionalOnBean that matches if a bean in the application context matches the given requirements.
One of those requirements could be that a bean is annotated with a given annotation:
#ConditionalOnBean(annotation = #CustomController)
Please be careful that this condition may not always work as expected if used on regular beans or configuration classes:
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.
See also the Javadoc of #ConditionalOnBean and Creating Your Own Auto-configuration.
Related
I have Spring boot app and I create my own autoconfiguration that should create bean in case any RestController in present in the context.
It looks something like that:
#AutoConfiguration
#ConditionalOnBean(RestController.class)
public class MyCustomAutoConfiguration {
#Bean
public MyBean myBean(){
// code to create my bean
}
}
I did defined class annotated with #RestController and I it works when I access it .
But my AutoConfiguration doesn't kick in . I get the following:
condition":"OnBeanCondition","message":"#ConditionalOnBean (types: org.springframework.web.bind.annotation.RestController; SearchStrategy: all) did not find any beans of type org.springframework.web.bind.annotation.RestController"}],"matched":[]}
As far as I know Autoconfiguration is done AFTER component scanning detected bean of type restcontroller..so why it didn't pick mine?
#CodnitionalOnBean checks that the bean of provided class is present in the current ApplicationContext. If you annotate any class, say class ABC, with #RestController or whatever annotation you want, the original class of the bean will be ABC (I say original because likely spring will wrap you bean with some Proxy, which is completely another topic), so the condition inside the #CodnitionalOnBean is not satisfied.
If you need to create some configuration beans in case you have a specific controller, then just annotate this configuration class with #CodnitionalOnBean(<YOUR CONTROLLER CLASS NAME, NOT THE ANNOTATION NAME>.class)
Even when we create the Beans in a Spring configuration class, I feel it is still useful to use #Component annotation on the class just to document/indicate that it's a Bean. Is it a good idea? Can there be any other issue that Spring will find the same bean defined in two different ways?
It is bad practice to create two beans from one class...
#Component
class SimpleComponent {
}
#Bean
public SimpleComponent simpleComponentBean(){
new SimpleComponent();
}
By default if we use #Component spring creates bean with name of class, but starts with small letter simpleComponent, if we use #Bean it takes method name and create bean with name simpleComponentBean and we have duplicated beans...
If we have method name the same, as component class name, spring replace one of them and we can Inject unexpected bean.
PS: intellij idea enterprise - correct indicate about all beans.
I started with generating my application using JHipster v.3.5.1.
After some time, I needed to create validator to perform some business logic validation on my entity, when it is created with POST. So I made:
#Component
public class MyValidator implements Validator
Then, I tried to inject it into my controller (annotated with #RestController), but no matter which way I tried, it always resulted in something like that:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.my.app.service.domain.MyValidator] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.
Ways I tried to create bean and inject it
#Autowired
private MyValidator myValidator;
#Inject
private MyValidator myValidator;
#Autowired
#Qualifier("myValidator")
private MyValidator myValidator; (with #Component("myValidator") on class)
#Inject
#Qualifier("myValidator")
private MyValidator myValidator; (with #Component("myValidator") on class)
//Below was inserted in class annotated with #Configuration
#Bean
public MyValidator myValidator() {
return new MyValidator();
}
However I tried it - it failed. I always got NoSuchBeanDefinitionException or field value was set to null.
I've also checked class location in project structure. To be 100% percent sure it's well placed, I've put it in package the with #Services, which are scanned and work well. No effect.
I know that it seems to be pretty easy task and I know this injection is possible (I've seen it done in project in my work), but somehow I'm not able to make it work in my project.
Maybe I'm missing something in configuration? Thanks for any help :)
I believe your issue is that when you use #Autowired inside a class annotated with #Configuration you are just referencing to a bean that is defined in a separate configuration file, that is it has to be declared in another file also with the #Configuration annotation.
If you want to refer refer to another implicit bean such as your validator annotated with #Component you will need to do it in another implicit bean also annotated to with implicit notation such as #Component, #Service, #Controller, etc
The #Autowired alone should work unless you have more than one class implementing the same interface. That is when you will need to use the #Qualifier.
In my project we have set up something like below
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})
#Inherited
#Documented
#Qualifier(“MessageConverterQualifier”)
public #interface MessageConverterRef {}
Above Is used at many places in CoreConfig files (annotation based loading)
#Bean
#MessageConverterRef
public DocumentToABCResponseMessageConverter
documentToABCResponseMessageConverter() {
return new DocumentToABCResponseMessageConverter();
}
#Bean
#MessageConverterRef
public StringToABCResponseMessageConverter stringToABCResponseMessageConverter(
StringToDomBasedMessageConverter stringToDomBasedMessageConverter) {
return new StringToABCResponseMessageConverter(stringToDomBasedMessageConverter);
}
I am not able to understand what is the need of MessageConvertoerRef custom annotation here.
This custom annotation is used at now of places while initializing the beans using #Bean.
Request you to let me know what does it mean and what difference is it making .
This is an elegant solution to ensure compile-time safety for autowiring a set of beans by using the same qualifier. If you look at your custom annotation #MessageConverterRef you will see that the only truly meaningful annotation it is decorated with is:
#Qualifier(“MessageConverterQualifier”))
Use case: you happen to have a set of beans serving the same purpose (like having converters for different types, like you do) it would be really convinient to annotate all of them with the same Spring Qualifier (in your case MessageConverterQualifier), so that they can be all autowired into a list.
The next step is to recognize that having a set of beans scattered across your project that should be annotated with the same qualifier name is not enterily safe, neither the most elegant solution. Defining your own annotation (#MessageConverterRef) once, and reuse it everywhere it is needed reduces the chance of of error (typos) and at the same time increases readability and provides a cleaner code.
For more info on the topic I suggest reading the corresponding Spring doc, especially this part:
Qualifiers also apply to typed collections (as discussed above): e.g. to Set. In such a case, all matching beans according to the declared qualifiers are going to be injected as a collection. This implies that qualifiers do not have to be unique; they rather simply constitute filtering criteria. For example, there could be multiple MovieCatalog beans defined with the same qualifier value "action"; all of which would be injected into a Set annotated with #Qualifier("action").
i have a little trouble in Spring with two component of a service.
I have this component:
#Component
public class SmartCardWrapper
and this one:
#Component
public class DummySmartCardWrapper extends SmartCardWrapper
The service autowire both but spring fails due this expection:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.cinebot.smartcard.SmartCardWrapper] is defined: expected single matching bean but found 2: [dummySmartCardWrapper, smartCardWrapper]
Why it doesn't use class names?
That's one of the most basic concepts of Spring - Inversion of Control.
You don't need to declare your dependencies using their implementation types (to avoid coupling with implementation). You can declare them using interfaces or superclasses instead, and make Spring find the proper implementation class in the context.
In other words, bean are not distinguished by their implementation classes, because you may want to change implementation class of a bean without changing the beans that depend on it. If you want to distinguish between different beans of the same type, use logical bean names instead:
#Autowired #Qualifier("smartCardWrapper")
private SmartCardWrapper smardCardWrapper;
#Autowired #Qualifier("dummySmartCardWrapper")
private SmartCardWrapper dummySmardCardWrapper;