Introduce AnnotationConfigWAC #scan and #register
Primarily for use in conjunction with ApplicationContextInitializer, these new #scan and #register methods mirror those in AnnotationConfigApplicationContext. #setConfigLocation and #setConfigLocations methods remain for compatibility with ContextLoader-style initialization, but have been locally overridden and documented clearly. AnnotationConfigWebApplicationContext#loadBeanDefinitions Javadoc has also been updated to explain the processing logic for each of these potential inputs. Issue: SPR-8320
This commit is contained in:
parent
56720fc42c
commit
e128ee2464
|
|
@ -16,12 +16,14 @@
|
||||||
|
|
||||||
package org.springframework.web.context.support;
|
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.BeanNameGenerator;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||||
import org.springframework.context.annotation.ScopeMetadataResolver;
|
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
|
* {@link org.springframework.web.context.WebApplicationContext} implementation
|
||||||
|
|
@ -57,14 +59,92 @@ import org.springframework.context.annotation.ScopeMetadataResolver;
|
||||||
*/
|
*/
|
||||||
public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext {
|
public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext {
|
||||||
|
|
||||||
|
private Class<?>[] annotatedClasses;
|
||||||
|
|
||||||
|
private String[] basePackages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a {@link BeanDefinition} for each class specified by {@link #getConfigLocations()},
|
* {@inheritDoc}
|
||||||
* or scan each specified package for annotated classes. Enables the default set of
|
* <p>This implementation accepts delimited values in the form of fully-qualified
|
||||||
* annotation configuration post processors, such that {@code @Autowired},
|
* class names, (typically of {@code Configuration} classes) or fully-qualified
|
||||||
* {@code @Required}, and associated annotations can be used.
|
* packages to scan for annotated classes. During {@link #loadBeanDefinitions}, these
|
||||||
* <p>Configuration class bean definitions are registered with generated bean definition
|
* locations will be processed in their given order, first attempting to load each
|
||||||
* names unless the {@code value} attribute is provided to the stereotype annotation.
|
* value as a class. If class loading fails (i.e. a {@code ClassNotFoundException}
|
||||||
* @see #getConfigLocations()
|
* occurs), the value is assumed to be a package and scanning is attempted.
|
||||||
|
* <p>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}
|
||||||
|
* <p>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.
|
||||||
|
* <p>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 org.springframework.beans.factory.config.BeanDefinition} for
|
||||||
|
* any classes specified by {@link #register(Class...)} and scan any packages
|
||||||
|
* specified by {@link #scan(String...)}.
|
||||||
|
* <p>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.
|
||||||
|
* <p>Enables the default set of annotation configuration post processors, such that
|
||||||
|
* {@code @Autowired}, {@code @Required}, and associated annotations can be used.
|
||||||
|
* <p>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 AnnotatedBeanDefinitionReader
|
||||||
* @see ClassPathBeanDefinitionScanner
|
* @see ClassPathBeanDefinitionScanner
|
||||||
*/
|
*/
|
||||||
|
|
@ -87,6 +167,22 @@ public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWe
|
||||||
scanner.setScopeMetadataResolver(scopeMetadataResolver);
|
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();
|
String[] configLocations = getConfigLocations();
|
||||||
if (configLocations != null) {
|
if (configLocations != null) {
|
||||||
for (String configLocation : configLocations) {
|
for (String configLocation : configLocations) {
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@
|
||||||
|
|
||||||
package org.springframework.web.context.support;
|
package org.springframework.web.context.support;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
|
@ -28,8 +28,20 @@ import org.springframework.context.annotation.Configuration;
|
||||||
public class AnnotationConfigWebApplicationContextTests {
|
public class AnnotationConfigWebApplicationContextTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSingleWellFormedConfigLocation() {
|
public void registerSingleClass() {
|
||||||
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
|
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.setConfigLocation(Config.class.getName());
|
||||||
ctx.refresh();
|
ctx.refresh();
|
||||||
|
|
||||||
|
|
@ -41,7 +53,7 @@ public class AnnotationConfigWebApplicationContextTests {
|
||||||
@Configuration
|
@Configuration
|
||||||
static class Config {
|
static class Config {
|
||||||
@Bean
|
@Bean
|
||||||
public TestBean testBean() {
|
public TestBean myTestBean() {
|
||||||
return new TestBean();
|
return new TestBean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue