How to configure #ConditionalOnProperty with multiple string matching conditions? - spring

I want to load a bean based on two String property value matching conditions.
Egg:
application.properties
foo.test.alpha=sodium
foo.test.beta=chlorine
Bean Config:
#Bean
#ConditionalOnProperty(prefix = "foo.test", name = "alpha", havingValue = "sodium")
public DefaultErrorHandler defaultErrorHandler(final DeadLetterPublishingRecoverer deadLetterPublishRecoverer) {
// do something
}
In the above bean config am able to check only value of foo.test.aplpha=sodium, but how to add foo.test.beta=chlorine matching in the above config ?

Related

Bean is created regardless of #ConditionalOnProperty matchIfMissing's value

I am trying to create a bean for cacheManager only when a specific cachemanager is not configured.
#Bean
#ConditionalOnProperty(name = "spring.cache.type", matchIfMissing = true)
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager() {
#Override
protected Cache createConcurrentMapCache(final String name) {
return new ConcurrentMapCache(name,
CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).build().asMap(), false);
}
};
return cacheManager;
}
This bean is created even when I have the property
spring.cache.type=redis
is configured. Did try different combinations with prefix with no luck. This bean is injected regardless of whether the cache type is specified or not in the application.properties.
The issue seems to be that the default value of having attribute does not work as you expect it to. Reading the reference documentation you will find the following for having:
The string representation of the expected value for the properties. If not specified, the property must not be equal to false.
This means, that in your case (because you are not specifying the value), the condition will always match unless you have spring.cache.type=false. This is also shown in the reference documentation in the following table (the property value "foo" will actually match the condition if havingValue="" which is actually the default if you do not specify it):
Having said all that I would say that your best option would be to create your own Condition just like #ray suggested.
Your issue is quite straightforward. You need to understand what is actually happening here.
As you already knows #ConditionalOnProperty creates a bean when the propert specify in your annotation is available in your yml. In your case it is spring.cache.type.
So if you annotate like this #ConditionalOnProperty(name = "spring.cache.type"), bean will not created when property not is not available. In other words "condition is false because condition is absent".
When you annotate like this #ConditionalOnProperty(name = "spring.cache.type", matchIfMissing = true), bean will created when the property not available because we explicitly mention that by matchIfMissing = true. In other words "assume condition is true when property is absent". So obviously condition is true when the property is available.
So to fix your issue what you can do something like this, define a havingValue that will never put as the value for that property. What happens then, bean will not created even when property is available because value of that property does matches your havingValue.
Something like this,
#ConditionalOnProperty(name = "spring.cache.type", matchIfMissing = true, havingValue = "none")
Method 2
You can create custom Condition class. Like follows,
public class ConditionalOnMisssing implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.getProperty("spring.cache.type") == null;
}
}
Then change your CacheManager bean as follows.
#Bean
#Conditional(ConditionalOnMisssing.class)
public CacheManager cacheManager() {}

Spring: two beans qualified different with the same name

Two beans qualidied different with the same name are getting me an exception.
Exception message:
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'tipusFonsSql', defined in class path resource [net/gencat/clt/arxius/connector/config/SqlGiacTxtResourceLoader.class], could not be registered. A bean with that name has already been defined in class path resource [net/gencat/clt/arxius/connector/config/SqlGiacImgResourceLoader.class] and overriding is disabled.
It's telling me that class SqlGiacTxtResourceLoader and class SqlGiacImgResourceLoader are defining two beans with a same name.
Nevertheless, they are "#Qualified" different. I mean:
Into SqlGiacImgResourceLoader
#Bean
#GiacImg #TipusFonsQ
public String tipusFonsSql() {
//...
}
Into SqlGiacTxtResourceLoader
#Bean
#GiacTxt #TipusFonsQ
public String tipusFonsSql() {
//...
}
As you can see, one is "#aulified" with #GiacImg annotation and the other ony by #GiacTxt.
Any ideas?
You have to name them like this
#Bean(name = "GiacImg TipusFonsQ")
public String tipusFonsSql() {
//...
}
and
#Bean(name = "GiacTxt TipusFonsQ")
public String tipusFonsSql() {
//...
}
to avoid bean conflict
There is 2 way to resolve this issue ( haha there could be many but I know below 2 approach):------
1st Method
change the bean name:---
#Bean(name = "custome bean name")
2nd Method
write down the below key in the application.properties:--
spring.main.allow-bean-definition-overriding=true
NOTE:-in your case you can change method name also

How to check two condition while using #ConditionalOnProperty or #ConditionalOnExpression

I need to check that two conditions are satisfied on a YAML property file, while creating a bean. How do I do that, as the #ConditionalOnProperty annotation supports only one property?
Since from the beginning of #ConditionalOnProperty it was possible to check more than one property. The name / value attribute is an array.
#Configuration
#ConditionalOnProperty({ "property1", "property2" })
protected static class MultiplePropertiesRequiredConfiguration {
#Bean
public String foo() {
return "foo";
}
}
For simple boolean properties with an AND check you don't need a #ConditionalOnExpression.
Use #ConditionalOnExpression annotation and SpEL expression as described here http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html.
Example:
#Controller
#ConditionalOnExpression("${controller.enabled} and ${some.value} > 10")
public class WebController {
You might be interested in the AllNestedConditions abstract class that was introduced in Spring Boot 1.3.0. This allows you to create composite conditions where all conditions you define must apply before any #Bean are initialized by your #Configuration class.
public class ThisPropertyAndThatProperty extends AllNestedConditions {
#ConditionalOnProperty("this.property")
#Bean
public ThisPropertyBean thisProperty() {
}
#ConditionalOnProperty("that.property")
#Bean
public ThatPropertyBean thatProperty() {
}
}
Then you can annotate your #Configuration like this:
#Conditional({ThisPropertyAndThatProperty.class}
#Configuration
#ConditionalOnExpression("#{${path.to.property.one:true} and ${path.to.property.two:true}}")
both true values are the default value if property is not found.
Resolved the issue by using #ConditionalOnExpression for the two properties together.
#ConditionalOnExpression("'${com.property1}${com.property2}'=='value1value2'")
Wherein property value in configuration is as below.
Property 1
Name - com.property1
Value - value1
Property 2
Name - com.property2
Value - value2

What is purpose of #ConditionalOnProperty annotation?

I just modified spring boot configuration, and encountered
#ConditionalOnProperty(prefix = "spring.social.", value = "auto-connection-views")
from org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration
#Bean(name = { "connect/twitterConnect", "connect/twitterConnected" })
#ConditionalOnProperty(prefix = "spring.social.", value = "auto-connection-views")
public View twitterConnectView() {
return new GenericConnectionStatusView("twitter", "Twitter");
}
I don't understand purpose of this annotation. I guess this might be enable to use bean only if property value exist(e.g. "spring.social", "auto-connection-views").
The annotation is used to conditionally create a Spring bean depending on the configuration of a property. In the usage you've shown in the question the bean will only be created if the spring.social.auto-connection-views property exists and it has a value other than false. This means that, for this View bean to be created, you need to set the spring.social.auto-connection-views property and it has to have a value other than false.
You can find numerous other uses of this annotation throughout the Spring Boot code base. Another example is:
#ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true)
public AmqpAdmin amqpAdmin(CachingConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
Note the use of matchIfMissing. In this case the AmqpAdmin bean will be created if the spring.rabbitmq.dynamic property exists and has a value other than false or the property doesn't exist at all. This makes the creation of the bean opt-out rather than the example in the question which is opt-in.
In case you are using this property on TYPE-level, i.e. on one of your #Configuration classes... Keep in mind that in such case the annotation is evaluated/checked against the default properties file, i.e. application.properties
#ConditionalOnProperty on TYPE level w/ #Configuration
Rather, it is the opposite. A precondition for implementing the method, if the property is set in the environment (development, approval, production) and is true value with the method can be executed.
If the property is not set in the environment annotation not prevented the execution of the method.

Overzealous auto-wiring of spring bean properties in grails

I have a domain class:
class Searcher {
String names
List<String> getExperiments() {
return names.split(',');
}
void setExperiments(List<String> list) {
names = list.join(',');
}
}
and a bean defined in the resource file
experiments(com.fxpal.querium.experiment.ExperimentHolder) {
otherProp = 'foo'
}
The experiments bean is semantically different from the experiments property of the Searcher class.
How do I prevent Spring from auto-wiring a specific property of a specific bean? Since the experiments property of the Searcher bean is derived, I don't want Spring to touch it at all.
Why not just name your bean experimentHolder? By default its going to auto wire by name.

Resources