SpringCloudConfig trouble
When I use these annotations #RefreshScope #ConfigurationProperties, I was in trouble.
#Component
#RefreshScope
#ConfigurationProperties(prefix = "config.message")
public class MessageProperties {
private int max_num;
private boolean begin;
private String ding_department;
// getter, setter...
}
like this ! Config does not work;
but when I use only #ConfigurationProperties,it works. So What's the use of #RefreshScope. And how to fix it?
So when you use "I was in trouble" in stackoveflow you are in trouble that there is high probability no one to answer.
#ConfigurationProperties is used for mapping properties to a POJO, with prefix you start using hierarchical properties structure. SO for example based on your description your code will work if you have the following .yml
config:
message:
max_num:
begin:
ding_department:
If for example you use spring-cloud-config server to store the configuration properties and spring-boot and want on change on the file the corresponding Bean with injected conf file to be update you add #RefreshScope, but even if you do this the bean is not updated you have to call the /refresh url or to trigger event which will refresh it.
Related
Suppose a simple Spring Boot #Component like this one:
#Component
#Data
#EnableScheduling
#ConfigurationProperties(prefix = "demo")
public class DemoClass {
private String aString;
private Long aLong;
#Scheduled(fixedDelayString = "${demo.delay}")
void getSomething() {
System.out.println("aString = " + aString);
System.out.println("aLong = " + aLong.toString());
}
}
It will not start throwing
ConfigurationPropertiesBindException: Error creating bean with name 'demoClass': Could not bind properties to 'DemoClass'
All you need to fix is a getSomething method name. Just rename it to putSomething for example.
I've lost three hours debugging Spring Boot sources and found it: Spring tries to bind Bean property named Something. And the exception occurs.
I know it's a weird practice to name methods starting with get if it's not a getter, but is it mentioned somewhere in Spring Docs? Does it say something about guessing properties names from method names?
Yes Spring uses the JavaBeans standard to process the configuration properties POJO, as explained here:
Such arrangement relies on a default empty constructor and getters and setters are usually mandatory, since binding is through standard Java Beans property descriptors, just like in Spring MVC.
I'd like to expose the current value of the configuration property of a spring-bean using spring-actuator https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/#env
GET /actuator/env/{props}
I have 2 services:
Cloud config service
Application service
The cloud config have 2 configurations are maximum-upload-allow = 1M and file-type = png
Application service load those configs from the cloud-config service
like:
#Configuration #Getter
public class ApplicationConfigurationProperty {
#Value("${maximum-upload-allow}")
private String maximumImageSize;
}
#Configuration #RefreshScope #Getter
public class FileConfigurationProperty {
#Value("${file-type}")
private String fileType;
}
I can get my configuration as well via GET /actuator/env/maximum-upload-allow (1M) and GET /actuator/env/file-type (png)
Now when I update configuration value maximum-upload-allow = 2M and file-type = jpg
Then I do refresh-scope by call bus-refresh https://cloud.spring.io/spring-cloud-static/spring-cloud-bus/2.1.4.RELEASE/multi/multi__bus_endpoints.html
I'd like to see my configurations using spring-actuator are:
GET /actuator/env/maximum-upload-allow => 1M (because No refreshscope)
GET /actuator/env/file-type => jpg (I marked as refreshscope)
but actually, spring-actuator return both new values (2M and jpg)
Q: How I can get my runtime value of maximum-upload-allow is 1M (current value because of NO RefreshScope here?
--- Update
#Configuration #Setter #Getter #ConfigurationProperties
public class ApplicationConfigurationProperty {
#Value("${maximum-upload-allow}")
private String maximumImageSize;
}
This configuration value refreshed without #RefreshScope annotation
I think these is correct behaviours mention here https://cloud.spring.io/spring-cloud-static/spring-cloud-bus/2.1.4.RELEASE/multi/multi__bus_endpoints.html
Thank you in advance.
I found a simple solution is to implement a new actuator endpoint which is load #RefreshScope and use Reflection to read bean's properties
I am using Azure KeyVault in Spring Boot to resolve secrets. The vault has a limitation in that properties can only be separated by -. I am using azure-keyvault-secrets-spring-boot-starter and this dependency replaces dashes with dots to be able to store secrets such as spring-datasource-url.
In my project we are using a fairly complex KeyVault and this requires us to prefix properties to know who owns them. So I store my property as prefix-sampleProperty in the vault. The vault starter lets my use this property on two different ways:
#Value("${prefix.sampleProperty}"
#Value("${prefix-sampleProperty}"
However, since my part of the application is only interested in a single namespace within this vault (the prefix namespace), I want to use the Spring annotation #ConfigurationProperties(prefix = "prefix") and simply disregard writing it for each property:
#Value("${sampleProperty}"
However, this does not work and fails with the following error:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'sampleProperty' in value "${sampleProperty}"
I have verified that my environment variables in Spring contain the property, and it exists in two forms, both with . (dot) and - (dash). They exist under propertySource -> source -> propertySources -> propertySourcesList -> KeyVaultPropertySource when Spring attempts to resolve the value from here.
All available property sources that Spring iterates through:
When Spring calls propertySource.getProperty(key);, key is sampleProperty, which does not exist, only prefix-sampleProperty and prefix.sampleProperty do.
This in turn calls this method, and here secretName is also sampleProperty, which does not exist in the map in this class.
So, my questions:
Are there any specific requirements for resolving properties with class level annotation ConfigurationProperties other than it has to be separated by .?
Are there any specific requirements for how to add properties to Springs environment to be able to resolve them with ConfigurationProperties?
Is this a fault in the implementation of the Azure KeyVault property source?
Edit:
#Getter
#Component
// #EnableConfigurationProperties // also tried here, not working
#ConfigurationProperties(prefix = "prefix")
public class SampleConfiguration {
private String sampleProperty;
}
I also added #EnableConfigurationProperties at the same place where I added #SpringBootApplication. This is how I wire the configuration:
#Configuration
// #EnableConfigurationProperties(DataLakeConfiguration.class) // also tried, no difference. also removed autowired
public class AzureBeanConfiguration {
#Autowired
public AzureBeanConfiguration(final SampleConfiguration configuration) {
this.configuration = configuration;
}
#Bean
public ADLStoreClient getDataLakeClient() {
// All properties on configuration object is null here
}
}
If I instead use this, it works:
#Getter
#Configuration
public class SampleConfiguration {
#Value("${prefix.sampleProperty}") // or prefix-sampleProperty
private String sampleProperty;
}
Edit 2:
Config class is annotated with:
#Getter
#Setter
#Configuration
#ConfigurationProperties(prefix = "prefix")
public class SampleConfiguration {
private String sampleProperty;
}
I set a breakpoint here, and when I hit it the parameter name equals prefix. I never receive anything like prefix.sampleProperty or anything containing that key, nothing resembling the name sampleProperty.
Is it possible to call a bean from bootstrap.properties?
I'm trying to implement a Cloud Config Client.
The Bean is similar to:
#Bean
public MyObject myObject(String environment) {
return new MyObject(environment);
}
....
public class MyObject {
private String environment;
// getters setters
}
In the bootstrap.properties file I have the following line:
spring.profiles.active= #Here I should get the value from the bean
Is it possible to write something like:
spring.profiles.active= ${myObject.environment}
Thank you very much.
You can, plugging into spring.factories:
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.demo.MyBootstrapConfiguration
And then write a normal Spring Java Configuration Object
#Configuration
public class MyBootstrapConfiguration {
// normal spring java config
}
Based on the docs, the bootstrap file:
Out of the box it is responsible for loading configuration properties
from the external sources, and also decrypting properties in the local
external configuration files
and the its content is meant to be referenced in the beans, not the other way round.
If you want to pass the active profile somehow to it, based on the docs you can:
1) Use -D option while starting the app:
java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar
2) Set SPRING_PROFILES_ACTIVE property on your OS environment.
I'm new to Spring and I'm building an application where some entities (JPA/Hibernate) need access to a property from application.properties. I do have a configuration class in which this is trivial:
#Configuration
public class FactoryBeanAppConfig {
#Value("${aws.accessKeyId}")
private String awsAccessKeyId;
#Value("${aws.secretKey}")
private String awsSecretKey;
}
but since entities do not have and I think they should not have the annotations such as #Configuration or #Component, what's the Spring way for them to access the property?
Now, I know I can create my own class, my own bean, and make it as a simple wrapper around the properties; but is that the Spring way to do it or is there another way?
specify Property file location using #PropertySource
Something like below
#PropertySource("classpath:/application.proerties")
You also need to add below bean in your config
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
There is no "Spring way", since JPA entities and Spring have nothing to do with each other. Most importantly, JPA entities are not Spring beans, so Spring doesn't know or care about them as they're not managed by Spring.
You can try to hack around, trying in vain to access Spring's configuration from code that should not be trying to access it, or you can accept the truth that your design is broken and you're trying to do something that's not meant to be done.
As was proposed several times, use a service class for this. It's managed by Spring, so it can access the Spring config, and it can handle entities, so there's no crossing boundaries.
First create a public static variable in some bean managed by Spring, then make the following use of the #Value annotation.
public static String variable;
#Value("${variable}")
private void setVariable(String value) {
variable = value;
}
It will be set at runtime on startup, now you can access it from entities and everywhere else because it is just a public static var.
You can use #PropertySource to load the properties file as follows
#Configuration
#PropertySource("classpath:/com/organization/config/application.proerties")
public class FactoryBeanAppConfig {
...
}
Entities should not acces environment properties. If you are using your entity through a service, then the service can access the properties to act on the entity.