Proper NoClassDefFoundError check against BeanUtils.instantiateClass
Issue: SPR-16369
This commit is contained in:
		
							parent
							
								
									32b4279929
								
							
						
					
					
						commit
						214576673a
					
				|  | @ -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). | ||||||
| 	 * <p>Note that this method tries to set the constructor accessible | 	 * <p>Note that this method tries to set the constructor accessible | ||||||
| 	 * if given a non-accessible (that is, non-public) constructor. | 	 * 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 | 	 * @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 | 	 * @see Constructor#newInstance | ||||||
| 	 */ | 	 */ | ||||||
| 	public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException { | 	public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException { | ||||||
|  | @ -113,10 +119,7 @@ public abstract class BeanUtils { | ||||||
| 		} | 		} | ||||||
| 		try { | 		try { | ||||||
| 			Constructor<T> ctor = (KotlinDetector.isKotlinType(clazz) ? | 			Constructor<T> ctor = (KotlinDetector.isKotlinType(clazz) ? | ||||||
| 					KotlinDelegate.findPrimaryConstructor(clazz) : clazz.getDeclaredConstructor()); | 					KotlinDelegate.getPrimaryConstructor(clazz) : clazz.getDeclaredConstructor()); | ||||||
| 			if (ctor == null) { |  | ||||||
| 				throw new BeanInstantiationException(clazz, "No default constructor found"); |  | ||||||
| 			} |  | ||||||
| 			return instantiateClass(ctor); | 			return instantiateClass(ctor); | ||||||
| 		} | 		} | ||||||
| 		catch (NoSuchMethodException ex) { | 		catch (NoSuchMethodException ex) { | ||||||
|  | @ -693,10 +696,26 @@ public abstract class BeanUtils { | ||||||
| 	private static class KotlinDelegate { | 	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 <T> Constructor<T> getPrimaryConstructor(Class<T> clazz) throws NoSuchMethodException { | ||||||
|  | 			Constructor<T> 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 | 		 * @param clazz the {@link Class} of the Kotlin class | ||||||
| 		 * @see <a href="http://kotlinlang.org/docs/reference/classes.html#constructors"> | 		 * @see <a href="http://kotlinlang.org/docs/reference/classes.html#constructors"> | ||||||
| 		 *     http://kotlinlang.org/docs/reference/classes.html#constructors</a> | 		 * http://kotlinlang.org/docs/reference/classes.html#constructors</a> | ||||||
| 		 */ | 		 */ | ||||||
| 		@Nullable | 		@Nullable | ||||||
| 		public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) { | 		public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) { | ||||||
|  | @ -706,8 +725,10 @@ public abstract class BeanUtils { | ||||||
| 					return null; | 					return null; | ||||||
| 				} | 				} | ||||||
| 				Constructor<T> constructor = ReflectJvmMapping.getJavaConstructor(primaryCtor); | 				Constructor<T> constructor = ReflectJvmMapping.getJavaConstructor(primaryCtor); | ||||||
| 				Assert.notNull(constructor, | 				if (constructor == null) { | ||||||
| 						() -> "Failed to find Java constructor for Kotlin primary constructor: " + clazz.getName()); | 					throw new IllegalStateException( | ||||||
|  | 							"Failed to find Java constructor for Kotlin primary constructor: " + clazz.getName()); | ||||||
|  | 				} | ||||||
| 				return constructor; | 				return constructor; | ||||||
| 			} | 			} | ||||||
| 			catch (UnsupportedOperationException ex) { | 			catch (UnsupportedOperationException ex) { | ||||||
|  | @ -718,7 +739,8 @@ public abstract class BeanUtils { | ||||||
| 		/** | 		/** | ||||||
| 		 * Instantiate a Kotlin class using the provided constructor. | 		 * Instantiate a Kotlin class using the provided constructor. | ||||||
| 		 * @param ctor the constructor of the Kotlin class to instantiate | 		 * @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> T instantiateClass(Constructor<T> ctor, Object... args) | 		public static <T> T instantiateClass(Constructor<T> ctor, Object... args) | ||||||
| 				throws IllegalAccessException, InvocationTargetException, InstantiationException { | 				throws IllegalAccessException, InvocationTargetException, InstantiationException { | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ import java.util.Set; | ||||||
| import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||||
| import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||||
| 
 | 
 | ||||||
|  | import org.springframework.beans.BeanInstantiationException; | ||||||
| import org.springframework.beans.BeanUtils; | import org.springframework.beans.BeanUtils; | ||||||
| import org.springframework.core.annotation.AnnotationAwareOrderComparator; | import org.springframework.core.annotation.AnnotationAwareOrderComparator; | ||||||
| import org.springframework.core.annotation.AnnotationUtils; | import org.springframework.core.annotation.AnnotationUtils; | ||||||
|  | @ -178,18 +179,25 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot | ||||||
| 		return listeners; | 		return listeners; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private List<TestExecutionListener> instantiateListeners(Collection<Class<? extends TestExecutionListener>> classesList) { | 	private List<TestExecutionListener> instantiateListeners(Collection<Class<? extends TestExecutionListener>> classes) { | ||||||
| 		List<TestExecutionListener> listeners = new ArrayList<>(classesList.size()); | 		List<TestExecutionListener> listeners = new ArrayList<>(classes.size()); | ||||||
| 		for (Class<? extends TestExecutionListener> listenerClass : classesList) { | 		for (Class<? extends TestExecutionListener> listenerClass : classes) { | ||||||
| 			try { | 			try { | ||||||
| 				listeners.add(BeanUtils.instantiateClass(listenerClass)); | 				listeners.add(BeanUtils.instantiateClass(listenerClass)); | ||||||
| 			} | 			} | ||||||
| 			catch (NoClassDefFoundError err) { | 			catch (BeanInstantiationException ex) { | ||||||
| 				if (logger.isDebugEnabled()) { | 				if (ex.getCause() instanceof NoClassDefFoundError) { | ||||||
| 					logger.debug(String.format("Could not instantiate TestExecutionListener [%s]. " + | 					// TestExecutionListener not applicable due to a missing dependency | ||||||
| 							"Specify custom listener classes or make the default listener classes " + | 					if (logger.isDebugEnabled()) { | ||||||
| 							"(and their required dependencies) available. Offending class: [%s]", | 						logger.debug(String.format( | ||||||
| 							listenerClass.getName(), err.getMessage())); | 								"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; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue