in archaius2-core/src/main/java/com/netflix/archaius/ConfigProxyFactory.java [225:283]
<T> T newProxy(final Class<T> type, final String initialPrefix, boolean immutable) {
Configuration annot = type.getAnnotation(Configuration.class);
final String prefix = derivePrefix(annot, initialPrefix);
warnWhenTooMany(PROXIES_COUNT, new InterfaceAndPrefix(type, prefix), excessiveProxyLimit, () -> String.format("Proxy(%s, %s)", type, prefix));
// There's a circular dependency between these maps and the proxy object. They must be created first because the
// proxy's invocation handler needs to keep a reference to them, but the proxy must be created before they get
// filled because we may need to call methods on the interface in order to fill the maps :-|
final Map<Method, PropertyValueGetter<?>> invokers = new HashMap<>();
final Map<Method, String> propertyNames = new HashMap<>();
final InvocationHandler handler = new ConfigProxyInvocationHandler<>(type, prefix, invokers, propertyNames);
final T proxyObject = (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[] { type }, handler);
List<RuntimeException> proxyingExceptions = new LinkedList<>();
// Iterate through all declared methods of the class looking for setter methods.
// Each setter will be mapped to a Property<T> for the property name:
// prefix + lowerCamelCaseDerivedPropertyName
for (Method method : type.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
try {
MethodInvokerHolder methodInvokerHolder = buildInvokerForMethod(type, prefix, method, proxyObject, immutable);
propertyNames.put(method, methodInvokerHolder.propertyName);
if (immutable) {
// Cache the current value of the property and always return that.
// Note that this will fail for parameterized properties and for primitive-valued methods
// with no value set!
Object value = methodInvokerHolder.invoker.invoke(new Object[]{});
invokers.put(method, (args) -> value);
} else {
invokers.put(method, methodInvokerHolder.invoker);
}
} catch (RuntimeException e) {
// Capture the exception and continue processing the other methods. We'll throw them all at the end.
proxyingExceptions.add(e);
}
}
if (!proxyingExceptions.isEmpty()) {
String errors = proxyingExceptions.stream()
.map(Throwable::getMessage)
.collect(Collectors.joining("\n\t"));
RuntimeException exception = new RuntimeException(
"Failed to create a configuration proxy for class " + type.getName()
+ ":\n\t" + errors, proxyingExceptions.get(0));
proxyingExceptions.subList(1, proxyingExceptions.size()).forEach(exception::addSuppressed);
throw exception;
}
return proxyObject;
}