Configurable components
The library provides the Configurable interface that can be implemented by
components that require configuration after instantiation.
The interface defines two methods with default implementations,
configure, that receives a Config object for programmatic
configuration, and isConfigured, that can return true if the
component has been configured to prevent re-configuration.
class MyComponent implements Configurable {
private String host;
private int port;
private boolean configured = false;
@Override
public void configure(Config config) {
this.host = config.get("host", "localhost");
this.port = config.getInteger("port", 8080);
this.configured = true;
}
@Override
public boolean isConfigured() {
return this.configured;
}
}
The class Configurer is the responsible for applying a Config to
the Configurable components, and is capable of configuring automatically
properties annotated with @ConfigurableProperty.
Nested Configurable beans are not configured by default.
Example:
class MyComponent implements Configurable {
@ConfigurableProperty("host")
private String host;
@ConfigurableProperty("port")
private int port;
// Does not require override configure() method
}
Note: The configurable bean must have accesible setters for the annotated properties, or the properties must be public.
Customization of components configuration
Components can customize the configuration process with the annotations
@PreferredConfig and @ConfigurationOptions, that can be applied to the
class.
Preferred configurations
The PreferredConfig annotation can be used to specify one or more
preferred Config subtypes for the component.
When the Configurer configures the component, it will try to find
a Config instance of the preferred types in the order specified,
and use the first one found to configure the component.
If none of the preferred configurations is found, the default
configuration provided to the Configurer will be used, unless
the fallbackToDefaultConfig option is set to false in the
annotation.
This allows libraries to provide specific Config subtypes for their
configurable components, while still allowing the application to
configure them using the default configuration.
The application that uses the library can use it's default configuration
or provide the library specific configuration to configure the
library components.
Components can convert to the specific Config subtype using the
Config.as(Class<T>) method in the configure method.
Example:
interface MySuperConfig extends Config {}
interface MyConfig extends MySuperConfig {
final String HOST_PROP = "host";
final String PORT_PROP = "port";
default String getHost() {
return get(HOST_PROP, "localhost");
}
default int getPort() {
return getInteger(PORT_PROP, 8080);
}
}
@PreferredConfig({ MyConfig.class, MySuperConfig.class })
class MyComponent implements Configurable {
private String host;
private int port;
@Override
public void configure(Config config) {
MyConfig myConfig = config.as(MyConfig.class);
this.host = myConfig.getHost();
this.port = myConfig.getPort();
}
}
This is useful for external libraries components, that can
be configured using the library custom Config subtypes,
providing specific configuration for the library,
or fallback to application's default configuration.
Configuration options
The ConfigurationOptions annotation allows to customize the configuration
process for the component.
By default, the Configurer will configure properties annotated
with @ConfigurableProperty.
As properties are only configured if annotated with
@ConfigurableProperty, setting configureProperties to false can be
used to skip the properties configuration step, for example in components
extending other configurable components that already configure their
properties.
Example:
class BaseComponent implements Configurable {
...
@ConfigurableProperty("host")
private String host;
...
}
@ConfigurationOptions(configureProperties = false)
class MyExtendedComponent extends BaseComponent {
...
@Override
public void configure(Config config) {
...
setHost(...);
...
}
...
}
Nested Configurable beans are not configured by default, as independent
configuration is expected for them.
This can be overriden setting configureNestedBeans to true, to enable
automatic configuration of nested beans.
Example:
class NestedComponent implements Configurable {
...
}
@ConfigurationOptions(configureNestedBeans = true)
class MyComponent implements Configurable {
...
private NestedComponent nestedComponent = new NestedComponent();
...
}
Note: The configurable bean must have accesible getters for the nested configurable beans, or the property must be public.
Configurer programmatic usage
The Configurer can be created programmatically using the static
Configurer.fromProvider method, that expectes a ConfigProvider
with a mandatory default configuration and optional alternative configurations:
Config config = ...;
AltConfig altConfig = ...;
Configurer configurer = Configurer.fromProvider(
ConfigProvider.builder(config)
.addConfig(altConfig)
.build());
Configurable component = ...;
configurer.configure(component);
Spring integration
Usage of configurable beans in Spring Framework is fully supported. See the Spring integration document for details.
