docs
Package: org.springframework.boot.util
The util package provides miscellaneous utility classes for instantiation and safe lambda invocation with generic type support.
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);
}@Order or Ordered interface// 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
));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;
});
});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);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 3Instantiator<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);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);
}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();
}ClassCastException caused by generic type erasure// 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));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));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 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));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"));
}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());
}
}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());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);
}
}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 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;