[SPR-8386] Polishing SmartContextLoader SPI; AnnotationConfigContextLoader now only considers static inner classes annotated with @Configuration as configuration classes.

This commit is contained in:
Sam Brannen 2011-06-18 19:45:39 +00:00
parent bede025eb9
commit 46639c5a1d
9 changed files with 173 additions and 42 deletions

View File

@ -34,14 +34,14 @@ public class ContextConfigurationAttributes {
private final Class<?> declaringClass;
private String[] locations;
private Class<?>[] classes;
private final boolean inheritLocations;
private final Class<? extends ContextLoader> contextLoaderClass;
private String[] locations;
private Class<?>[] classes;
/**
* Resolves resource locations from the {@link ContextConfiguration#locations() locations}
@ -97,6 +97,20 @@ public class ContextConfigurationAttributes {
return this.declaringClass;
}
/**
* TODO Document isInheritLocations().
*/
public boolean isInheritLocations() {
return this.inheritLocations;
}
/**
* TODO Document getContextLoaderClass().
*/
public Class<? extends ContextLoader> getContextLoaderClass() {
return this.contextLoaderClass;
}
/**
* TODO Document getLocations().
*/
@ -125,20 +139,6 @@ public class ContextConfigurationAttributes {
this.classes = classes;
}
/**
* TODO Document isInheritLocations().
*/
public boolean isInheritLocations() {
return this.inheritLocations;
}
/**
* TODO Document getContextLoaderClass().
*/
public Class<? extends ContextLoader> getContextLoaderClass() {
return this.contextLoaderClass;
}
/**
* TODO Document overridden toString().
*/

View File

@ -310,7 +310,7 @@ abstract class ContextLoaderUtils {
if (contextLoader instanceof SmartContextLoader) {
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
// TODO Decide on mutability of locations and classes properties
smartContextLoader.processContextConfigurationAttributes(configAttributes);
smartContextLoader.processContextConfiguration(configAttributes);
locationsList.addAll(Arrays.asList(configAttributes.getLocations()));
classesList.addAll(Arrays.asList(configAttributes.getClasses()));
}

View File

@ -27,13 +27,13 @@ import org.springframework.context.ApplicationContext;
public interface SmartContextLoader extends ContextLoader {
/**
* TODO Document processContextConfigurationAttributes().
* TODO Document processContextConfiguration().
*/
void processContextConfigurationAttributes(ContextConfigurationAttributes configAttributes);
void processContextConfiguration(ContextConfigurationAttributes configAttributes);
/**
* TODO Document loadContext().
*/
ApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration) throws Exception;
ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception;
}

View File

@ -42,9 +42,9 @@ import org.springframework.util.StringUtils;
public abstract class AbstractContextLoader implements SmartContextLoader {
/**
* TODO Document processContextConfigurationAttributes().
* TODO Document processContextConfiguration().
*/
public void processContextConfigurationAttributes(ContextConfigurationAttributes configAttributes) {
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
String[] processedLocations = processLocations(configAttributes.getDeclaringClass(),
configAttributes.getLocations());

View File

@ -52,17 +52,16 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
*
* @see SmartContextLoader#loadContext(MergedContextConfiguration)
*/
public final ConfigurableApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration)
throws Exception {
public final ConfigurableApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Loading ApplicationContext for merged context configuration [%s].",
mergedContextConfiguration));
mergedConfig));
}
GenericApplicationContext context = new GenericApplicationContext();
context.getEnvironment().setActiveProfiles(mergedContextConfiguration.getActiveProfiles());
context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
prepareContext(context);
customizeBeanFactory(context.getDefaultListableBeanFactory());
loadBeanDefinitions(context, mergedContextConfiguration);
loadBeanDefinitions(context, mergedConfig);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
customizeContext(context);
context.refresh();
@ -149,13 +148,12 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
* and override this method to provide a custom strategy for loading or
* registering bean definitions.
* @param context the context into which the bean definitions should be loaded
* @param mergedContextConfiguration TODO Document parameters.
* @param mergedConfig TODO Document parameters.
* @since 3.1
* @see #loadContext
*/
protected void loadBeanDefinitions(GenericApplicationContext context,
MergedContextConfiguration mergedContextConfiguration) {
createBeanDefinitionReader(context).loadBeanDefinitions(mergedContextConfiguration.getLocations());
protected void loadBeanDefinitions(GenericApplicationContext context, MergedContextConfiguration mergedConfig) {
createBeanDefinitionReader(context).loadBeanDefinitions(mergedConfig.getLocations());
}
/**

View File

@ -23,6 +23,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.MergedContextConfiguration;
@ -43,14 +44,12 @@ public class AnnotationConfigContextLoader extends AbstractGenericContextLoader
/**
* TODO Document overridden processContextConfigurationAttributes().
*
* @see org.springframework.test.context.SmartContextLoader#processContextConfigurationAttributes
* TODO Document overridden processContextConfiguration().
*/
public void processContextConfigurationAttributes(ContextConfigurationAttributes configAttributes) {
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
if (ObjectUtils.isEmpty(configAttributes.getClasses()) && isGenerateDefaultClasses()) {
Class<?>[] defaultConfigurationClasses = generateDefaultConfigurationClasses(configAttributes.getDeclaringClass());
configAttributes.setClasses(defaultConfigurationClasses);
Class<?>[] defaultConfigClasses = generateDefaultConfigurationClasses(configAttributes.getDeclaringClass());
configAttributes.setClasses(defaultConfigClasses);
}
}
@ -87,6 +86,8 @@ public class AnnotationConfigContextLoader extends AbstractGenericContextLoader
/**
* TODO Document isGenerateDefaultClasses().
* <p>
* TODO Consider renaming to a generic boolean generatesDefaults() method and moving to SmartContextLoader.
*/
protected boolean isGenerateDefaultClasses() {
return true;
@ -101,7 +102,7 @@ public class AnnotationConfigContextLoader extends AbstractGenericContextLoader
* class name refers to a nested <code>static</code> class within the
* test class.
*
* @see #generateDefaultLocations(Class)
* @see #generateDefaultConfigurationClasses(Class)
*/
protected String getConfigurationClassNameSuffix() {
return "$ContextConfiguration";
@ -118,10 +119,18 @@ public class AnnotationConfigContextLoader extends AbstractGenericContextLoader
List<Class<?>> configClasses = new ArrayList<Class<?>>();
try {
configClasses.add((Class<?>) getClass().getClassLoader().loadClass(className));
Class<?> configClass = (Class<?>) getClass().getClassLoader().loadClass(className);
if (configClass.isAnnotationPresent(Configuration.class)) {
configClasses.add(configClass);
}
else {
logger.warn(String.format(
"Found candidate configuration class [%s], but it is not annotated with @Configuration.", className));
}
}
catch (ClassNotFoundException e) {
logger.warn(String.format("Cannot load @Configuration class with generated class name [%s].", className), e);
logger.warn(String.format(
"Cannot load @Configuration class with generated class name [%s]: class not found", className));
}
return configClasses.toArray(new Class<?>[configClasses.size()]);

View File

@ -0,0 +1,50 @@
/*
* Copyright 2002-2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
/**
* Unit tests for {@link AnnotationConfigContextLoader}.
*
* @author Sam Brannen
* @since 3.1
*/
public class AnnotationConfigContextLoaderTests {
private final AnnotationConfigContextLoader contextLoader = new AnnotationConfigContextLoader();
@Test
public void generateDefaultConfigurationClassesWithFailure() {
Class<?>[] defaultLocations = contextLoader.generateDefaultConfigurationClasses(FooConfigInnerClassTestCase.class);
assertNotNull(defaultLocations);
assertEquals("FooConfigInnerClassTestCase.FooConfig should NOT be found", 0, defaultLocations.length);
}
@Test
public void generateDefaultConfigurationClassesWithSuccess() {
Class<?>[] defaultLocations = contextLoader.generateDefaultConfigurationClasses(ContextConfigurationInnerClassTestCase.class);
assertNotNull(defaultLocations);
assertEquals("ContextConfigurationInnerClassTestCase.ContextConfiguration should be found", 1,
defaultLocations.length);
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.support;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Sam Brannen
* @since 3.1
*/
public class ContextConfigurationInnerClassTestCase {
@Configuration
static class ContextConfiguration {
@Bean
public String foo() {
return "foo";
}
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.support;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Sam Brannen
* @since 3.1
*/
public class FooConfigInnerClassTestCase {
@Configuration
static class FooConfig {
@Bean
public String foo() {
return "foo";
}
}
}