-
Notifications
You must be signed in to change notification settings - Fork 16
Bean properties
ConfigMe allows you to use properties of a custom JavaBean class type. This is useful when you have more complicated structures that belong together as a whole, which you'd like to map onto one class.
- Introduction
- Error handling
- Using beans within beans
- Collections
- Maps
- Saving and loading
- Real-life example
Let's look at an example. Given the following JavaBean class:
public class Country {
private String name;
private int population;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
}
The following yml file:
country:
name: 'Denmark'
population: 5614000
neighbor:
name: 'Sweden'
population: 9593000
and a SettingsHolder
class:
public class Countries implements SettingsHolder {
public static final Property<Country> COUNTRY =
newBeanProperty(Country.class, "country", new Country());
public static final Property<Country> NEIGHBOR =
newBeanProperty(Country.class, "neighbor", new Country());
}
If we set up a SettingsManager with the above classes and yml file, we'll get the following result:
// Denmark
String countryName = settingsManager.getProperty(Countries.COUNTRY).getName();
// Country object for Sweden
Country neighbor = settingsManager.getProperty(Countries.NEIGHBOR);
int neighborPop = neighbor.getPopulation(); // 9593000
JavaBeans are simple Java classes with getters and setters. As you can see in the Country
example, each property is
private and has a getter and setter method with standard naming conventions. ConfigMe picks up on properties which have
both a getter and a setter and will look in the YML file for those properties to build a bean object.
As with all other properties, bean properties never resolve to null
, i.e. when you do
settingsManager.getProperty(property)
it is guaranteed to never return null
. If the contents in the YML file cannot
be used to create a bean (e.g. because a field is missing), the default value of the property is used.
You can define a default value in your bean by assigning it directly to a field. For example, if the name of a country should be optional, we could assign it to an empty string at the beginning:
public class Country {
private String name = "";
private int population;
// getters and setters
}
Now if your YML has something like:
country:
population: 1234
The Countries.COUNTRY property will resolve to a Country object that has an empty name and its population set to 1234. We could also omit the population: since the field is a primitive it will automatically be assigned a value of 0 on initialization.
ConfigMe will never return a bean in which a field is null
. If it does not succeed to set a field's value, the mapping is considered to have failed and the default object is used. It is therefore important to initialize fields as shown with the country name above if you don't want the absence of one field to make ConfigMe ignore all fields and use the default bean object provided on initialization.
The JavaBean classes can use other JavaBean classes, recursively.
As an example, if we want to add the country's president to our configuration, we can create a President
class:
public class President {
private String name;
private int sinceYear;
// getters and setters
}
We can integrate this new class into the Country
class:
public class Country {
private String name;
private int population;
private President leader;
// getters and setters
}
Update your YML:
country:
name: 'Latvia'
population: 1957000
leader:
name: 'Raimonds Vējonis'
sinceYear: 2015
This gives you the opportunity to look up the country's president in your code:
Country country = settingsManager.getProperty(Countries.COUNTRY);
System.out.println(country.getLeader().getName()); // Raimonds Vējonis
You can use collections (such as List and Set) in your beans. For example, we can change the Country
class to contain
a list of neighbors:
public class Country {
private String name;
private int population;
private List<String> neighbors;
// getters and setters for all three properties
}
Given the YML:
country:
name: Denmark
population: 5614000
neighbors:
- Sweden
- Norway
- Germany
This will create a Country
object with getNeighbors()
containing a list of "Sweden", "Norway", "Germany". Again,
if this property should be optional, you can set private List<String> neighbors = new ArrayList<>();
in the bean class.
- The type argument of collections must always have a specific type on the fields of JavaBean classes, i.e. you cannot
have a field
List<?> neighbors
orList neighbors
. It must have a specific type, such asList<Country> neighbors
. - Fields with a type more specific than
List
orSet
are not supported. For instance,Iterable<String> neighbors
,Collection<String>
,Set<String>
are fine, but specific types such asTreeSet<String>
are not. - You can declare collections of other bean types, e.g. you can have a
List<Country>
field. If an entry in the YML cannot be mapped to aCountry
, the entire entry is skipped.
You can also use Map
properties in your beans. Only maps with String
keys are supported, e.g. Map<String, Integer>
or Map<String, Country>
are fine, but something like Map<Integer, Country>
is not supported. Maps are useful when you want to support arbitrary keys in a section, e.g.:
country:
name: Denmark
population: 123
neighbors:
sweden:
name: Sweden
norway:
name: Norway
Under neighbors
you want to support any key that is defined. This is typically the most user-friendly way when you
want to have a dynamic section of a bean type. For the above YML, your JavaBean has to be as follows:
public class Country {
private String name;
private int population;
private Map<String, Country> neighbors = new HashMap<>();
// getters and setters for all three properties
}
Country country = settingsManager.getProperty(Countries.COUNTRY);
country.getNeighbors(); // Returns map with "sweden" and "norway"
If certain keys have a special meaning in your configuration, you should favor individual properties in your class rather than loading everything into a map. Maps are convenient when you want to allow arbitrary keys which you don't know beforehand, as in this example.
You may observe some non-intuitive behavior when working with bean properties.
The value you retrieve from SettingsManager#getProperty(BeanProperty)
will always be the same object as long as you
don't reload. However, if you change the bean property, it is recommended to explicitly call
SettingsManager#setProperty
to make your intention clear and to future-proof it.
As with other properties, remember that the settings manager might provide the default value to you! In such cases, you probably don't want to change the actual default value but make a copy of it first.
CommandSettingsHolder, which declares a bean property of type CommandConfig
Navigation
« Migration service | Bean properties | Custom property types » |
Guide
- Introduction
- Getting started
- Migration service
- Bean properties
- Custom property types
- Technical documentation
Updating
Development (internal)