Application configuration backed by ConfigSlurper, plus support for Spring Framework placeholders, and @Value annotations.
Configure using actual typed values, not just Strings.
day = 'Monday' // String, duh!
count = 123 // a real int, no quotes!
vowels = [ 'a', 'e','i', 'o', 'u'] // a real List
now = new Date() // any Object!
// should be able to configure any type Groovy can muster.
Grouped and organized!
// instead of
myService.foo = 1
myService.bar = 2
// do this
myService {
foo = 1
bar = 2
}
Either by direct setting on the Config object, or automatically by Spring's Environment bean.
target = 'http://default/'
environments {
dev {
target = 'http://localhost/'
}
qa {
target = 'http://qa/'
}
}
By setting the locations to load on the Config object. Latter locations will overrides values set by earlier locations.
Locations are Spring's resource strings such as classpath:x/y/z
, or file:/x/y/z
.
With an additional type class:fully.qualified.Classname
which loads a compiled config groovy class.
Config config = new Config();
config.setLocations(
"class:conf.MyConfig",
"file:/usr/local/etc/my-config.groovy"
);
Just like Spring's PropertySourcesPlaceholderConfigurer,
it resolves ${...}
placeholders within bean definition property values and @Value
annotations against the current Spring Environment, and the Config bean.
public class MyBean {
@Value("${day}")
String day;
@Value("${count}")
int count;
@Value("#{config.get("vowels")}")
List<String> vowels;
@Value("#{config.get('now')}")
Date now;
}
repositories {
jcenter()
}
dependencies {
implementation("com.ctzen.config:slurper-configuration:<version>")
}
// setup
Config config = new Config();
config.setProfiles('dev'); // optional
config.setLocations('class:conf/MyConfig');
config.load();
@Configuration
public class AppConfig {
@Bean
public static Config config() {
Config config = new Config();
// no need to setProfiles(), typically set by Environment,
// perhaps via the -Dspring.profiles.active parameter.
config.setLocations(
"class:conf/MyConfig",
"file:/usr/local/etc/my-local-config.groovy"
);
return config;
}
@Bean
public static ConfigPlaceholderConfigurer configPlaceholderConfigurer(Config config) {
return new ConfigPlaceholderConfigurer(config);
}
}
package conf
who = 'McGann'
life = 42
lorem {
ipsum = """
Lorem ipsum dolor sit amet, nec primis argumentum an, nec integre eruditi laoreet eu. Eam illum nulla id, mea ea sonet alterum.
You can inject other var here like, the answer is ${life}.
"""
}
nemesis = [
'Sherlock Holmes': 'James Moriarty',
'Peter Pan': 'Captain Hook',
'John McClane': 'Hans Gruber'
]
// get(key) will throw NoSuchKeyException if key is absent
String doctor = config.get("who");
int answer = config.get("life");
String gibbish = config.get("lorem.ipsum");
Map<String,String> archenimies = config.get("nemesis");
// get(key, default) will return the default if key is absent
int i = config.get("no.such.key", 7); // i == 7
Spring @Value Annotation
public class MyBean {
// ${} may be used simple types
@Value("${who}")
String doctor;
@Value("${life}")
int answer;
// #{} SpEL can be used for all types
@Value("#{config.get('nemesis')}")
Map<String,String> archenimies
}
./gradlew clean build
Config location ends with .properties
can be used as config sources.
Profiles are handled by file naming, for example:
config.setProfiles('dev');
config.setLocations('classpath:conf/my-config.properties');
The following properties files (if exists) will be loaded in order:
1. classpath:conf/my-config.properties
2. classpath:conf/[email protected]
Essentially, the base config filename is appended with @<profile name>
for each profile.
.properties files function the same as the other location types (such as overridding), but only String values are supported (duh).
Say you wish to use this in developing a library (jar), and you have some default configuration values, e.g. in your com.acme.AcmeConfig class, instead of asking your user to add that location, you can set it in META-INF/slurper-configuration.properties
of your jar (ironic, I know). Here is an example:
# locations is comma separated and they are PREPENDED, i.e. loaded first.
locations=class:com.acme.AcmeConfig