提交 9c5cabf4 编写于 作者: J Juergen Hoeller

Refined ApplicationContextInitializer assignability exception

(cherry picked from commit ca19920d)
上级 97b01778
/* /*
* 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 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of * use this file except in compliance with the License. You may obtain a copy of
...@@ -373,8 +373,8 @@ public abstract class Assert { ...@@ -373,8 +373,8 @@ public abstract class Assert {
public static void isAssignable(Class<?> superType, Class<?> subType, String message) { public static void isAssignable(Class<?> superType, Class<?> subType, String message) {
notNull(superType, "Type to check against must not be null"); notNull(superType, "Type to check against must not be null");
if (subType == null || !superType.isAssignableFrom(subType)) { if (subType == null || !superType.isAssignableFrom(subType)) {
throw new IllegalArgumentException((StringUtils.hasLength(message) ? message + " " : "") throw new IllegalArgumentException((StringUtils.hasLength(message) ? message + " " : "") +
+ subType + " is not assignable to " + superType); subType + " is not assignable to " + superType);
} }
} }
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -25,6 +25,7 @@ import org.apache.commons.logging.LogFactory; ...@@ -25,6 +25,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.GenericTypeResolver; import org.springframework.core.GenericTypeResolver;
...@@ -86,8 +87,8 @@ public abstract class AbstractContextLoader implements SmartContextLoader { ...@@ -86,8 +87,8 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
*/ */
@Override @Override
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) { public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
String[] processedLocations = processLocations(configAttributes.getDeclaringClass(), String[] processedLocations =
configAttributes.getLocations()); processLocations(configAttributes.getDeclaringClass(), configAttributes.getLocations());
configAttributes.setLocations(processedLocations); configAttributes.setLocations(processedLocations);
} }
...@@ -135,7 +136,9 @@ public abstract class AbstractContextLoader implements SmartContextLoader { ...@@ -135,7 +136,9 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void invokeApplicationContextInitializers(ConfigurableApplicationContext context, private void invokeApplicationContextInitializers(ConfigurableApplicationContext context,
MergedContextConfiguration mergedConfig) { MergedContextConfiguration mergedConfig) {
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = mergedConfig.getContextInitializerClasses();
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses =
mergedConfig.getContextInitializerClasses();
if (initializerClasses.isEmpty()) { if (initializerClasses.isEmpty()) {
// no ApplicationContextInitializers have been declared -> nothing to do // no ApplicationContextInitializers have been declared -> nothing to do
return; return;
...@@ -145,13 +148,15 @@ public abstract class AbstractContextLoader implements SmartContextLoader { ...@@ -145,13 +148,15 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
Class<?> contextClass = context.getClass(); Class<?> contextClass = context.getClass();
for (Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>> initializerClass : initializerClasses) { for (Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, Class<?> initializerContextClass =
ApplicationContextInitializer.class); GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
Assert.isAssignable(initializerContextClass, contextClass, String.format( if (initializerContextClass != null && !initializerContextClass.isInstance(context)) {
"Could not add context initializer [%s] since its generic parameter [%s] " throw new ApplicationContextException(String.format(
+ "is not assignable from the type of application context used by this " "Could not apply context initializer [%s] since its generic parameter [%s] " +
+ "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(), "is not assignable from the type of application context used by this " +
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
contextClass.getName())); contextClass.getName()));
}
initializerInstances.add((ApplicationContextInitializer<ConfigurableApplicationContext>) BeanUtils.instantiateClass(initializerClass)); initializerInstances.add((ApplicationContextInitializer<ConfigurableApplicationContext>) BeanUtils.instantiateClass(initializerClass));
} }
...@@ -161,6 +166,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader { ...@@ -161,6 +166,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
} }
} }
// --- ContextLoader ------------------------------------------------------- // --- ContextLoader -------------------------------------------------------
/** /**
...@@ -171,7 +177,6 @@ public abstract class AbstractContextLoader implements SmartContextLoader { ...@@ -171,7 +177,6 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
* and the configured {@linkplain #getResourceSuffixes() resource suffixes}; * and the configured {@linkplain #getResourceSuffixes() resource suffixes};
* otherwise, the supplied {@code locations} will be * otherwise, the supplied {@code locations} will be
* {@linkplain #modifyLocations modified} if necessary and returned. * {@linkplain #modifyLocations modified} if necessary and returned.
*
* @param clazz the class with which the locations are associated: to be * @param clazz the class with which the locations are associated: to be
* used when generating default locations * used when generating default locations
* @param locations the unmodified locations to use for loading the * @param locations the unmodified locations to use for loading the
...@@ -186,30 +191,26 @@ public abstract class AbstractContextLoader implements SmartContextLoader { ...@@ -186,30 +191,26 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
*/ */
@Override @Override
public final String[] processLocations(Class<?> clazz, String... locations) { public final String[] processLocations(Class<?> clazz, String... locations) {
return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ? generateDefaultLocations(clazz) return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ?
: modifyLocations(clazz, locations); generateDefaultLocations(clazz) : modifyLocations(clazz, locations);
} }
/** /**
* Generate the default classpath resource locations array based on the * Generate the default classpath resource locations array based on the
* supplied class. * supplied class.
*
* <p>For example, if the supplied class is {@code com.example.MyTest}, * <p>For example, if the supplied class is {@code com.example.MyTest},
* the generated locations will contain a single string with a value of * the generated locations will contain a single string with a value of
* {@code "classpath:com/example/MyTest<suffix>"}, where {@code <suffix>} * {@code "classpath:com/example/MyTest<suffix>"}, where {@code <suffix>}
* is the value of the first configured * is the value of the first configured
* {@linkplain #getResourceSuffixes() resource suffix} for which the * {@linkplain #getResourceSuffixes() resource suffix} for which the
* generated location actually exists in the classpath. * generated location actually exists in the classpath.
*
* <p>As of Spring 3.1, the implementation of this method adheres to the * <p>As of Spring 3.1, the implementation of this method adheres to the
* contract defined in the {@link SmartContextLoader} SPI. Specifically, * contract defined in the {@link SmartContextLoader} SPI. Specifically,
* this method will <em>preemptively</em> verify that the generated default * this method will <em>preemptively</em> verify that the generated default
* location actually exists. If it does not exist, this method will log a * location actually exists. If it does not exist, this method will log a
* warning and return an empty array. * warning and return an empty array.
*
* <p>Subclasses can override this method to implement a different * <p>Subclasses can override this method to implement a different
* <em>default location generation</em> strategy. * <em>default location generation</em> strategy.
*
* @param clazz the class for which the default locations are to be generated * @param clazz the class for which the default locations are to be generated
* @return an array of default application context resource locations * @return an array of default application context resource locations
* @since 2.5 * @since 2.5
...@@ -224,23 +225,22 @@ public abstract class AbstractContextLoader implements SmartContextLoader { ...@@ -224,23 +225,22 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix; String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix;
String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath; String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
ClassPathResource classPathResource = new ClassPathResource(resourcePath); ClassPathResource classPathResource = new ClassPathResource(resourcePath);
if (classPathResource.exists()) { if (classPathResource.exists()) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info(String.format("Detected default resource location \"%s\" for test class [%s]", 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()) { else if (logger.isDebugEnabled()) {
logger.debug(String.format("Did not detect default resource location for test class [%s]: " logger.debug(String.format("Did not detect default resource location for test class [%s]: " +
+ "%s does not exist", clazz.getName(), classPathResource)); "%s does not exist", clazz.getName(), classPathResource));
} }
} }
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info(String.format("Could not detect default resource locations for test class [%s]: " 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))); "no resource found for suffixes %s.", clazz.getName(), ObjectUtils.nullSafeToString(suffixes)));
} }
return EMPTY_STRING_ARRAY; return EMPTY_STRING_ARRAY;
...@@ -282,36 +282,31 @@ public abstract class AbstractContextLoader implements SmartContextLoader { ...@@ -282,36 +282,31 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
} }
/** /**
* Get the suffix to append to {@link ApplicationContext} resource * Get the suffixes to append to {@link ApplicationContext} resource locations
* locations when detecting default 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)
* @see #getResourceSuffixes()
*/
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 * <p>The default implementation simply wraps the value returned by
* {@link #getResourceSuffix()} in a single-element array, but this * {@link #getResourceSuffix()} in a single-element array, but this
* can be overridden by subclasses in order to support multiple suffixes. * can be overridden by subclasses in order to support multiple suffixes.
*
* @return the resource suffixes; never {@code null} or empty * @return the resource suffixes; never {@code null} or empty
* @since 4.1 * @since 4.1
* @see #generateDefaultLocations(Class) * @see #generateDefaultLocations(Class)
*/ */
protected String[] getResourceSuffixes() { protected String[] getResourceSuffixes() {
return new String[] { getResourceSuffix() }; 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)
* @see #getResourceSuffixes()
*/
protected abstract String getResourceSuffix();
} }
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -49,19 +49,18 @@ import org.springframework.util.StringUtils; ...@@ -49,19 +49,18 @@ import org.springframework.util.StringUtils;
* Performs the actual initialization work for the root application context. * Performs the actual initialization work for the root application context.
* Called by {@link ContextLoaderListener}. * Called by {@link ContextLoaderListener}.
* *
* <p>Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter * <p>Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter at the
* at the {@code web.xml} context-param level to specify the context * {@code web.xml} context-param level to specify the context class type, falling
* class type, falling back to the default of * back to {@link org.springframework.web.context.support.XmlWebApplicationContext}
* {@link org.springframework.web.context.support.XmlWebApplicationContext}
* if not found. With the default ContextLoader implementation, any context class * if not found. With the default ContextLoader implementation, any context class
* specified needs to implement the ConfigurableWebApplicationContext interface. * specified needs to implement the {@link ConfigurableWebApplicationContext} interface.
* *
* <p>Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"} * <p>Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"} context-param
* context-param and passes its value to the context instance, parsing it into * and passes its value to the context instance, parsing it into potentially multiple
* potentially multiple file paths which can be separated by any number of * file paths which can be separated by any number of commas and spaces, e.g.
* commas and spaces, e.g. "WEB-INF/applicationContext1.xml, * "WEB-INF/applicationContext1.xml, WEB-INF/applicationContext2.xml".
* WEB-INF/applicationContext2.xml". Ant-style path patterns are supported as well, * Ant-style path patterns are supported as well, e.g.
* e.g. "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/&#42;&#42;/*Context.xml". * "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/&#42;&#42;/*Context.xml".
* If not explicitly specified, the context implementation is supposed to use a * If not explicitly specified, the context implementation is supposed to use a
* default location (with XmlWebApplicationContext: "/WEB-INF/applicationContext.xml"). * default location (with XmlWebApplicationContext: "/WEB-INF/applicationContext.xml").
* *
...@@ -70,10 +69,9 @@ import org.springframework.util.StringUtils; ...@@ -70,10 +69,9 @@ import org.springframework.util.StringUtils;
* Spring's default ApplicationContext implementations. This can be leveraged * Spring's default ApplicationContext implementations. This can be leveraged
* to deliberately override certain bean definitions via an extra XML file. * to deliberately override certain bean definitions via an extra XML file.
* *
* <p>Above and beyond loading the root application context, this class * <p>Above and beyond loading the root application context, this class can optionally
* can optionally load or obtain and hook up a shared parent context to * load or obtain and hook up a shared parent context to the root application context.
* the root application context. See the * See the {@link #loadParentContext(ServletContext)} method for more information.
* {@link #loadParentContext(ServletContext)} method for more information.
* *
* <p>As of Spring 3.1, {@code ContextLoader} supports injecting the root web * <p>As of Spring 3.1, {@code ContextLoader} supports injecting the root web
* application context via the {@link #ContextLoader(WebApplicationContext)} * application context via the {@link #ContextLoader(WebApplicationContext)}
...@@ -470,11 +468,11 @@ public class ContextLoader { ...@@ -470,11 +468,11 @@ public class ContextLoader {
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) { for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass = Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null) { if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
Assert.isAssignable(initializerContextClass, wac.getClass(), String.format( throw new ApplicationContextException(String.format(
"Could not add context initializer [%s] since its generic parameter [%s] " + "Could not apply context initializer [%s] since its generic parameter [%s] " +
"is not assignable from the type of application context used by this " + "is not assignable from the type of application context used by this " +
"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(), "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName())); wac.getClass().getName()));
} }
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass)); this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -43,7 +43,6 @@ import org.springframework.core.GenericTypeResolver; ...@@ -43,7 +43,6 @@ import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
...@@ -738,17 +737,17 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic ...@@ -738,17 +737,17 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader()); Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader());
Class<?> initializerContextClass = Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null) { if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
Assert.isAssignable(initializerContextClass, wac.getClass(), String.format( throw new ApplicationContextException(String.format(
"Could not add context initializer [%s] since its generic parameter [%s] " + "Could not apply context initializer [%s] since its generic parameter [%s] " +
"is not assignable from the type of application context used by this " + "is not assignable from the type of application context used by this " +
"framework servlet [%s]: ", initializerClass.getName(), initializerContextClass.getName(), "framework servlet: [%s]", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName())); wac.getClass().getName()));
} }
return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class); return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class);
} }
catch (Exception ex) { catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(String.format("Could not instantiate class [%s] specified " + throw new ApplicationContextException(String.format("Could not load class [%s] specified " +
"via 'contextInitializerClasses' init-param", className), ex); "via 'contextInitializerClasses' init-param", className), ex);
} }
} }
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -225,7 +225,7 @@ public class ContextLoaderTests { ...@@ -225,7 +225,7 @@ public class ContextLoaderTests {
listener.contextInitialized(new ServletContextEvent(sc)); listener.contextInitialized(new ServletContextEvent(sc));
fail("expected exception"); fail("expected exception");
} }
catch (IllegalArgumentException ex) { catch (ApplicationContextException ex) {
assertTrue(ex.getMessage().contains("not assignable")); assertTrue(ex.getMessage().contains("not assignable"));
} }
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册