diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
index 3d3cc32a7ffb9a4e607a9ba16975b200de30bcb8..1a8d3cfd82f6366c38e557ae80afafa84d7eb963 100644
--- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
@@ -98,12 +98,18 @@ public abstract class BeanUtils {
}
/**
- * Instantiate a class using its no-arg constructor.
+ * Instantiate a class using its 'primary' constructor (for Kotlin classes,
+ * potentially having default arguments declared) or its default constructor
+ * (for regular Java classes, expecting a standard no-arg setup).
*
Note that this method tries to set the constructor accessible
* if given a non-accessible (that is, non-public) constructor.
- * @param clazz class to instantiate
+ * @param clazz the class to instantiate
* @return the new instance
- * @throws BeanInstantiationException if the bean cannot be instantiated
+ * @throws BeanInstantiationException if the bean cannot be instantiated.
+ * The cause may notably indicate a {@link NoSuchMethodException} if no
+ * primary/default constructor was found - or an exception thrown from
+ * the constructor invocation attempt, including a runtime-generated
+ * {@link NoClassDefFoundError} in case of an unresolvable dependency.
* @see Constructor#newInstance
*/
public static T instantiateClass(Class clazz) throws BeanInstantiationException {
@@ -113,10 +119,7 @@ public abstract class BeanUtils {
}
try {
Constructor ctor = (KotlinDetector.isKotlinType(clazz) ?
- KotlinDelegate.findPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
- if (ctor == null) {
- throw new BeanInstantiationException(clazz, "No default constructor found");
- }
+ KotlinDelegate.getPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
return instantiateClass(ctor);
}
catch (NoSuchMethodException ex) {
@@ -693,10 +696,26 @@ public abstract class BeanUtils {
private static class KotlinDelegate {
/**
- * Return the Java constructor corresponding to the Kotlin primary constructor if any.
+ * Determine the Java constructor corresponding to the Kotlin primary constructor.
+ * @param clazz the {@link Class} of the Kotlin class
+ * @throws NoSuchMethodException if no such constructor found
+ * @since 5.0.3
+ * @see #findPrimaryConstructor
+ * @see Class#getDeclaredConstructor
+ */
+ public static Constructor getPrimaryConstructor(Class clazz) throws NoSuchMethodException {
+ Constructor ctor = findPrimaryConstructor(clazz);
+ if (ctor == null) {
+ throw new NoSuchMethodException();
+ }
+ return ctor;
+ }
+
+ /**
+ * Retrieve the Java constructor corresponding to the Kotlin primary constructor, if any.
* @param clazz the {@link Class} of the Kotlin class
* @see
- * http://kotlinlang.org/docs/reference/classes.html#constructors
+ * http://kotlinlang.org/docs/reference/classes.html#constructors
*/
@Nullable
public static Constructor findPrimaryConstructor(Class clazz) {
@@ -706,8 +725,10 @@ public abstract class BeanUtils {
return null;
}
Constructor constructor = ReflectJvmMapping.getJavaConstructor(primaryCtor);
- Assert.notNull(constructor,
- () -> "Failed to find Java constructor for Kotlin primary constructor: " + clazz.getName());
+ if (constructor == null) {
+ throw new IllegalStateException(
+ "Failed to find Java constructor for Kotlin primary constructor: " + clazz.getName());
+ }
return constructor;
}
catch (UnsupportedOperationException ex) {
@@ -718,7 +739,8 @@ public abstract class BeanUtils {
/**
* Instantiate a Kotlin class using the provided constructor.
* @param ctor the constructor of the Kotlin class to instantiate
- * @param args the constructor arguments to apply (use null for unspecified parameter if needed)
+ * @param args the constructor arguments to apply
+ * (use {@code null} for unspecified parameter if needed)
*/
public static T instantiateClass(Constructor ctor, Object... args)
throws IllegalAccessException, InvocationTargetException, InstantiationException {
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
index 57f18f4d846ba490c1a5901a420a92c5d8f31b04..e096e216e360fd3e0259183945b46aca353933f3 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
@@ -28,6 +28,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
@@ -178,18 +179,25 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
return listeners;
}
- private List instantiateListeners(Collection> classesList) {
- List listeners = new ArrayList<>(classesList.size());
- for (Class extends TestExecutionListener> listenerClass : classesList) {
+ private List instantiateListeners(Collection> classes) {
+ List listeners = new ArrayList<>(classes.size());
+ for (Class extends TestExecutionListener> listenerClass : classes) {
try {
listeners.add(BeanUtils.instantiateClass(listenerClass));
}
- catch (NoClassDefFoundError err) {
- if (logger.isDebugEnabled()) {
- logger.debug(String.format("Could not instantiate TestExecutionListener [%s]. " +
- "Specify custom listener classes or make the default listener classes " +
- "(and their required dependencies) available. Offending class: [%s]",
- listenerClass.getName(), err.getMessage()));
+ catch (BeanInstantiationException ex) {
+ if (ex.getCause() instanceof NoClassDefFoundError) {
+ // TestExecutionListener not applicable due to a missing dependency
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format(
+ "Skipping candidate TestExecutionListener [%s] due to a missing dependency. " +
+ "Specify custom listener classes or make the default listener classes " +
+ "and their required dependencies available. Offending class: [%s]",
+ listenerClass.getName(), ex.getCause().getMessage()));
+ }
+ }
+ else {
+ throw ex;
}
}
}