ConfigurationProperties in nested classes - spring

With following yml
app:
a:
prop: aaa
b:
prop: bbb
#Component
public abstract class Common {
#Value("${prop}")
private String prop;
#ConfigurationProperties(prefix = "app.a")
#PropertySource("classpath:app.yml")
#Component
public static class A extends Common {
}
#ConfigurationProperties(prefix = "app.b")
#PropertySource("classpath:app.yml")
#Component
public static class B extends Common {
}
}
But those two classes has same value, either for a or b.
How can I solve this?

I found the problem. Simply. yml doesn't work with PropertySource.
I'm still want to believe I'm wrong.
I changed the .yml file to properties and tried with this.
#PropertySource("classpath:/vendor.properties")
#EnableConfigurationProperties
public abstract class Common {
#Value("${prop}")
private String prop;
#ConfigurationProperties(prefix = "app.a")
#Component
public static class A extends Common {
}
#ConfigurationProperties(prefix = "app.b")
#Component
public static class B extends Common {
}
}
And it worked.

You could use a list for your configuration parameters :
app:
props:
- key: a
value: aaa
- key: b
value: bbb
And retreive your value with a more complex way in a separate bean :
#ConfigurationProperties(prefix = "app")
public class CommonConfiguration {
List<Prop> props;
//Getters and setters
public Prop retreiveSpecificConfiguration(String className) {
//some kind of logic here
}
public static class Prop {
private String key, value;
//Getters and setters
}
}
Inject it in your Common class implementation :
#Autowired
CommonConfiguration config;

Related

Spring Boot #ConfigurationProperties

so I'm kinda new to Springboot and I'm trying to get the value from application.properties. I want to get multiple value from the application.properties and insert it into a list. At first, I tried to get the value from controller class and it works. Now I tried to get the value from a new class, but the value won't show up and it's showing an error because it says that it's null. Am i missing an annotation or did i do something wrong in the code? Below is my code.
application.properties:
example.name[0] = asdf
example.name[1] = qwer
List Value class:
#ConfigurationProperties(prefix = "example")
#Configuration
public class NameProperties {
private List<String> name;
public List<String> getName() {
return name;
}
public void setName(List<String> name) {
this.name = name;
}
}
What i tried in controller and worked:
#RestController
#CrossOrigin
#RequestMapping("/tes/**")
public class NameController {
#Autowired
NameProperties property = new NameProperties();
#GetMapping
public String tes() {
String name = property.getName().get(0);
System.out.println(name);
return name;
}
}
In the new class that doesn't work:
#Component
public class NameConfiguration {
#Autowired
NameProperties property = new NameProperties();
public void getName(int index) {
System.out.println(property.getName().get(0));
}
}
The code to test the new class in the controller:
#RestController
#CrossOrigin
#RequestMapping("/tes/**")
public class NameController {
NameConfiguration conf = new NameConfiguration();
#GetMapping
public String tes() {
conf.getName(0);
}
}
Is it because the value doesn't get injected when I call the class or what should I do? Appreciate any kind of help. Thanks!
Hello friend when you declare your class as a Spring Bean you shouldn't initialize the object yourself other the properties define in it will not be injected by Spring, so you should let spring help you with that, try these class below
NameProperties
#Component
#ConfigurationProperties(prefix = "example")
public class NameProperties {
private List<String> name;
public List<String> getName() {
return name;
}
public void setName(List<String> name) {
this.name = name;
}
}
NameConfiguration.java
#Component
public class NameConfiguration {
#Autowired
NameProperties property;
public void getName(int index) {
System.out.println(property.getName().get(0));
}
}

Spring filter beans based on annotation ignoring property values

Here is a simple example:
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Type {
public String name();
}
#Configuration
public class Configuration {
#Bean
#Type(name = "type1")
public String getType1() {...}
#Bean
#Type(name = "type2")
public String getType2() {...}
...
#Bean
#Type(name = "typeN")
public String getTypeN() {...}
}
public class Test {
#Autowired
#Type // Here I'm asked to specify a name, but I want to get all the beans with #Type annotation regardless of the name property.
private List<String> types;
}
As mentioned in the comment, I want to find all the beans with annotation #Type regardless of the name property. How can achieve it?
A not good workaround to achieve it:
#Retention(RetentionPolicy.RUNTIME)
#Qualifier(value = "Type") // --> add this line
public #interface Type {
public String name();
}
and autowire it like this:
#Component
public class Test {
#Autowired
#Qualifier(value = "Type")
private List<String> types;
public void printAll(){
System.out.println(types);
}
}

Spring: How to inject a Supplier<String> function as constructor parameter

I've coded this class:
#Component
public class AuditFactory {
private Supplier<String> auditIdSupplier;
public AuditFactory(Supplier<String> auditIdSupplier) {
this.auditIdSupplier = auditIdSupplier;
}
}
It's used as a dependency of a #Service class:
#Service
public class AuditService {
private AuditFactory auditFactory;
public AuditService(AuditFactory auditFactory) {
this.auditFactory = auditFactory;
}
}
How could I tell to Spring that injects a Supplier<String> when AuditFactory is injected?
EDIT
#Bean
public Supplier<String> auditIdSupplier(FrontOfficeProperties frontOfficeProperties) {
return () -> String.join(
"-",
frontOfficeProperties.getCpdId(),
frontOfficeProperties.getRedisAuditKeyPrefix(),
UUID.randomUUID().toString()
);
}
where FrontOfficeProperties is an #ConfigurationProperties annotated class.
below approach might help you to fix your issue.
also can you please share Supplier class as well.
#Component
public class AuditFactory {
private Supplier<String> auditIdSupplier;
public AuditFactory(Supplier<String> auditIdSupplier) {
this.auditIdSupplier = auditIdSupplier;
}
}
#Service
public class AuditService {
private AuditFactory auditFactory;
public AuditService(AuditFactory auditFactory) {
this.auditFactory = auditFactory;
}
}

Why are spring beans validated even if the condition says it should not be loaded into the context?

Given the example below, I would expect MyConfig.getSrvConfig() would not be called and therefore no validation would be executed on the returned object neither.
But for some reason the validation is executed and the test case fails. Is there anything wrong in this setup?
I know the test would pass if I have private MySrvConfigBean srvConfig not initialized at declaration - but I really don't want MySrvConfigBean to be a standalone class with a #ConfigurationProperties(prefix = "cfg.srvConfig") annotation.
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = { TestCaseConfiguration.class })
public class ConditionalConfigValidationTest {
#Autowired
private ApplicationContext applicationContext;
#Test
public void test() {
assertNotNull(applicationContext);
assertFalse("srvConfig must NOT be in context", applicationContext.containsBean("srvConfig"));
}
#Configuration
#EnableConfigurationProperties(value = { MyConfig.class })
public static class TestCaseConfiguration {
}
#Component
#Validated
#ConfigurationProperties(prefix = "cfg")
public static class MyConfig {
private MySrvConfigBean srvConfig = new MySrvConfigBean();
#Bean
#Valid
#Conditional(MyCondition.class)
public MySrvConfigBean getSrvConfig() {
return srvConfig;
}
public static class MySrvConfigBean {
#NotNull
private String name;
public String getName() {
return name;
}
}
}
public static class MyCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
}
The reason we would like to have it this way is, because we then are able to structure configuration in code the same way as we have it in the YAML file, e.g.: (cfg and cfgA are the "root" objects for two different configuration hierarchies).
cfg:
srvConfig:
name: Dude
clientConfig:
xxx: true
yyy: Muster
cfgA:
aaaConfig:
bbb: false
ccc: Dundy
dddConfig:
fff: 3
It feels like the execution of the validation (triggered by #Valid on getSrvConfig()) is a bug in this case.
Apparently this is not supported and should be solved in a different way:
#Configuration
#Conditional(MyCondition.class)
#EnableConfigurationProperties(value = { MyConfig.class })
public static class TestCaseConfiguration {
}

Spring Inject Collection From Superclass

I have the following scenario:
class Super{
private List<String> someStringsThatWillBeDifferentForEveryInstancePerDerivedType;
}
#Component
class Derived1 extends Super{
#Autowired
private String name;
}
#Component
class Derived2 extends Super{
#Autowired
private Long configId;
}
I have a different List defined as Spring bean in xml for each derived class…call them listForDerived1 and listForDerived2. How can I wire these lists into my derived classes? I attempted constructor injection but I can't seem to find any luck injecting both the collection and the other deps.
You can use constructor injection with #Qualifier.
class Super {
private List<String> someStrings;
public Super(private List<String> someStrings) {
this.someStrings = someStrings;
}
}
#Component
class Derived1 extends Super {
#Autowired
public Derived1(#Qualifier("listForDerived1") List<String> listForDerived1, OtherBean bean) {
super(listForDerived1);
}
}
#Component
class Derived2 extends Super {
#Autowired
public Derived1(#Qualifier("listForDerived2") List<String> listForDerived1, OtherBean bean) {
super(listForDerived2);
}
}
Also see official Spring doc: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-autowired-annotation-qualifiers

Resources