#Value with singleton in Java - java

I have a class ResourcesProperties.java
#NoArgsConstructor
#PropertySource("classpath:config.properties")
#Component
public class ResourcesProperties {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConf() {
return new PropertySourcesPlaceholderConfigurer();
}
public static ResourcesProperties instance;
#Bean
public static ResourcesProperties getInstance() {
if (instance == null)
instance = new ResourcesProperties();
return instance;
}
#Getter
#Value("${room.rightStatusRoom}")
private static boolean rightStatusRoom;
#Getter
#Value("${room.countGuests}")
private static int countGuests;
}
There is a "config.properties" property file
room.rightStatusRoom=true
room.countGuests=101
Why, when accessing the of fields with #Value annotation, they don't return the specified values from property file?
Example:
if(ResourcesProperties.getInstance().isRightStatusRoom()) { //returned false instead true
//business-logic
}

My solution:
#NoArgsConstructor
#PropertySource("classpath:config.properties")
#Component
public class ResourcesProperties {
#Getter
#Value("${room.rightStatusRoom}")
private boolean rightStatusRoom;
#Getter
#Value("${room.countGuests}")
private int countGuests;
}

Spring doesn't allow to inject value into a static object.
So you have 2 options, get rid of the static or call a non-static setter that sets the static variable.
More info can be found here: https://mkyong.com/spring/spring-inject-a-value-into-static-variables/
So these are the solutions.
Solution 1:
#NoArgsConstructor
#PropertySource("classpath:config.properties")
#Component
public class ResourcesProperties {
#Getter
#Value("${room.rightStatusRoom}")
private boolean rightStatusRoom;
#Getter
#Value("${room.countGuests}")
private int countGuests;
}
Solution 2:
#NoArgsConstructor
#PropertySource("classpath:config.properties")
#Component
public class ResourcesProperties {
#Getter
private static boolean rightStatusRoom;
#Getter
private static int countGuests;
#Value("${room.rightStatusRoom}")
public void setRightStatusRoom(boolean rightStatusRoom) {
this.rightStatusRoom = rightStatusRoom
}
#Value("${room.countGuests}")
public void setCountGuests(int countGuests) {
this.countGuests = countGuests
}
}

Related

How can configure dynamically from a condition the load of values from application.properties with configuration properties

