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 | ||||
| 	 * 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> T instantiateClass(Class<T> clazz) throws BeanInstantiationException { | ||||
|  | @ -113,10 +119,7 @@ public abstract class BeanUtils { | |||
| 		} | ||||
| 		try { | ||||
| 			Constructor<T> 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 <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 | ||||
| 		 * @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 | ||||
| 		public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) { | ||||
|  | @ -706,8 +725,10 @@ public abstract class BeanUtils { | |||
| 					return null; | ||||
| 				} | ||||
| 				Constructor<T> 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> T instantiateClass(Constructor<T> ctor, Object... args) | ||||
| 				throws IllegalAccessException, InvocationTargetException, InstantiationException { | ||||
|  |  | |||
|  | @ -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<TestExecutionListener> instantiateListeners(Collection<Class<? extends TestExecutionListener>> classesList) { | ||||
| 		List<TestExecutionListener> listeners = new ArrayList<>(classesList.size()); | ||||
| 		for (Class<? extends TestExecutionListener> listenerClass : classesList) { | ||||
| 	private List<TestExecutionListener> instantiateListeners(Collection<Class<? extends TestExecutionListener>> classes) { | ||||
| 		List<TestExecutionListener> 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; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue