Spring bind different properties files to different beans - spring

It's my MySQL properties file
mysql.properties
dialect=org.hibernate.dialect.MySQL57Dialect
hbm2ddl_method=validate
show_sql=true
format_sql=false
pool_name=testpool
jdbc_url=jdbc:mysql://localhost:3306/testdb
minimum_idle=2
uname=root
password=testpsw
cache_prep_stmts=true
prep_stmt_cache_size=256
prep_stmt_cache_sql_limit=2048
use_server_prep_stmts=true
maximum_pool_size=30
driver_class_name=com.mysql.jdbc.Driver
and Oracle properties file for datasource configuration.
dialect=org.hibernate.dialect.Oracle10gDialect
hbm2ddl_method=validate
show_sql=true
format_sql=false
pool_name=testpool
jdbc_url=jdbc:oracle:thin:#localhost:1521:testdb
minimum_idle=2
uname=barn_act
password=testpsw
cache_prep_stmts=true
prep_stmt_cache_size=256
prep_stmt_cache_sql_limit=2048
use_server_prep_stmts=true
maximum_pool_size=30
driver_class_name=oracle.jdbc.OracleDriver
I created two classes like this to bind properties into fields.
#Component("mysql_props")
#PropertySource(value = "classpath:/mysql.properties")
#ConfigurationProperties
#Getter
#Setter
public class HibernateMySQLProperties {
private String dialect;
//other props
}
#Component("oracle_props")
#PropertySource(value = "classpath:/oracle.properties")
#ConfigurationProperties
#Getter
#Setter
public class HibernateOracleProperties {
//same fileds as mysql
}
When I inject these two beans to PersistenceConfiguration class same propety fields are injected.
#Configuration
#EnableConfigurationProperties({ HibernateOracleProperties.class, HibernateMySQLProperties.class })
public class PersistenceConfig {
#Autowired
#Qualifier("oracle_props")
private HibernateOracleProperties oracleProps;
#Autowired
#Qualifier("mysql_props")
private HibernateMySQLProperties mysqlProps;
}
How to solve this problem ?

This is a known problem/behavior in spring prior to spring-boot. In spring property placeholder can understand unique key. Your both properties file has same name of keys.
So the solution would be like below with fewer changes.
Change the property file like below.
mysql.properties
mysql.dialect=org.hibernate.dialect.MySQL57Dialect
****** all otheres same start with mysql.
Oracle properties file
oracle.dialect=org.hibernate.dialect.Oracle10gDialect
****** all otheres same start with oracle.
Now change your Hibernate*Properties.java #ConfigurationProperties annotation.
#Component("oracle_props")
#PropertySource(value = "classpath:/oracle.properties")
#ConfigurationProperties(prefix = "oracle")
#Component("mysql_props")
#PropertySource(value = "classpath:/mysql.properties")
#ConfigurationProperties(prefix = "mysql")
No need of any change in PersistenceConfig.java file.

Related

Why setters are mandatory for fields in a class which reads properties from application.yml file in springboot?

Following is my code:
Why setter is mandatory. Without it, the class does not
read the property from the
application.yml file
correctly.
Thank you.
#Getter
#Setter
#NoArgsConstructor
#Configuration
#ConfigurationProperties(prefix = "test")
#EnableConfigurationProperties
public class KafkaTopicConfig {
private String bootstrapAddress;
#Value(value = "${test.bootstrapAddress}")
private String bootstrapAddressFromVariable;
should only use #Value in encapsulated components/services (we can call them configuration services).
This way, we will have all our configurations in one place, and that component will only have the responsibility of loading and providing them to other components.
https://stackabuse.com/the-value-annotation-in-spring
From baeldung.com... The Spring framework uses standard Java bean setters, so we must declare setters for each of the properties.
So it looks like you're using Lombok so I would make my class look more like this:
#ConfigurationProperties(prefix = "test")
#Data
public class KafkaTopicConfig {
private String bootstrapAddress;
}
Then in the main spring boot application class or a #Configuration class I would do:
#Configuration
#EnableConfigurationProperties({KafkaTopicConfig.class})
public MyApplicationConfig{
}
Then to use my configuration properties I would autowire it into the #Component where I wished to use it.
e.g.
#Component
public MyComponent{
private final KafkaTopicConfig config;
public MyComponent(KafkaTopicConfig config) {
this.config = config;
}
public void doStuff() {
if ("some address".equals(config.getBootstrapAddress())) {
blah();
}
}
}
Using the #Value inside the configuration properties feels confusing to me, and defeats the point of using configuration properties in the first place.

(De-)Serializing Spring Boot ConfigurationProperties with Jackson

My spring Boot application uses a class annotated with #Configuration and #ConfigurationProperties:
#Configuration
#ConfigurationProperties(prefix = "my")
#Getter
#Setter
public class MyConfigurationProperties {
#Value("${timeout}")
private int defaultTimeout;
}
Now I'd like to read and update these configuration properties using a (private) REST interface, so I created the following controller.
#RestController
#RequestMapping(path = "config")
public class ConfigController {
final MyConfigurationProperties myConfig;
public ConfigController(MyConfigurationProperties myConfig) {
this.myConfig = myConfig;
}
#GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<MyConfigurationProperties> get() {
return ResponseEntity.ok(myConfig);
}
}
Unfortunately this doesn't seem to work as I get the following exception when calling the endpoint:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.context.expression.StandardBeanExpressionResolver and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
The JSON response even contains "strange" properties when disabling FAIL_ON_EMPTY_BEANS:
How would I be able to (de-)serialize this configuration class using Jackson?
When removing the annotations #Configuration and #ConfigurationProperties for testing purposes, the serialization works just fine. So it seems to have to do something with the class being a bean?
You are mixing things: configuration properties and autowiring a value. If you want to have a POJO as a configuration property class, drop the #Value annotation. You can also drop #Configuration annotation if you add #EnableConfigurationProperties to your main class (this way all #ConfigurationProperties classes will be automatically detected). If a problem persists, please update the question and show us how the properties prefixed with 'my' look like.

Spring ConditionalOnProperty cannot read property in custom.properties file

Hi I have some custom properties files like application.properties.And I put some property in this files.Myproblem is #ConditionalOnProperty cannot read myproperties in customproperties file.
If I put same property in application.properties ConditionalOnProperty works but when I put myproperty in customproperties file that time it is not working.Except ConditionalOnProperty I dont have any problem to read values from custom properties file.Is there any way to read external properties from ConditionalOnProperty in spring boot.
#Congiuration
#PropertySource("classpath:myproperties.properties")
#ConfigurationProperties(prefix="mycustomprop")
public class MyProperties{
private String myproperty;
#Configuration
#ConditionalOnProperty(name = "mycustomprop.myproperty", havingValue = "false") //not work when myproperty in customproperty file
public class MyConfiguration implements WebMvcConfigurer
#SpringBootApplication
#PropertySources({
#PropertySource("classpath:myproperties.properties")
})
public class MyApplication
Your #PropertySource annotation needs to go on a #Configuration class (or #SpringBootApplication if you want), not your custom MyProperties object.

spring boot 2: load application.properties manually

is it possible to access application.properties values while creating datasource in SpringApplication.run?
I have tried to inject value with #Value but when i need the values, the bean with #ConfigurationProperties is not created yet.
if you are referring to data source url,username and password you can use these keys :
spring.datasource.url=your_database_url_and_port_number
spring.datasource.username=your_database_username
spring.datasource.password=your_username_passsword
You need to add #EnableConfigurationProperties annotation mentioning the properties POJO name.
#SpringBootApplication
#EnableConfigurationProperties(ConfigProperties.class)
Then in the ConfigProperties class you need to add the below annotation.Prefix is what you will set in the properties file.
#ConfigurationProperties(prefix = "mail")
public class ConfigProperties {
private String hostName;
private int port;
private String from;
// standard getters and setters
}
Values in properties file.
#Simple properties
mail.hostname=host#mail.com
mail.port=9000
mail.from=mailer#mail.com

SpringBoot difference between #Configuration and #configurationProperties

From spring boot documentation, #ConfigurationProperties will
generate your own configuration metadata file from items annotated
with #ConfigurationProperties
I tried use #Configuration and #ConfigurationProperties separately on my configuration class.
#Component
//#Configuration
#ConfigurationProperties
#EnableSpringDataWebSupport
#EnableAsync
public class AppConfig {
...
}
I didn't see any noticable difference.
What's the usage of #ConfigurationProperties or #Configuration?
#Configuration is used to create a class the creates new beans (by annotating its methods with #Bean):
#Configuration
public class CustomConfiguration {
#Bean
public SomeClass someClass() {
return new SomeClass();
}
}
#ConfigurationProperties binds external configuration into the fields of the class which it annotates. It's common to use it with a #Bean method to create a new bean that encapsulates configuration which can be controlled externally.
Here's a real world example of how we've used it. Consider a simple POJO that holds some values related to connecting to ZooKeeper:
public class ZookeeperProperties
{
private String connectUrl;
private int sessionTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(5);
private int connectTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(15);
private int retryMillis = (int) TimeUnit.SECONDS.toMillis(5);
private int maxRetries = Integer.MAX_VALUE;
// getters and setters for the private fields
}
Now we can create a bean of type ZookeeperProperties and automatically populate it using external configuration:
#Configuration
public class ZooKeeperConfiguration {
#ConfigurationProperties(prefix = "zookeeper")
#Bean
public ZookeeperProperties zookeeperProperties() {
// Now the object we create below will have its fields populated
// with any external config that starts with "zookeeper" and
// whose suffix matches a field name in the class.
//
// For example, we can set zookeeper.retryMillis=10000 in our
// config files, environment, etc. to set the corresponding field
return new ZookeeperProperties();
}
}
The benefit of this is that it's less verbose than adding #Value to every field of ZookeeperProperties. Instead, you provide a single annotation on the #Bean method and Spring automatically binds any external configuration it finds with the matching prefix to the fields of that class.
It also lets different users of my class (i.e. anyone who creates a bean type of ZookeeperProperties) use their own prefix to configure the class.
The use case of ConfigurationProperties is for externalizing configuration.
#Configuration
Indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.
#ConfigrationProperties
-- Is added to a class definition or a #Bean method in a #Configuration class if you want to bind and validate some external Properties (e.g. from a .properties file).
See the screenshot to differentiate #Value from #ConfigurationProperties.
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties

Resources