diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java index 1e8c0db0dd..f3c79d2ee7 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java +++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java @@ -16,12 +16,14 @@ package org.springframework.web.context.support; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.context.annotation.ScopeMetadataResolver; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * {@link org.springframework.web.context.WebApplicationContext} implementation @@ -57,14 +59,92 @@ import org.springframework.context.annotation.ScopeMetadataResolver; */ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext { + private Class[] annotatedClasses; + + private String[] basePackages; + + /** + * {@inheritDoc} + *

This implementation accepts delimited values in the form of fully-qualified + * class names, (typically of {@code Configuration} classes) or fully-qualified + * packages to scan for annotated classes. During {@link #loadBeanDefinitions}, these + * locations will be processed in their given order, first attempting to load each + * value as a class. If class loading fails (i.e. a {@code ClassNotFoundException} + * occurs), the value is assumed to be a package and scanning is attempted. + *

Note that this method exists primarily for compatibility with Spring's + * {@link org.springframework.web.context.ContextLoader} and that if this application + * context is being configured through an + * {@link org.springframework.context.ApplicationContextInitializer}, use of the + * {@link #register} and {@link #scan} methods are preferred. + * @see #register(Class...) + * @see #scan(String...) + * @see #setConfigLocations(String[]) + * @see #loadBeanDefinitions(DefaultListableBeanFactory) + */ + @Override + public void setConfigLocation(String location) { + super.setConfigLocation(location); + } + + /** + * {@inheritDoc} + *

This implementation accepts individual location values as fully-qualified class + * names (typically {@code @Configuration} classes) or fully-qualified packages to + * scan. During {@link #loadBeanDefinitions}, these locations will be processed in + * order, first attempting to load values as a class, and upon class loading failure + * the value is assumed to be a package to be scanned. + *

Note that this method exists primarily for compatibility with Spring's + * {@link org.springframework.web.context.ContextLoader} and that if this application + * context is being configured through an + * {@link org.springframework.context.ApplicationContextInitializer}, use of the + * {@link #register} and {@link #scan} methods are preferred. + * @see #scan(String...) + * @see #register(Class...) + * @see #setConfigLocation(String) + * @see #loadBeanDefinitions(DefaultListableBeanFactory) + */ + @Override + public void setConfigLocations(String[] locations) { + super.setConfigLocations(locations); + } + + /** + * Set the annotated classes (typically {@code @Configuration} classes) + * for this web application context. + * @see #loadBeanDefinitions(DefaultListableBeanFactory) + */ + public void register(Class... annotatedClasses) { + Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified"); + this.annotatedClasses = annotatedClasses; + } + + /** + * Set the base packages to be scanned for annotated classes. + * @see #loadBeanDefinitions(DefaultListableBeanFactory) + */ + public void scan(String... basePackages) { + Assert.notEmpty(basePackages, "At least one base package must be specified"); + this.basePackages = basePackages; + } + /** - * Register a {@link BeanDefinition} for each class specified by {@link #getConfigLocations()}, - * or scan each specified package for annotated classes. Enables the default set of - * annotation configuration post processors, such that {@code @Autowired}, - * {@code @Required}, and associated annotations can be used. - *

Configuration class bean definitions are registered with generated bean definition - * names unless the {@code value} attribute is provided to the stereotype annotation. - * @see #getConfigLocations() + * Register a {@link org.springframework.beans.factory.config.BeanDefinition} for + * any classes specified by {@link #register(Class...)} and scan any packages + * specified by {@link #scan(String...)}. + *

For any values specified by {@link #setConfigLocation(String)} or + * {@link #setConfigLocations(String[])}, attempt first to load each location as a + * class, registering a {@code BeanDefinition} if class loading is successful, + * and if class loading fails (i.e. a {@code ClassNotFoundException} is raised), + * assume the value is a package and attempt to scan it for annotated classes. + *

Enables the default set of annotation configuration post processors, such that + * {@code @Autowired}, {@code @Required}, and associated annotations can be used. + *

Configuration class bean definitions are registered with generated bean + * definition names unless the {@code value} attribute is provided to the stereotype + * annotation. + * @see #register(Class...) + * @see #scan(String...) + * @see #setConfigLocation() + * @see #setConfigLocations() * @see AnnotatedBeanDefinitionReader * @see ClassPathBeanDefinitionScanner */ @@ -87,6 +167,22 @@ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWe scanner.setScopeMetadataResolver(scopeMetadataResolver); } + if (!ObjectUtils.isEmpty(this.annotatedClasses)) { + if (logger.isInfoEnabled()) { + logger.info("Registering annotated classes: [" + + StringUtils.arrayToCommaDelimitedString(this.annotatedClasses) + "]"); + } + reader.register(this.annotatedClasses); + } + + if (!ObjectUtils.isEmpty(this.basePackages)) { + if (logger.isInfoEnabled()) { + logger.info("Scanning base packages: [" + + StringUtils.arrayToCommaDelimitedString(this.basePackages) + "]"); + } + scanner.scan(this.basePackages); + } + String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { diff --git a/org.springframework.web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java b/org.springframework.web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java index c7d7b399c0..7f0b27a0d3 100644 --- a/org.springframework.web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java +++ b/org.springframework.web/src/test/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContextTests.java @@ -16,9 +16,9 @@ package org.springframework.web.context.support; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.Assert.assertNotNull; +import org.junit.Test; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -28,8 +28,20 @@ import org.springframework.context.annotation.Configuration; public class AnnotationConfigWebApplicationContextTests { @Test - public void testSingleWellFormedConfigLocation() { - AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); + public void registerSingleClass() { + AnnotationConfigWebApplicationContext ctx = + new AnnotationConfigWebApplicationContext(); + ctx.register(Config.class); + ctx.refresh(); + + TestBean bean = ctx.getBean(TestBean.class); + assertNotNull(bean); + } + + @Test + public void configLocationWithSingleClass() { + AnnotationConfigWebApplicationContext ctx = + new AnnotationConfigWebApplicationContext(); ctx.setConfigLocation(Config.class.getName()); ctx.refresh(); @@ -41,7 +53,7 @@ public class AnnotationConfigWebApplicationContextTests { @Configuration static class Config { @Bean - public TestBean testBean() { + public TestBean myTestBean() { return new TestBean(); } }