All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.perfectable.introspection.proxy.LazyInitialization Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version
package org.perfectable.introspection.proxy;

import java.lang.reflect.Method;
import java.util.Optional;

import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import static org.perfectable.introspection.Introspections.introspect;

/**
 * Helper class to create proxy which delegate invocation to lazily-initialized instance.
 *
 * 

Proxies are created by {@link #createProxy}. */ public final class LazyInitialization { /** * Additional interface that all proxies created by {@link #createProxy} will implement. * * @param type of actual delegation target. */ @SuppressWarnings("InterfaceMayBeAnnotatedFunctional") public interface Proxy { /** * Extracts delegation target. * * @return target if it was initialized, empty if proxy hasn't yet been initialized. */ Optional extractInstance(); } /** * Provider interface for proxy delegation target. * * @param type of target */ @FunctionalInterface public interface Initializer { /** * Initializes the actual target and returns it. * *

In ideal scenario this method is called once for each proxy. * * @return Delegation target for created proxy */ T initialize(); } /** * Creates a proxy that forwards invocations to lazily initialized instance. * *

On any method call on resulting proxy it will either initialize instance and cache it, or use existing * instance, and delegate the call to it, returning the result or rethrowing exception of the call. There are * two exceptions of this rule: *

    *
  • {@link Object#finalize} is always ignored when called on proxy
  • *
  • Methods from {@link Proxy} interface are treated specially and handled with separate route. They * won't necessarily initialize the proxy and won't be forwarded to the target.
  • *
* *

Proxies will have {@code resultClass} as a superclass/superinterface. Additionally, proxy class will implement * {@link Proxy} interface. This wont be reflected in the compile-time type, but it can be checked by instanceof * and it can be casted to it. * * @param resultClass class or interface that proxy has to implement. This is also a superclass/superinterface of * initialized instance * @param initializer method of initializing actual delegation target * @param type of proxy * @return proxy object */ public static T createProxy(Class resultClass, Initializer initializer) { LazyInitializationHandler handler = LazyInitializationHandler.create(initializer); return ProxyBuilder.forType(resultClass).withInterface(Proxy.class).instantiate(handler); } private static final class LazyInitializationHandler implements InvocationHandler<@Nullable Object, Exception, MethodInvocation> { private static final Method EXTRACT_INSTANCE_METHOD = introspect(Proxy.class).methods().named("extractInstance").parameters().unique(); private final Initializer initializer; private final Object initializationMonitor = new Object(); private @MonotonicNonNull T instance; static LazyInitializationHandler create(Initializer initializer) { return new LazyInitializationHandler<>(initializer); } private LazyInitializationHandler(Initializer initializer) { this.initializer = initializer; } @Override public @Nullable Object handle(MethodInvocation invocation) throws Exception { MethodInvocation.Decomposer> decomposer = this::replaceInvocation; Invocation<@Nullable Object, Exception> replaced = invocation.decompose(decomposer); return replaced.invoke(); } private Invocation<@Nullable Object, Exception> replaceInvocation(Method method, @SuppressWarnings("unused") T receiver, @Nullable Object... arguments) { if (EXTRACT_INSTANCE_METHOD.equals(method)) { return () -> Optional.ofNullable(instance); } synchronized (initializationMonitor) { if (this.instance == null) { this.instance = this.initializer.initialize(); } } return MethodInvocation.of(method, this.instance, arguments); } } private LazyInitialization() { // utility class } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy