Skip to content

Commit 2145766

Browse files
committed
Proper NoClassDefFoundError check against BeanUtils.instantiateClass
Issue: SPR-16369
1 parent 32b4279 commit 2145766

File tree

2 files changed

+51
-21
lines changed

2 files changed

+51
-21
lines changed

spring-beans/src/main/java/org/springframework/beans/BeanUtils.java

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,18 @@ public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationExceptio
9898
}
9999

100100
/**
101-
* Instantiate a class using its no-arg constructor.
101+
* Instantiate a class using its 'primary' constructor (for Kotlin classes,
102+
* potentially having default arguments declared) or its default constructor
103+
* (for regular Java classes, expecting a standard no-arg setup).
102104
* <p>Note that this method tries to set the constructor accessible
103105
* if given a non-accessible (that is, non-public) constructor.
104-
* @param clazz class to instantiate
106+
* @param clazz the class to instantiate
105107
* @return the new instance
106-
* @throws BeanInstantiationException if the bean cannot be instantiated
108+
* @throws BeanInstantiationException if the bean cannot be instantiated.
109+
* The cause may notably indicate a {@link NoSuchMethodException} if no
110+
* primary/default constructor was found - or an exception thrown from
111+
* the constructor invocation attempt, including a runtime-generated
112+
* {@link NoClassDefFoundError} in case of an unresolvable dependency.
107113
* @see Constructor#newInstance
108114
*/
109115
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
@@ -113,10 +119,7 @@ public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationExc
113119
}
114120
try {
115121
Constructor<T> ctor = (KotlinDetector.isKotlinType(clazz) ?
116-
KotlinDelegate.findPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
117-
if (ctor == null) {
118-
throw new BeanInstantiationException(clazz, "No default constructor found");
119-
}
122+
KotlinDelegate.getPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
120123
return instantiateClass(ctor);
121124
}
122125
catch (NoSuchMethodException ex) {
@@ -693,10 +696,26 @@ private static void copyProperties(Object source, Object target, @Nullable Class
693696
private static class KotlinDelegate {
694697

695698
/**
696-
* Return the Java constructor corresponding to the Kotlin primary constructor if any.
699+
* Determine the Java constructor corresponding to the Kotlin primary constructor.
700+
* @param clazz the {@link Class} of the Kotlin class
701+
* @throws NoSuchMethodException if no such constructor found
702+
* @since 5.0.3
703+
* @see #findPrimaryConstructor
704+
* @see Class#getDeclaredConstructor
705+
*/
706+
public static <T> Constructor<T> getPrimaryConstructor(Class<T> clazz) throws NoSuchMethodException {
707+
Constructor<T> ctor = findPrimaryConstructor(clazz);
708+
if (ctor == null) {
709+
throw new NoSuchMethodException();
710+
}
711+
return ctor;
712+
}
713+
714+
/**
715+
* Retrieve the Java constructor corresponding to the Kotlin primary constructor, if any.
697716
* @param clazz the {@link Class} of the Kotlin class
698717
* @see <a href="http://kotlinlang.org/docs/reference/classes.html#constructors">
699-
* http://kotlinlang.org/docs/reference/classes.html#constructors</a>
718+
* http://kotlinlang.org/docs/reference/classes.html#constructors</a>
700719
*/
701720
@Nullable
702721
public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
@@ -706,8 +725,10 @@ public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
706725
return null;
707726
}
708727
Constructor<T> constructor = ReflectJvmMapping.getJavaConstructor(primaryCtor);
709-
Assert.notNull(constructor,
710-
() -> "Failed to find Java constructor for Kotlin primary constructor: " + clazz.getName());
728+
if (constructor == null) {
729+
throw new IllegalStateException(
730+
"Failed to find Java constructor for Kotlin primary constructor: " + clazz.getName());
731+
}
711732
return constructor;
712733
}
713734
catch (UnsupportedOperationException ex) {
@@ -718,7 +739,8 @@ public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
718739
/**
719740
* Instantiate a Kotlin class using the provided constructor.
720741
* @param ctor the constructor of the Kotlin class to instantiate
721-
* @param args the constructor arguments to apply (use null for unspecified parameter if needed)
742+
* @param args the constructor arguments to apply
743+
* (use {@code null} for unspecified parameter if needed)
722744
*/
723745
public static <T> T instantiateClass(Constructor<T> ctor, Object... args)
724746
throws IllegalAccessException, InvocationTargetException, InstantiationException {

spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.commons.logging.Log;
2929
import org.apache.commons.logging.LogFactory;
3030

31+
import org.springframework.beans.BeanInstantiationException;
3132
import org.springframework.beans.BeanUtils;
3233
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
3334
import org.springframework.core.annotation.AnnotationUtils;
@@ -178,18 +179,25 @@ public final List<TestExecutionListener> getTestExecutionListeners() {
178179
return listeners;
179180
}
180181

181-
private List<TestExecutionListener> instantiateListeners(Collection<Class<? extends TestExecutionListener>> classesList) {
182-
List<TestExecutionListener> listeners = new ArrayList<>(classesList.size());
183-
for (Class<? extends TestExecutionListener> listenerClass : classesList) {
182+
private List<TestExecutionListener> instantiateListeners(Collection<Class<? extends TestExecutionListener>> classes) {
183+
List<TestExecutionListener> listeners = new ArrayList<>(classes.size());
184+
for (Class<? extends TestExecutionListener> listenerClass : classes) {
184185
try {
185186
listeners.add(BeanUtils.instantiateClass(listenerClass));
186187
}
187-
catch (NoClassDefFoundError err) {
188-
if (logger.isDebugEnabled()) {
189-
logger.debug(String.format("Could not instantiate TestExecutionListener [%s]. " +
190-
"Specify custom listener classes or make the default listener classes " +
191-
"(and their required dependencies) available. Offending class: [%s]",
192-
listenerClass.getName(), err.getMessage()));
188+
catch (BeanInstantiationException ex) {
189+
if (ex.getCause() instanceof NoClassDefFoundError) {
190+
// TestExecutionListener not applicable due to a missing dependency
191+
if (logger.isDebugEnabled()) {
192+
logger.debug(String.format(
193+
"Skipping candidate TestExecutionListener [%s] due to a missing dependency. " +
194+
"Specify custom listener classes or make the default listener classes " +
195+
"and their required dependencies available. Offending class: [%s]",
196+
listenerClass.getName(), ex.getCause().getMessage()));
197+
}
198+
}
199+
else {
200+
throw ex;
193201
}
194202
}
195203
}

0 commit comments

Comments
 (0)