Spring ConfigurationProperties file - giving a default value - java

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());
}
}

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.

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;
...
}

Multiple configuration tree to one class in spring boot

i have this in Application.yaml:
override:
email:
enabled: true
value: "test#mycompany.com"
phone:
enabled: true
value: "+420666666666"
How do I make a single class configuration with these values?
I tried this:
public class RecipientOverrideConfig {
#Configuration
#ConfigurationProperties("override.email")
#Data
public class EmailOverride{
Boolean enabled;
String value;
}
#Configuration
#ConfigurationProperties("override.phone")
#Data
public class SmsOverride{
Boolean enabled;
String value;
}
}
But is there a better way to do this?
I suggest making the whole class a ConfigurationProperties
#ConfigurationProperties("override")
public class RecipientOverrideProperties {
private OverrideConfig email;
private OverrideConfig phone;
public class OverrideConfig {
private Boolean enabled;
private String value;
}
// getters and setters were omitted for brevity
}
And then autowire that into your configuration:
#Configuration
public class RecipientOverrideConfig {
#Autowired // or even better, use constructor injection
private RecipientOverrideProperties overrideProperties;
}

#Value with singleton in 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
}
}

Spring creates two of #Configuration beans starting up

I have following class:
#Configuration
public class EndpointStatus {
private static final Logger serverLogger = LogManager.getLogger(EndpointStatus.class);
private Long id;
private volatile Status status;
#OneToOne
private volatile CurrentJob currentJob;
public enum Status {
AVAILABLE,
BUSY
}
#Bean
#Primary
public EndpointStatus getEndpointStatus() {
serverLogger.info("STATUS CREATED");
return new EndpointStatus();
}
public EndpointStatus() {
}
public CurrentJob getCurrentJob() {
return currentJob;
}
public void setCurrentJob(CurrentJob currentJob) {
this.currentJob = currentJob;
}
public Status getStatus() {
return status;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setStatus(Status status) {
this.status = status;
}
public boolean isBusy() {
return getStatus() == Status.BUSY;
}
Bean is used in endpoint which is annotated with #Component
and then i try to get the bean in endpoint like
ApplicationContext ctx = new AnnotationConfigApplicationContext(EndpointStatus.class);
EndpointStatus sc = ctx.getBean(EndpointStatus.class);
EndpointStatus is not used anywhere else.
To my knowledge, there should be no reason to create a second bean...
However at startup I always get
INFO 6169 [main] c.e.k.d.r.m.i.EndpointStatus : STATUS CREATED
INFO 6169 [main] c.e.k.d.r.m.i.EndpointStatus : STATUS CREATED
What am I doing wrong here?
EDIT:
Have tried every answer given to no avail whatsoever..
my class now looks like this
#Configuration
public class EndpointStatusConfig {
private static final Logger serverLogger = LogManager.getLogger(JavaXRest.class);
private Long id;
private volatile Status status = EndpointStatusConfig.Status.AVAILABLE;
#OneToOne
private volatile CurrentJob currentJob;
public enum Status {
AVAILABLE,
BUSY
}
#Bean
#Primary
public EndpointStatusConfig getEndpointStatus() {
serverLogger.info("STATUS CREATED");
return new EndpointStatusConfig();
}
public CurrentJob getCurrentJob() {
return currentJob;
}
public void setCurrentJob(CurrentJob currentJob) {
this.currentJob = currentJob;
}
public Status getStatus() {
return status;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setStatus(Status status) {
this.status = status;
}
public boolean isBusy() {
return getStatus() == Status.BUSY;
}
}
no matter #Component or #Configuration, making call to sc in endpoint will result in hundreds of beans created crashing the app...
EDIT2:
this is just getting worse and worse...
now even call to
if ( sc.isBusy() ) { return Response.ok( sc.getCurrentJob() ).type(MediaType.APPLICATION_JSON).build(); }
will jump to #Bean and create as many EndpointStatus objects as it can before the application crashes.... #Component creates one at startup, then thousands. #Configuration will create 2 at startup and then thousands also...
Just a guesswork but defining the configuration class both as a configuration and a factory bean return type is probably the issue.
EndpointStatus is a configuration class as the class is declared with #Configuration and a configuration class produces a bean in Spring and it is also an explicit bean as you annotated the bean factory method getEndpointStatus() with #Bean.
It is a little like if you had defined twice the bean.
Simply change the name of your Configuration class from EndpointStatus to EndpointStatusConfig and this will then only create a single bean with EndpointStatus class.
As you are annotating EndpointStatus both as #Configuration & #Bean it creates 2 Beans.
I think your problem is using #Configuration instead of #Component
The #Configuration will try add to spring context any #Autowired or #Bean inside the class with #Configuration but won't add it self to the spring context.
If you want to add that class as a bean to the spring context you should use #Component
Edit:
Did you try to inject EndpointStatus class with the #Configuration on it self?
In case you don't and don't know what is injection in spring try this:
#Autowired
EndpointStatus status;
void yourMethod(){
//Change this
//ApplicationContext ctx = new AnnotationConfigApplicationContext(EndpointStatus.class);
//EndpointStatus sc = ctx.getBean(EndpointStatus.class);
//Use instead the status variable declared before
}

Categories