jeyjeyemem
Joined 8 years ago
Joined 8 years ago
public class PklResolver implements Resolver {
public Optional<String> resolve(InvocationContext context, String propertyName) {
return getFromPklConfig(propertyName);
}
}
// Register custom resolver when building ExternalizedProperties
ExternalizedProperties externalizedProperties = ExternalizedProperties.builder()
.resolvers(new PklResolver(...))
.build();
AppProperties appProperties = externalizedProperties.initialize(AppProperties.class);
// Resolves config from PklResolver
String myConfig = appProperties.myConfig();See benchmarks here: https://github.com/joel-jeremy/java-config-library-benchmark...
Aside from the performance, the other advantage of using this config library is how it makes testing easier and how easy it is to integrate with your dependency injection frameworks of choice.
The goal of this library is to make it easy for applications to implement configuration best practices by providing easy-to-use APIs as well as providing the flexibility to choose where to store their configurations/properties.
Externalized Properties takes full advantage of Java's Dynamic Proxies.
Why Dynamic Proxies?
* Dependency Injection Friendly
Since Externalized Properties works with interfaces, it makes it easy to integrate with dependency injection (DI) frameworks. it's as simple as building ExternalizedProperties, initializing a dynamic proxy from an interface, and registering the proxy interface to your chosen DI framework.
* Testing Friendly
Another side-effect of being dependency injection friendly is that it also makes it easy to mock/stub out configurations/properties on unit tests. It's as simple as creating a stub implementation of the proxy interface or using mocking frameworks to mock the proxy interface.
The setup is more or less similar, you register a bunch of event handlers when initializing the library. The main difference is that, in addition to events, Emissary also supports "request" messages. Requests enforce the invariant that only one handler should handle it - if there are multiple handlers registered for a given request, Emissary will throw an error. That, and some extension points which is only needed for more advance use cases.
The InstanceProvider allows users to let the dependency injection (DI) framework of their choice instantiate the request/event handler classes (see https://github.com/joel-jeremy/emissary?tab=readme-ov-file#e...). This is in contrast to the way EventBus does it where subscribers need to be instantiated at startup time. By leveraging the DI framework, additional services can be injected to the handler's constructor.
See: https://github.com/joel-jeremy/emissary/blob/main/emissary-c...
Here are the default invocation strategies built-in right now: https://github.com/joel-jeremy/emissary/tree/main/emissary-c...
Emissary aims to take advantage of the simplicity of using the annotations for handlers (e.g. @RequestHandler/@EventHandler) without the drawbacks of reflection (slow).
What differentiates Emissary from other messaging/dispatch libraries? It takes advantage of java.lang.invoke.LambdaMetafactory to avoid the cost of invoking methods reflectively. This results in performance close to directly invoking the request handler and event handler methods.
~ 1000% more throughput compared to other similar libraries (Spring's ApplicationEventPublisher, Pipelinr, EventBus) ~ 90% faster compared to other similar libraries (Spring's ApplicationEventPublisher, Pipelinr, EventBus)
Benchmarks found on the GitHub repository: https://github.com/joel-jeremy/emissary?tab=readme-ov-file#p...
By using ExternalizedProperties instead of direct System.getenv() you get other useful features such as automatic conversions, variable expansion, and processing (e.g. automatic decryption/automatic base64 decode, etc)