Reputation: 369
I'm working with properties in Spring and I have a doubt about the Properties
Valores.properties
estudiante.nombre=Antonio, Juan , Maria, Raquel
estudiante.edad=28,20,21,23
Now I have a class to develop the bean
public class Estudiante {
public Estudiante() {
}
public Estudiante(String nombre, Integer edad) {
super();
this.nombre = nombre;
this.edad = edad;
}
@Value("${estudiante.nombre}")
private String nombre;
@Value("${estudiante.edad}")
private Integer edad;
public Integer getEdad() {
return edad;
}
public void setEdad(Integer edad) {
this.edad = edad;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
@Override
public String toString() {
return '\n' +"Estudiante{" + "nombre=" + nombre + ", edad=" + edad + '}';
}
}
A java class to make the configuration
@Configuration
@PropertySource(value="classpath:valores.properties")
public class AppConfig {
@Value("#{'${estudiante.nombre}'.split(',')}")
private List<String> nombres;
@Value("#{'${estudiante.edad}'.split(',')}")
private List<Integer> edades;
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
public List<String> getNombres() {
return nombres;
}
public List<Integer> getEdades() {
return edades;
}
public List<Estudiante> getListaStudents() {
List<Estudiante> listaStudents = new ArrayList<>();
for (int i= 0;i< nombres.size();i++){
listaStudents.add(new Estudiante(nombres.get(i),edades.get(i)));
}
return listaStudents;
}
}
And the main java class
public class Principal {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig appConfig = context.getBean(AppConfig.class);
System.out.println(appConfig.getListaStudents());
((ConfigurableApplicationContext)context).close();
}
}
The program works and the output is OK
[
Estudiante{nombre=Antonio, edad=28},
Estudiante{nombre= Juan , edad=20},
Estudiante{nombre= Maria, edad=21},
Estudiante{nombre= Raquel, edad=23}]
But I don't know if it is the right way to develop. I dón't like to build a method getListaStudents() in the AppConfig class to make a list of objets and I don't like to use the new() method in Spring
I think it's not a good idea but I don't know other way to solve. Any solution or any idea?
Thanks in advance
Upvotes: 5
Views: 10511
Reputation: 369
Uooohh.. It's a good solution. Thanks for your time Bunti (and thanks to the revisions of other users)
I have develop a solution with your advices, buy I have a problem to catch and split the values of the properties File.
The new class (with the advices) is here
@Configuration
@PropertySource(value="classpath:valores.properties")
public class AppConfig {
@Resource(name = "getStudents")
private List<Estudiante> estudiantes;
public List<Estudiante> getEstudiantes() {
return estudiantes;
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public ConversionService conversionService() {
return new DefaultConversionService();
}
@Bean
public List<Estudiante> getStudents(
@Value("${estudiante.nombre}")List<String> nombres,
@Value("${estudiante.edad}") List<Integer> numbers
) {
List<Estudiante> listaStudents = new ArrayList<>();
for (int i= 0;i< nombres.size();i++){
listaStudents.add(new Estudiante(nombres.get(i),numbers.get(i)));
//listaStudents.add(new Estudiante(nombres.get(i)));
}
return listaStudents;
}
}
Running, I have several exception but I think It is the same problem
With only catch the String values of the Property Files (in this example the values from name) the system write
[Estudiante{nombre=Antonio, Juan , Maria, Raquel, edad=null}]
To allow this output, I have commented all the references to Integer Values (so, the age/edad is null... no problem)
Really, it doesn`t split the properties File and the string values is "a,b,c,d..." (and the size is 1)
So, when I use the Properties File
estudiante.nombre=Antonio, Juan , Maria, Raquel
estudiante.edad=28,20,21,23
and catch String and Integer there is an exception
Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'appConfig':
Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'getStudents' defined in test.AppConfig:
Unsatisfied dependency expressed through constructor argument with index 1 of type [java.util.List]: :
Failed to convert value of type 'java.lang.String' to required type 'java.util.List'; nested exception is java.lang.NumberFormatException:
For input string: "28,20,21,23"; nested exception is org.springframework.beans.TypeMismatchException:
Failed to convert value of type 'java.lang.String' to required type 'java.util.List';
nested exception is java.lang.NumberFormatException: For input string: "28,20,21,23"
The exception is logical. I expect a number to parse... but i catch "28,20,21,23"... and not convert into a number.
So, the converts method understands a list of values separated by , ?
Upvotes: 0
Reputation: 1760
I think this is a proper approach with few improvements, but depending on the data size and requirements you may not want to load data from property files. Property files are usually used for configuration options such as database configurations for different environments, caching configurations etc..
In your AppConfig, you don't need to use SpringEL to parse a List
of Integer
s or String
s like this,
@Value("#{'${estudiante.nombre}'.split(',')}")
private List<String> nombres;
I would suggest to use something like this instead,
@Value("${estudiante.edad}") List<Integer> nombres
but for this to work you need to have one addtional bean configuration for Spring's ConversionService
@Bean
public ConversionService conversionService() {
return new DefaultConversionService();
}
This way Spring's conversion service will have default converters to convert a list of Strings or Integers so that you can avoid somewhat less readable #{'${estudiante.edad}'.split(',')}
in @Value
annotation.
Now you can use above @Value
to be used directly in a new bean to create a set of Students like this.
@Bean
public List<Estudiante> getStudents(
@Value("${estudiante.edad}") List<Integer> numbers,
@Value("${estudiante.nombre}") List<String> names) {
List<Estudiante> listaStudents = new ArrayList<Estudiante>();
for (int i= 0;i< numbers.size();i++){
listaStudents.add(new Estudiante(names.get(i), numbers.get(i)));
}
return listaStudents;
}
and you can inject list of Estudiante
s with @Resource
,
@Resource(name = "getStudents")
private List<Estudiante> estudiantes;
I don't see anything wrong with creating Estudiante
objects with new
keyword. Just as we use new
in other methods that are annotated with @Bean
this is a perfectly valid scenario. If you want, for whatever reason to avoid new
in creating Estudiante
, you can inject ApplicationContext and get Estudiante
with Estudiante studiante = applicationContext.getBean(Estudiante.class);
and don' forget to mark Estudiante
class with @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
I dón't like to build a method getListaStudents() in the AppConfig class to make a list of objects
As far as I know there is no simple and proper way to create a List
of Estudiante
objects as they need to have their own values. You can make student creation to its own configuration class such as EstudianteConfiguration
and import it to your main AppConfig
class with @Import annotation.
Upvotes: 2