or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

admin-jmx.mdansi-support.mdaot-native-image.mdapplication-info.mdavailability.mdbootstrap.mdbootstrapping.mdbuilder.mdcloud-platform.mdconfiguration-annotations.mdconfiguration-data.mdconfiguration-properties.mdconversion.mddiagnostics.mdenvironment-property-sources.mdindex.mdjson-support.mdlifecycle-events.mdlogging.mdorigin-tracking.mdresource-loading.mdretry-support.mdssl-tls.mdstartup-metrics.mdsupport.mdsystem-utilities.mdtask-execution.mdthreading.mdutilities.mdvalidation.mdweb-support.md
tile.json

utilities.mddocs/

Spring Boot Utilities Package

Package: org.springframework.boot.util

The util package provides miscellaneous utility classes for instantiation and safe lambda invocation with generic type support.

Instantiator

Instantiator<T>

Generic factory for instantiating objects with constructor dependency injection.

public class Instantiator<T> {

    public Instantiator(Class<?> type,
                       Consumer<AvailableParameters> availableParameters);

    public Instantiator(Class<?> type,
                       Consumer<AvailableParameters> availableParameters,
                       FailureHandler failureHandler);

    public List<T> instantiate(Collection<String> names);

    public List<T> instantiate(ClassLoader classLoader, Collection<String> names);

    public T instantiate(String name);

    public T instantiate(ClassLoader classLoader, String name);

    public T instantiateType(Class<?> type);

    public List<T> instantiateTypes(Collection<Class<?>> types);

    public <A> A getArg(Class<A> type);
}

Inner Interfaces:

public interface AvailableParameters {
    void add(Class<?> type, Object instance);
    void add(Class<?> type, Function<Class<?>, Object> factory);
}
public interface FailureHandler {
    void handleFailure(Class<?> type,
                      String implementationName,
                      Throwable failure);
}

Features

  • Finds and invokes the best-matching constructor based on available parameters
  • Supports multiple constructor parameter types
  • Orders results using @Order or Ordered interface
  • Throws exceptions or uses custom failure handler

Basic Usage

// Create instantiator for a specific type
Instantiator<EnvironmentPostProcessor> instantiator =
    new Instantiator<>(EnvironmentPostProcessor.class, parameters -> {
        parameters.add(DeferredLogFactory.class, logFactory);
        parameters.add(ConfigurableBootstrapContext.class, bootstrapContext);
    });

// Instantiate from class names
List<EnvironmentPostProcessor> processors =
    instantiator.instantiate(List.of(
        "com.example.CustomPostProcessor",
        "com.example.AnotherPostProcessor"
    ));

// Instantiate from classes
List<EnvironmentPostProcessor> processors =
    instantiator.instantiateTypes(List.of(
        CustomPostProcessor.class,
        AnotherPostProcessor.class
    ));

Advanced Usage with Factories

Instantiator<MyService> instantiator =
    new Instantiator<>(MyService.class, parameters -> {
        // Fixed instance
        parameters.add(ApplicationContext.class, applicationContext);

        // Factory that provides different instance per type
        parameters.add(Logger.class, type ->
            LoggerFactory.getLogger(type));

        // Conditional provision
        parameters.add(DataSource.class, type -> {
            if (type.getName().contains("Primary")) {
                return primaryDataSource;
            }
            return secondaryDataSource;
        });
    });

Custom Failure Handling

Instantiator.FailureHandler loggingHandler =
    (type, implementationName, failure) -> {
        logger.warn("Failed to instantiate {} [{}]: {}",
            implementationName, type.getName(), failure.getMessage());
    };

Instantiator<Plugin> instantiator = new Instantiator<>(
    Plugin.class,
    parameters -> parameters.add(Config.class, config),
    loggingHandler
);

// Failed instantiations are logged but don't stop the process
List<Plugin> plugins = instantiator.instantiate(pluginClassNames);

Constructor Selection

The instantiator selects constructors based on available parameters:

public class MyService {

    // Constructor 1: Most parameters (preferred if all available)
    public MyService(ApplicationContext context,
                    DataSource dataSource,
                    Logger logger) {
        // Will be used if all three parameters are available
    }

    // Constructor 2: Fewer parameters (fallback)
    public MyService(ApplicationContext context) {
        // Will be used if only ApplicationContext is available
    }

    // Constructor 3: No parameters (last resort)
    public MyService() {
        // Will be used if no parameters match
    }
}

// Instantiator will try Constructor 1 first, then 2, then 3

Getting Individual Arguments

Instantiator<MyService> instantiator =
    new Instantiator<>(MyService.class, parameters -> {
        parameters.add(String.class, "config-value");
        parameters.add(Integer.class, 42);
    });

// Get individual arguments without instantiation
String configValue = instantiator.getArg(String.class);
Integer port = instantiator.getArg(Integer.class);

// Useful for manual instantiation
MyService service = new MyService(configValue, port);

LambdaSafe

LambdaSafe Utility

Utility for safely invoking lambda callbacks that may have generic type mismatches.

public final class LambdaSafe {

    public static <C, A> Callback<C, A> callback(
        Class<C> callbackType,
        C callbackInstance,
        A argument,
        Object... additionalArguments);

    public static <C, A> Callbacks<C, A> callbacks(
        Class<C> callbackType,
        Collection<? extends C> callbackInstances,
        A argument,
        Object... additionalArguments);
}

Callback and Callbacks Classes

public static final class Callback<C, A> {

    public Callback<C, A> withLogger(Class<?> loggerSource);

    public Callback<C, A> withLogger(Log logger);

    public Callback<C, A> withFilter(Filter<C, A> filter);

    public void invoke(Consumer<C> invoker);

    public <R> InvocationResult<R> invokeAnd(Function<C, R> invoker);
}
public static final class Callbacks<C, A> {

    public Callbacks<C, A> withLogger(Class<?> loggerSource);

    public Callbacks<C, A> withLogger(Log logger);

    public Callbacks<C, A> withFilter(Filter<C, A> filter);

    public void invoke(Consumer<C> invoker);

    public <R> Stream<R> invokeAnd(Function<C, R> invoker);
}
@FunctionalInterface
public interface Filter<C, A> {

    boolean match(Class<C> callbackType,
                 C callbackInstance,
                 A argument,
                 Object[] additionalArguments);

    static <C, A> Filter<C, A> allowAll();
}
public static final class InvocationResult<R> {

    public boolean hasResult();

    public R get();

    public R get(R fallback);

    public static <R> InvocationResult<R> of(R value);

    public static <R> InvocationResult<R> noResult();
}

Features

  • Handles ClassCastException caused by generic type erasure
  • Automatically filters callbacks based on generic type compatibility
  • Logs non-matching types at debug level
  • Continues execution even when individual callbacks fail

Basic Callback Usage

// Define a generic callback interface
interface ApplicationListener<E extends ApplicationEvent> {
    void onEvent(E event);
}

// Create a specific listener
ApplicationListener<MyEvent> listener = event -> {
    System.out.println("Handling: " + event.getMessage());
};

// Invoke safely
MyEvent event = new MyEvent("test");
LambdaSafe.callback(ApplicationListener.class, listener, event)
    .invoke(l -> l.onEvent(event));

Multiple Callbacks

List<ApplicationListener<ApplicationEvent>> listeners = List.of(
    new MyEventListener(),
    new AnotherEventListener(),
    new GenericListener()
);

ApplicationEvent event = new ApplicationStartedEvent();

// Invoke all compatible listeners
LambdaSafe.callbacks(ApplicationListener.class, listeners, event)
    .withLogger(MyClass.class)
    .invoke(listener -> listener.onEvent(event));

Callbacks with Return Values

interface DataProcessor<T> {
    String process(T data);
}

List<DataProcessor<?>> processors = List.of(
    (DataProcessor<String>) s -> s.toUpperCase(),
    (DataProcessor<Integer>) i -> String.valueOf(i * 2),
    (DataProcessor<Double>) d -> String.format("%.2f", d)
);

// Process string and collect results
Stream<String> results = LambdaSafe
    .callbacks(DataProcessor.class, processors, "hello")
    .invokeAnd(processor -> processor.process("hello"));

List<String> processed = results.collect(Collectors.toList());
// Result: ["HELLO"] (only String processor matched)

Custom Filtering

// Custom filter that matches based on annotation
Filter<MyCallback, MyData> annotationFilter = (callbackType, callback, data, args) -> {
    return callback.getClass().isAnnotationPresent(Active.class);
};

LambdaSafe.callbacks(MyCallback.class, callbacks, data)
    .withFilter(annotationFilter)
    .invoke(callback -> callback.process(data));

// Allow all filter
LambdaSafe.callbacks(MyCallback.class, callbacks, data)
    .withFilter(Filter.allowAll())
    .invoke(callback -> callback.process(data));

Handling Results with InvocationResult

interface DataLoader<T> {
    T load(String id);
}

DataLoader<User> userLoader = id -> userRepository.findById(id);

InvocationResult<User> result = LambdaSafe
    .callback(DataLoader.class, userLoader, "userId")
    .invokeAnd(loader -> loader.load("userId"));

if (result.hasResult()) {
    User user = result.get();
    // Use loaded user
} else {
    // Callback didn't match or failed
    User defaultUser = result.get(new User("default"));
}

Real-World Example: Event System

public class EventDispatcher {

    private final List<ApplicationListener<?>> listeners = new ArrayList<>();
    private final Log logger = LogFactory.getLog(EventDispatcher.class);

    public void addListener(ApplicationListener<?> listener) {
        listeners.add(listener);
    }

    public void publishEvent(ApplicationEvent event) {
        LambdaSafe.callbacks(ApplicationListener.class, listeners, event)
            .withLogger(logger)
            .invoke(listener -> listener.onEvent(event));
    }

    public <R> List<R> publishEventWithResponse(
            ApplicationEvent event,
            Function<ApplicationListener<?>, R> responseExtractor) {

        return LambdaSafe.callbacks(ApplicationListener.class, listeners, event)
            .withLogger(logger)
            .invokeAnd(listener -> {
                listener.onEvent(event);
                return responseExtractor.apply(listener);
            })
            .collect(Collectors.toList());
    }
}

Example: Plugin System

public class PluginManager<T> {

    private final Class<T> pluginType;
    private final List<T> plugins;

    public PluginManager(Class<T> pluginType) {
        this.pluginType = pluginType;
        this.plugins = new ArrayList<>();
    }

    public void registerPlugin(T plugin) {
        plugins.add(plugin);
    }

    public void notifyPlugins(Consumer<T> action) {
        LambdaSafe.callbacks(pluginType, plugins, null)
            .invoke(action);
    }

    public <R> Stream<R> queryPlugins(Function<T, R> query) {
        return LambdaSafe.callbacks(pluginType, plugins, null)
            .invokeAnd(query);
    }
}

// Usage
interface DataPlugin {
    void onDataChange(DataEvent event);
    String getStatus();
}

PluginManager<DataPlugin> manager = new PluginManager<>(DataPlugin.class);
manager.registerPlugin(new CachePlugin());
manager.registerPlugin(new LoggingPlugin());

// Notify all plugins
DataEvent event = new DataEvent("update");
manager.notifyPlugins(plugin -> plugin.onDataChange(event));

// Query all plugins
List<String> statuses = manager
    .queryPlugins(DataPlugin::getStatus)
    .collect(Collectors.toList());

Common Use Cases

Dynamic Service Loading

public class ServiceLoader<T> {

    private final Instantiator<T> instantiator;

    public ServiceLoader(Class<T> serviceType,
                        Consumer<AvailableParameters> parameters) {
        this.instantiator = new Instantiator<>(serviceType, parameters);
    }

    public List<T> loadServices(String... classNames) {
        return instantiator.instantiate(Arrays.asList(classNames));
    }

    public List<T> loadFromConfig(Configuration config) {
        List<String> classNames = config.getServiceClassNames();
        ClassLoader classLoader = config.getClassLoader();
        return instantiator.instantiate(classLoader, classNames);
    }
}

Safe Generic Event Handling

public class TypeSafeEventBus {

    private final Map<Class<?>, List<Object>> listeners = new HashMap<>();

    public <E> void register(Class<E> eventType, Consumer<E> listener) {
        listeners.computeIfAbsent(eventType, k -> new ArrayList<>())
            .add(listener);
    }

    @SuppressWarnings("unchecked")
    public <E> void publish(E event) {
        Class<?> eventType = event.getClass();
        List<Object> eventListeners = listeners.getOrDefault(eventType,
            Collections.emptyList());

        LambdaSafe.callbacks(Consumer.class,
                (Collection<Consumer<?>>) (Collection<?>) eventListeners,
                event)
            .invoke(listener -> listener.accept(event));
    }
}

Import Statements

import org.springframework.boot.util.Instantiator;
import org.springframework.boot.util.Instantiator.AvailableParameters;
import org.springframework.boot.util.Instantiator.FailureHandler;
import org.springframework.boot.util.LambdaSafe;
import org.springframework.boot.util.LambdaSafe.Callback;
import org.springframework.boot.util.LambdaSafe.Callbacks;
import org.springframework.boot.util.LambdaSafe.Filter;
import org.springframework.boot.util.LambdaSafe.InvocationResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.function.Consumer;
import java.util.function.Function;