mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Provide more control over factory failure handling
Add an additional `FactoryInstantiationFailureHandler` strategy interface to `SpringFactoriesLoader` to allows instantiation failures to be handled on a per-factory bases. For example, to log trace messages for only factories that can't be created the following can be used: FactoryInstantiationFailureHandler.logging(logger); If no `FactoryInstantiationFailureHandler` instance is supplied then `FactoryInstantiationFailureHandler.throwing()` is used which provides back-compatible behavior by throwing an `IllegalArgumentException`. See gh-28057 Co-authored-by: Madhura Bhave <bhavem@vmware.com> Co-authored-by: Andy Wilkinson <wilkinsona@vmware.com>
This commit is contained in:
committed by
Stephane Nicoll
parent
0b716c4f90
commit
12244b2e51
+74
@@ -21,8 +21,10 @@ import java.lang.reflect.Modifier;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
@@ -30,10 +32,16 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader.ArgumentResolver;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader.FactoryInstantiator;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader.FailureHandler;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringFactoriesLoader}.
|
||||
@@ -95,6 +103,14 @@ class SpringFactoriesLoaderTests {
|
||||
+ "[org.springframework.core.io.support.MyDummyFactory1] for factory type [java.lang.String]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void attemptToLoadFactoryOfIncompatibleTypeWithLoggingFailureHandler() {
|
||||
Log logger = mock(Log.class);
|
||||
FailureHandler failureHandler = FailureHandler.logging(logger);
|
||||
List<String> factories = SpringFactoriesLoader.loadFactories(String.class, null, failureHandler);
|
||||
assertThat(factories.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadFactoryWithNonDefaultConstructor() {
|
||||
ArgumentResolver resolver = ArgumentResolver.of(String.class, "injected");
|
||||
@@ -116,6 +132,64 @@ class SpringFactoriesLoaderTests {
|
||||
.havingRootCause().withMessageContaining("Class [org.springframework.core.io.support.MultipleConstructorArgsDummyFactory] has no suitable constructor");
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadFactoryWithMissingArgumentUsingLoggingFailureHandler() {
|
||||
Log logger = mock(Log.class);
|
||||
FailureHandler failureHandler = FailureHandler.logging(logger);
|
||||
List<DummyFactory> factories = SpringFactoriesLoader.loadFactories(DummyFactory.class, LimitedClassLoader.multipleArgumentFactories, failureHandler);
|
||||
assertThat(factories).hasSize(2);
|
||||
assertThat(factories.get(0)).isInstanceOf(MyDummyFactory1.class);
|
||||
assertThat(factories.get(1)).isInstanceOf(MyDummyFactory2.class);
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class FailureHandlerTests {
|
||||
|
||||
@Test
|
||||
void throwingReturnsHandlerThatThrowsIllegalArgumentException() {
|
||||
FailureHandler handler = FailureHandler.throwing();
|
||||
RuntimeException cause = new RuntimeException();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> handler.handleFailure(
|
||||
DummyFactory.class, MyDummyFactory1.class.getName(),
|
||||
cause)).withMessageStartingWith("Unable to instantiate factory class").withCause(cause);
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwingWithFactoryReturnsHandlerThatThrows() {
|
||||
FailureHandler handler = FailureHandler.throwing(IllegalStateException::new);
|
||||
RuntimeException cause = new RuntimeException();
|
||||
assertThatIllegalStateException().isThrownBy(() -> handler.handleFailure(
|
||||
DummyFactory.class, MyDummyFactory1.class.getName(),
|
||||
cause)).withMessageStartingWith("Unable to instantiate factory class").withCause(cause);
|
||||
}
|
||||
|
||||
@Test
|
||||
void loggingReturnsHandlerThatLogs() {
|
||||
Log logger = mock(Log.class);
|
||||
FailureHandler handler = FailureHandler.logging(logger);
|
||||
RuntimeException cause = new RuntimeException();
|
||||
handler.handleFailure(DummyFactory.class, MyDummyFactory1.class.getName(), cause);
|
||||
verify(logger).trace(isA(LogMessage.class), eq(cause));
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleMessageReturnsHandlerThatAcceptsMessage() {
|
||||
List<Throwable> failures = new ArrayList<>();
|
||||
List<String> messages = new ArrayList<>();
|
||||
FailureHandler handler = FailureHandler.handleMessage((message, failure) -> {
|
||||
failures.add(failure);
|
||||
messages.add(message.get());
|
||||
});
|
||||
RuntimeException cause = new RuntimeException();
|
||||
handler.handleFailure(DummyFactory.class, MyDummyFactory1.class.getName(), cause);
|
||||
assertThat(failures).containsExactly(cause);
|
||||
assertThat(messages).hasSize(1);
|
||||
assertThat(messages.get(0)).startsWith("Unable to instantiate factory class");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class ArgumentResolverTests {
|
||||
|
||||
Reference in New Issue
Block a user