I am trying of implement a strategy to load from application.properties two values set dinamically depending on condition of income:
# Configuration A
payouts.xxx.by-default.business-name=ABC S.A.S
payouts.xxx.by-default.nit=830109723
payouts.xxx.by-default.account.number=1234567890
payouts.xxx.by-default.account.type=01
payouts.xxx.by-default.account.financial-institute-id=GEROCOBB
payouts.xxx.by-default.BIC=4566
# Configuration B
payouts.xxx.loan.business-name=XYZ S.A.S
payouts.xxx.loan.nit=830109723
payouts.xxx.loan.account.number=1234567890
payouts.xxx.loan.account.type=01
payouts.xxx.loan.account.financial-institute-id=GEROCOBB
payouts.xxx.loan.BIC=344444
So I have 2 class that represented the properties values :
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class AccountConfiguration {
private String businessName;
private String nit;
private String bic;
private Account account;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Account {
private String number;
private String type;
private String financialInstituteId;
}
And also I have other class that represented the configuration class to get the values from application.properties
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Configuration
#ConfigurationProperties(prefix = "payouts.xxx.loan")
public class PayoutAccountConfiguration {
private AccountConfiguration byDefault;
private AccountConfiguration loan;
public AccountConfiguration getConfiguration(String flag) {
if (flag.equals(PayoutOrderStatus.IN_AUTOMATIC)) {
return loan;
}else{
return byDefault;
}
}
}
And for last in the application class I have anotation to enabled the configuration
#SpringBootApplication
#EnableFeignClients
#EnableConfigurationProperties({AccountConfiguration.class, Account.class})
#Import(EncryptedPropertyConfiguration.class)
public class TestApplication {
/**
* The main method to start the application.
* #param args command-line arguments
*/
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
Now I need that in getConfiguration method there are logic to decided depends on income if return the value by default(Configuration A) or the value personalized (Configuration B). I don't know how do these. Can you help me. giving ideas please.

Spring PropertySource() How does it work?

in my resources/application.properties, I have:
classa.keypath=${user.home}/App/jsonfile.json
classa.attribute=TEST
in my ClassAConfig() class, I have
#Component
#PropertySource(value="classpath:application.properties")
#ConfigurationProperties(prefix="classa")
public class ClassAConfig{
public static String keypath;
public static String attribute;
//getters
}
and in my ClassA(), I have:
#Service
#EnableConfigurationProperties(ClassAConfig.class)
#Component
#AutoConfigureAfter(ClassAConfig.class)
public class ClassA{
#Autowired
private ClassAConfig ClassAConfig;
private static String keypath;
private static String attribute;
#Autowired
public ClassA(ClassAConfig ClassAConfig){
this.keypath=ClassAConfig.getKeypath();
this.attribute=ClassAConfig.getAttribute();
System.out.println(keypath);
//prints out null
}
}
Why is it printing null?

How to forbid Spring Boot injecting both fields of the same type with the same bean instance?

I have a class with two final fields of the same type, and I need to make second field injected with null if property props.enabled in application.yml is false. However, if it's false Spring Boot injects both fields with the same bean instance.
How to forbid Spring Boot injecting both fields of the same type with the same bean instance?
#AllArgsConstructor
public class MySettings {
private int val;
}
My configuration class
#Configuration
public class MySpringConfig {
#Bean
public MySettings settingsA() {
return new MySettings(1);
}
#Bean
#ConditionalOnProperty(prefix = "props", name = "enabled")
public MySettings settingsB() {
return new MySettings(2);
}
}
And this is my class
#Component
#RequiredArgsConstructor
public class MyClass {
private final MySettings settingsA; // MySettings(1)
private final MySettings settingsB; // also MySettings(1) but must be null if props.enabled=false
#Value("${props.enabled}")
private boolean enabled;
...
}
This is part of the real project, so I have very little space to deviate from
UPDATE
I came up with solution of constructor injection but the code starts to look ugly
#Component
public class MyClass {
private final MySettings settingsA;
private final MySettings settingsB;
private boolean enabled;
public MyClass(MySettings settingsA,
#Nullable #Qualifier("settingsB") MySettings settingsB,
#Value("${props.enabled}") boolean enabled) {
this.settingsA = settingsA;
this.settingsB = settingsB;
this.enabled = enabled;
}
...
You need a qualifier to tell Spring which bean to inject:
#Configuration
public class MySpringConfig {
#Bean(name = "SettingsA")
public MySettings settingsA() {
return new MySettings(1);
}
#Bean(name = "SettingsB")
#ConditionalOnProperty(prefix = "props", name = "enabled")
public MySettings settingsB() {
return new MySettings(2);
}
}
And now in MyClass:
#Component
public class MyClass {
private final MySettings settingsA;
private final MySettings settingsB;
#Value("${props.enabled}")
private boolean enabled;
public MyClass(#Qualifier("SettingsA") MySettings settingsA, #Qualifier("SettingsB") MySettings settingsB) {
this.settingsA = settingsA;
this.settingsB = settingsB;
}
...
}
However, since one of those Beans might not be available I believe you need to not include it in the constructor injection otherwise you will get an error. In that case you need to do the following:
#Component
public class MyClass {
#Qualifier("SettingsA")
#Autowired
private MySettings settingsA;
#Qualifier("SettingsB")
#Autowired(required = false)
private MySettings settingsB;
#Value("${props.enabled}")
private boolean enabled;
...
}

Spring ConfigurationProperties file - giving a default value

I have a ConfigurationProperties class looks something like this (stripped out to show what i need)
#ConfigurationProperties(prefix = "something")
#Configuration
#Data
public class SomethingProps {
private OtherProps otherProps;
}
#Data
public class OtherProps {
private boolean enabled = true;
}
When I provide this in my application.yml file everything works fine
something:
otherProps: true
But when I don't provide otherProps at all in the yml file then i get a null pointer exception when autowiring OtherProps in a constructor. My expectation is that it would default to true, i have tried annotating with #NotNull
Initialize OtherProps.
#ConfigurationProperties(prefix = "something")
#Configuration
#Data
public class SomethingProps {
private OtherProps otherProps=new OtherProps();
}
#Data
public class OtherProps {
private boolean enabled = true;
}
I would suggest you import your config class instead of creating an object.
For eg.
#ConfigurationProperties(prefix = "something")
#Configuration
#Import({OtherProps.class})
#Data
public class SomethingProps {
}
and your OtherProps class will be like this,
#Data
public class OtherProps {
private boolean enabled = true;
}
try the following
#Configuration
#EnableConfigurationProperties(value = { OtherProps.class })
#Data
public class SomethingProps {
}
and then
#Data
#ConfigurationProperties(prefix = "something")
public class OtherProps {
private boolean enabled = true;
}
actually you can use #Value annotation as alternative
#Value("x.y.z:default_value")
private boolean isEnabled;
I usually use the following approach:
#ConfigurationProperties
public class SomethingProperties {
private OtherProps otherProps = new OtherProps();
// getters/setters/lombok generated whatever
public static class OtherProps {
private boolean enabled = true;
// getters/setters/lombok generated whatever
}
}
#Configuration
#EnableConfigurationProperties(SomethingProps.class)
public class MyConfiguration {
#Bean
public SampleBean sampleBean(SomethingProps config) {
return new SampleBean(config.getOtherProps().isEnabled());
}
}

Extending class with changing field requirements

Let's have example base class
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
#JsonProperty("login")
private String login;
#JsonProperty("password")
private String password;
#JsonProperty("additionalData")
private String additionalData;
}
and second one that extends User class
#Data
#NoArgsConstructor
#AllArgsConstructor
public class EnhancedUser extends User {
#NotNull
#JsonProperty("additionalData")
private String additionalData;
}
I but It doesn't work because when I create instance of EnhancedUser class field additionalData can be null.
Any idea?
Look:
public class Sample {
public static void main(String[] args) {
EnhancedUser enhancedUser = new EnhancedUser();
enhancedUser.setAdditionalData("TAMU");
enhancedUser.setLogin("ANY");
enhancedUser.setPassword("ANY");
System.out.println(enhancedUser);
System.out.println(enhancedUser.getAdditionalData());
}
#Data
#NoArgsConstructor
#AllArgsConstructor
public static class User {
private String login;
private String password;
private String additionalData;
}
#EqualsAndHashCode(callSuper = true)
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString(callSuper = true)
public static class EnhancedUser extends User {
#NotNull
private String additionalData;
}
}
And the result of printlnis
Sample.EnhancedUser(super=Sample.User(login=ANY, password=ANY, additionalData=TAMU), additionalData=TAMU)
TAMU
You do realize that you actually have 2 fields "additionalData"? Since you can't override fields but merely hide them. And this is a huge nono anti-pattern in general.
Either you rename your field or you think of a more approriate implementation, like implementing this logic yourself with and a constructor argument and a call of the additionalData setter from your constructor.

Categories