I have multiple bean be annotated with #ConditionalOnMissingBean, which will be used? How can i control the priority?
If you want to control #Bean creation ordering, you can use the annotation #Order
#Component
#Order(1)
public class First {
public int first() {
return 1;
}
}
#Component
#Order(2)
public class Second {
public int second() {
return 2;
}
}
Or you can also use #DependsOn
#Configuration
public class ActionCfg {
#Bean
#DependsOn({"actionA","actionB"})
public ActionC actionC(){
return new ActionC();
}
#Bean("ActionA")
public ActionA actionA() {
return new ActionA();
}
#Bean("ActionB")
public ActionB actionB() {
return new ActionB();
}
}
ActionA and ActionB will initialized before ActionC.
The bean whose auto-configuration class is run first will be taken.
There is #AutoConfigureBefore, #AutoConfigureAfter and #AutoConfigureOrder to control the order of auto-configuration classes.
Related
Why boolean matches(...) method in SpecificCaseCondition class is invoked 2 times ? I expect it to be invoked only once, on AnyConfiguration creation. In fact, it's invoked 2 times.
public class SpecificCaseCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}
}
#Configuration
#Conditional(SpecificCaseCondition.class)
public class AnyConfiguration {
#Bean
public Service firstService() {
return new RealService();
}
#Bean
public Service secondService() {
return new RealService();
}
#Bean
public Service thirdService() {
return new RealService();
}
}
When Spring Boot auto-configures the beans for the app context, it does so in multiple phases. By default, condition at the class level* will be evaluated multiple times, for each phase. That's probably why you're seeing your custom Condition method being called more than once; Spring is invoking it during each phase.
One way to avoid that would be to annotate the #Bean methods with #Conditional instead of the entire class. Like this:
#Configuration
public class AnyConfiguration {
#Bean
#Conditional(SpecificCaseCondition.class)
public Service firstService() {
return new RealService();
}
#Bean
#Conditional(SpecificCaseCondition.class)
public Service secondService() {
return new RealService();
}
#Bean
#Conditional(SpecificCaseCondition.class)
public Service thirdService() {
return new RealService();
}
}
In my experiments, method-level conditions are only evaluated during the REGISTER_BEAN phase.
There is a downside to this solution, of course - it isn't very DRY. As an alternative, you can change your condition to implement ConfigurationCondition which has a method, public ConfigurationPhase getConfigurationPhase(), to dictate which phase the condition should be evaluated in.
The various #Conditional* annotations can be placed at the class level or the method level.
I have 2 configuration classes in my spring application.
Configuration and AnotherConfiguration. The AnotherConfiguration is conditioned to create beans only if a certain parameter is provided (this is handled by the ConditionalOnClass annotation).
Configuration.java
#Configuration
public class Configuration {
#Bean
public Stage testStage() {
return someStage1;
}
#Bean
public Stage testStage2() {
return someStage2;
}
}
AnotherConfiguration.java
#Configuration
#ConditionalOnClass()
public class AnotherConfiguration {
#Bean
public Stage testStage2() {
return newStage2;
}
}
The use case is that if I supply an argument that satisfies the Conditional argument for AnotherConfiguration, newStage2 should be returned to all the classes expecting a testStage2 bean. But currently, the testStage2 bean is being resolved from Configuration class instead of being overridden by AnotherConfiguration.
I have tried adding the #Primary annotation to the definition in AnotherConfiguration but that just resolves newStage2 to all the classes expecting bean of type Stage irrespective of the qualifier. Is there a way to instruct spring to override bean definitions only of the same QualifierName (here testStage2.
Due to the project constraints, I cannot make changes to Configuration.java but can make any change to AnotherConfiguration.java keeping the name (testStage2()) same.
I really don't recomend it but
use a conditional instead of an onClass because that will always be true without params
public class Cond implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
and then define the overridden bean to load into the context
#Component("testStage2")
#Conditional(value = Cond.class)
#Primary
public class AnotherStage extends Stage {
public AnotherStage(){
//do whatever
}
}
Sorry bean style
#Configuration
public class AnotherConfiguration {
#Bean("testBean2")
#Conditional(value = Cond.class)
#Primary
public Stage testStage2() {
return newStage2;
}
}
I have following configuration:
#Qualifier1
#Qualifier2
#Bean
public MyBean bean1(){...}
#Qualifier2
#Qualifier3
#Bean
public MyBean bean2(){...}
#Qualifier1
#Qualifier2
#Qualifier3
#Bean
public MyBean bean3(){...}
#Qualifier3
#Bean
public MyBean bean4(){...}
#Qualifier1
#Bean
public MyBean bean5(){...}
And it is the injection place:
#Qualifier2
#Qualifier3
#Autowired:
private List<MyBean> beans;
By default spring uses AND logic for each #Qualifier
So bean2 and bean3 will be injected.
But I want to have OR logic for that stuff so I expect beans bean1 bean2 bean3 and bean4 to be injected
How can I achieve it?
P.S.
#Qualifier annotation is not repeatable so I have to create meta annotation for each annotation:
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Qualifier1 {
}
What if you used marker interfaces instead of qualifiers? For example:
public class MyBean1 extends MyBean implements Marker1 {}
public class MyBean2 extends MyBean implements Marker2 {}
public class MyBean12 extends MyBean implements Marker1, Marker2 {}
Then using this:
#Bean
public MyBean1 myBean1() {
//...
}
#Bean
public MyBean2 myBean2() {
//...
}
#Bean
public MyBean12 myBean12() {
//...
}
and this:
#Autowired private List<Marker1> myBeans;
You would get a list of myBean1 and myBean12 beans.
And for this:
#Autowired private List<Marker2> myBeans;
You would get a list of myBean2 and myBean12 beans.
Will this work?
UPDATE I
Custom FactoryBean
I implemented TagsFactoryBean class and #Tags annotation which you can use to solve your task (I hope :)).
First, mark your beans with #Tags annotation:
#Tags({"greeting", "2letters"})
#Bean
public Supplier<String> hi() {
return () -> "hi";
}
#Tags({"parting", "2letters"})
#Bean
public Supplier<String> by() {
return () -> "by";
}
#Tags("greeting")
#Bean
public Supplier<String> hello() {
return () -> "hello";
}
#Tags("parting")
#Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
#Tags("other")
#Bean
public Supplier<String> other() {
return () -> "other";
}
Then prepare TagsFactoryBean:
#Bean
public TagsFactoryBean words() {
return TagsFactoryBean.<Supplier>builder()
.tags("greeting", "other")
.type(Supplier.class)
.generics(String.class)
.build();
}
Here tags is an array of desired tags whose beans should be selected, type is a selected beans type, and generics is an array of generic types of the beans. The last parameter is optional and should be used only if your beans are generic.
Then you can use it with #Qualifier annotation (otherwise Spring injects all beans of Supplier<String> type):
#Autowired
#Qualifier("words")
private Map<String, Supplier<String>> beans;
The Map beans will contain three beans: hi, hello and other (their name are keys of the Map and their instances are its values).
More usage examples you can find in tests.
UPDATE II
Custom AutowireCandidateResolver
Thanks to #bhosleviraj recommendation, I implemented TaggedAutowireCandidateResolver that simplifies the process of autowiring the desired beans. Just mark your beans and the autowired collection with the same tags and you will get them injected into the collection:
#Autowired
#Tags({"greeting", "other"})
private Map<String, Supplier<String>> greetingOrOther;
#Configuration
static class Beans {
#Tags({"greeting", "2symbols", "even"})
#Bean
public Supplier<String> hi() {
return () -> "hi";
}
#Tags({"parting", "2symbols", "even"})
#Bean
public Supplier<String> by() {
return () -> "by";
}
#Tags({"greeting", "5symbols", "odd"})
#Bean
public Supplier<String> hello() {
return () -> "hello";
}
#Tags({"parting", "7symbols", "odd"})
#Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
#Tags({"other", "5symbols", "odd"})
#Bean
public Supplier<String> other() {
return () -> "other";
}
}
You can use not only the Map for injecting beans but also other Collections.
To make it work you have to register a CustomAutowireConfigurer bean in your application and provide it with TaggedAutowireCandidateResolver:
#Configuration
public class AutowireConfig {
#Bean
public CustomAutowireConfigurer autowireConfigurer(DefaultListableBeanFactory beanFactory) {
CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
beanFactory.setAutowireCandidateResolver(new TaggedAutowireCandidateResolver());
configurer.postProcessBeanFactory(beanFactory);
return configurer;
}
}
More usage examples see in this Test.
Answer requires deep understanding of how autowiring resolution is implemented in Spring, so we can extend it.
I couldn't come up with any solution yet, but I can give you some pointers.
Possible candidate to extend is QualifierAnnotationAutowireCandidateResolver , override method that resolves to a qualified bean. And pass the custom autowire resolver to the bean factory.
You can clone source code and correct version branch from here:
https://github.com/spring-projects/spring-framework
There is a CustomAutowireConfigurerTests in spring-beans module, that might help you understand few things.
I guess you can't do it by using annotation.
What I'd use is the org.springframework.context.ApplicationContextAware Maybe you need to write some extra code but in this way you can solve your issue.
I'd implement a class like this:
#Component
public class SpringContextAware implements ApplicationContextAware {
public static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
}
public static synchronized ApplicationContext getCtx() {
return ctx;
}
}
Then in all beans where you need the OR logic you want you can do something like this:
#Autowired
private SpringContextAware ctxAware;
#PostConstruct
public void init() {
//Here you can do your OR logic
ctxAware.getCtx().getBean("qualifier1") or ctxAware.getCtx().getBean("qualifier2")
}
Will this solve your issue?
Angelo
How can I conditionally create a Bean given a situation where the String is null?
The following example would cause an error, but I would like to somehow prevent it from happening by only creating beans when the string being assessed is not empty.
public class MyAppContext
#Value("${this.string.is.null}")
private String nullString;
#SupressWarnings("SpringJavaAutowiringInspection")
#Bean
public MessageListenerContainer myQueue() {
bean.setDestinationName(nullString)
}
You could use the #ConditionalOnExpression annotation:
#Bean
#ConditionalOnExpression("'${this.string.is.null}'!=null")
public MessageListener myQueue() {
bean.setDestinationName(nullString)
}
Or create a custom condition:
public class MyPropNotNull implements Condition {
public MyPropNotNull() {}
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.getProperty("this.string.is.null") != null;
}
}
#Bean
#Conditional(MyPropNotNull.class)
public MessageListener myQueue() {
bean.setDestinationName(nullString)
}
Update: if having a bean of this type is mandatory, don't forget to add a fallback bean. Example:
#ConditionalOnMissingBean
#Bean
public MessageListener useThisOneWhenTheOtherIsMissing() {
// this bean will be used when the other one is not available
// ...
}
try https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/condition/ConditionalOnProperty.html
#ConditionalOnProperty(value = "propertyName")
#Bean
public MessageListener myQueue() {
bean.setDestinationName(nullString)
}
Given a class:
class MyConfiguration {
#Bean
String bean() {
return new String();
}
}
as you may notice it does not have #Configuration annotation.
How can I make it behave like it has #Configuration annotation, but not adding it?
#Bean annotation should not work in Lite Mode, https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html
Something like:
#Configuration
class MainConfiguration {
#Bean
MyConfiguration myConfiguration() {
MyConfiguration myConfiguration = do_some_spring_magic();
// myConfiguration behaving like it's having #Configuration here
return myConfiguration;
}
}
I dont understand what is the use case you are trying to achieve here.. But if you are looking for programatically registering beans into the Spring Context then you can do it as below.
#Configuration
public class MyBeanRegisterFactory implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanRegistry) throws BeansException {
//depending on some condition you can do the below line
beanRegistry.registerBeanDefinition("myBeanClass", new RootBeanDefinition("com.mybean.MyBeanClass"));
}
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
In Your case, the bean definitions inside the class MyConfiguration can be programatically registered as below into to the spring context.
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/support/BeanDefinitionRegistryPostProcessor.html