Refined ApplicationContextInitializer assignability exception

This commit is contained in:
Juergen Hoeller
2016-02-24 17:50:14 +01:00
parent a68b910b7c
commit ca19920d74
5 changed files with 74 additions and 82 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.GenericTypeResolver;
@@ -86,8 +87,8 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
*/
@Override
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
String[] processedLocations = processLocations(configAttributes.getDeclaringClass(),
configAttributes.getLocations());
String[] processedLocations =
processLocations(configAttributes.getDeclaringClass(), configAttributes.getLocations());
configAttributes.setLocations(processedLocations);
}
@@ -135,7 +136,9 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
@SuppressWarnings("unchecked")
private void invokeApplicationContextInitializers(ConfigurableApplicationContext context,
MergedContextConfiguration mergedConfig) {
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = mergedConfig.getContextInitializerClasses();
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses =
mergedConfig.getContextInitializerClasses();
if (initializerClasses.isEmpty()) {
// no ApplicationContextInitializers have been declared -> nothing to do
return;
@@ -145,13 +148,15 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
Class<?> contextClass = context.getClass();
for (Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass,
ApplicationContextInitializer.class);
Assert.isAssignable(initializerContextClass, contextClass, String.format(
"Could not add context initializer [%s] since its generic parameter [%s] "
+ "is not assignable from the type of application context used by this "
+ "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
contextClass.getName()));
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null && !initializerContextClass.isInstance(context)) {
throw new ApplicationContextException(String.format(
"Could not apply context initializer [%s] since its generic parameter [%s] " +
"is not assignable from the type of application context used by this " +
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
contextClass.getName()));
}
initializerInstances.add((ApplicationContextInitializer<ConfigurableApplicationContext>) BeanUtils.instantiateClass(initializerClass));
}
@@ -161,6 +166,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
}
}
// --- ContextLoader -------------------------------------------------------
/**
@@ -171,7 +177,6 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
* and the configured {@linkplain #getResourceSuffixes() resource suffixes};
* otherwise, the supplied {@code locations} will be
* {@linkplain #modifyLocations modified} if necessary and returned.
*
* @param clazz the class with which the locations are associated: to be
* used when generating default locations
* @param locations the unmodified locations to use for loading the
@@ -186,30 +191,26 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
*/
@Override
public final String[] processLocations(Class<?> clazz, String... locations) {
return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ? generateDefaultLocations(clazz)
: modifyLocations(clazz, locations);
return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ?
generateDefaultLocations(clazz) : modifyLocations(clazz, locations);
}
/**
* Generate the default classpath resource locations array based on the
* supplied class.
*
* <p>For example, if the supplied class is {@code com.example.MyTest},
* the generated locations will contain a single string with a value of
* {@code "classpath:com/example/MyTest<suffix>"}, where {@code <suffix>}
* is the value of the first configured
* {@linkplain #getResourceSuffixes() resource suffix} for which the
* generated location actually exists in the classpath.
*
* <p>As of Spring 3.1, the implementation of this method adheres to the
* contract defined in the {@link SmartContextLoader} SPI. Specifically,
* this method will <em>preemptively</em> verify that the generated default
* location actually exists. If it does not exist, this method will log a
* warning and return an empty array.
*
* <p>Subclasses can override this method to implement a different
* <em>default location generation</em> strategy.
*
* @param clazz the class for which the default locations are to be generated
* @return an array of default application context resource locations
* @since 2.5
@@ -224,23 +225,22 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix;
String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
ClassPathResource classPathResource = new ClassPathResource(resourcePath);
if (classPathResource.exists()) {
if (logger.isInfoEnabled()) {
logger.info(String.format("Detected default resource location \"%s\" for test class [%s]",
prefixedResourcePath, clazz.getName()));
prefixedResourcePath, clazz.getName()));
}
return new String[] { prefixedResourcePath };
return new String[] {prefixedResourcePath};
}
else if (logger.isDebugEnabled()) {
logger.debug(String.format("Did not detect default resource location for test class [%s]: "
+ "%s does not exist", clazz.getName(), classPathResource));
logger.debug(String.format("Did not detect default resource location for test class [%s]: " +
"%s does not exist", clazz.getName(), classPathResource));
}
}
if (logger.isInfoEnabled()) {
logger.info(String.format("Could not detect default resource locations for test class [%s]: "
+ "no resource found for suffixes %s.", clazz.getName(), ObjectUtils.nullSafeToString(suffixes)));
logger.info(String.format("Could not detect default resource locations for test class [%s]: " +
"no resource found for suffixes %s.", clazz.getName(), ObjectUtils.nullSafeToString(suffixes)));
}
return EMPTY_STRING_ARRAY;
@@ -282,15 +282,26 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
}
/**
* Get the suffix to append to {@link ApplicationContext} resource
* locations when detecting default locations.
*
* <p>Subclasses must provide an implementation of this method that
* returns a single suffix. Alternatively subclasses may provide a
* <em>no-op</em> implementation of this method and override
* {@link #getResourceSuffixes()} in order to provide multiple custom
* suffixes.
*
* Get the suffixes to append to {@link ApplicationContext} resource locations
* when detecting default locations.
* <p>The default implementation simply wraps the value returned by
* {@link #getResourceSuffix()} in a single-element array, but this
* can be overridden by subclasses in order to support multiple suffixes.
* @return the resource suffixes; never {@code null} or empty
* @since 4.1
* @see #generateDefaultLocations(Class)
*/
protected String[] getResourceSuffixes() {
return new String[] {getResourceSuffix()};
}
/**
* Get the suffix to append to {@link ApplicationContext} resource locations
* when detecting default locations.
* <p>Subclasses must provide an implementation of this method that returns
* a single suffix. Alternatively subclasses may provide a <em>no-op</em>
* implementation of this method and override {@link #getResourceSuffixes()}
* in order to provide multiple custom suffixes.
* @return the resource suffix; never {@code null} or empty
* @since 2.5
* @see #generateDefaultLocations(Class)
@@ -298,20 +309,4 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
*/
protected abstract String getResourceSuffix();
/**
* Get the suffixes to append to {@link ApplicationContext} resource
* locations when detecting default locations.
*
* <p>The default implementation simply wraps the value returned by
* {@link #getResourceSuffix()} in a single-element array, but this
* can be overridden by subclasses in order to support multiple suffixes.
*
* @return the resource suffixes; never {@code null} or empty
* @since 4.1
* @see #generateDefaultLocations(Class)
*/
protected String[] getResourceSuffixes() {
return new String[] { getResourceSuffix() };
}
}