Reputation: 2476
This is my project structure
- src
- main
- java
- mypackage
- resources
- config
application.yml
and i have this in application.yml
document:
templates:
filetypes:
- elem1
- elem2
- elem3
- elem4
hello:
test: "hello"
in my Endpoint i have the following
@Value("${document.templates.filetypes}")
List<String> templatesFileTypes;
@Value("${document.hello.test}")
String hello;
in any function i can access something like System.out.println(hello)
and its perfectly working
but for the fileTypes its not even compiling and i receive this error :
Error creating bean with name 'configurationEndPoint': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'document.templates.filetypes' in value "${document.templates.filetypes}"
Searched alot and every solution i could find was that pointing to the write application.yml/application.xml file, which is invalid in my case since i can read the other test String but not the array;
i tried String[]
i tried ArrayList<String>
but i can't get it to work
Upvotes: 13
Views: 26318
Reputation: 4805
In continuation to Jose Martinez answer above you can format the yaml as below if you have a long list of values to increase the readability see the example below.
document:
templates:
filetypes: |-
elem1,
elem2,
elem3,
elem4
@Value("${document.templates.filetypes}")
private String[] array;
@Value("#{'${document.templates.filetypes}'.split(',')}")
private List<String> list;
@PostConstruct
void testList(){
list.stream().forEach(System.out::println);
for (String a : array) {
System.out.println(a);
}
}
Upvotes: 0
Reputation: 47
I think this is also a valid way:
@Service
public class FileTypeService {
private final List<String> fileTypes;
public FileTypeService(Environment environment) {
this.fileTypes = Binder.get(environment).bind("document.templates.filetypes", Bindable.listOf(String.class)).get();
}
}
Bindable.setOf
can be used if a Set is needed instead of List.
Upvotes: 1
Reputation: 2476
The other solution provided by @Jose Martinez would work but not really as clear as needed, because its reading document.templates.filetypes
as a String and then splitting it into array of Strings; thus i am adding my solution to this,
1- Create new class FileTypesProperties
@Configuration
@ConfigurationProperties(prefix = "document.templates")
public class FileTypesConfig {
private List<String> fileTypes;
public List<String> getFileTypes() {
return fileTypes;
}
public void setFileTypes(List<String> fileTypes) {
this.fileTypes = fileTypes;
}
}
2- Create service and inject the previous class
@Service
public class FileTypeService {
private final List<String> fileTypes;
@Autowired
public FileTypeService(FileTypesConfig fileTypesConfig){
this.fileTypes = fileTypesConfig.getFileTypes();
}
public List<String> getFileTypes(){
return this.fileTypes;
}
}
3- In your end point simply autowire and call the previous service
@RestController
public class ConfigurationEndPoint {
@Autowired
FileTypeService fileTypeService;
@GetMapping("/api/filetypes")
@ResponseBody
public ResponseEntity<List<String>> getDocumentTemplatesFileTypes(){
return ResponseEntity.ok(fileTypeService.getFileTypes());
}
}
And then your yaml file can be a real array
document:
templates:
file-types:
- elem1
- elem2
- elem3
- elem4
I believe this is cleaner than splitting a String into smaller strings into an array, hope this will help out someone.
Upvotes: 11
Reputation: 11992
One way is to pass the elements as delimited list. Typically we have used comma and it works out of the box for String arrays. To use a List, then you will need to set the delimiter using Spring SPEL format... see example below.
document:
templates:
filetypes:elem1,elem2,elem3
-
@Value("${document.templates.filetypes:}")
private String[] array;
@Value("#{'${document.templates.filetypes}'.split(',')}")
private List<String> list;
@PostConstruct
void testList(){
list.stream().forEach(System.out::println);
for (String a : array) {
System.out.println(a);
}
}
Upvotes: 15