Introduce FeatureSpecification support
Introduce FeatureSpecification interface and implementations
FeatureSpecification objects decouple the configuration of
spring container features from the concern of parsing XML
namespaces, allowing for reuse in code-based configuration
(see @Feature* annotations below).
* ComponentScanSpec
* TxAnnotationDriven
* MvcAnnotationDriven
* MvcDefaultServletHandler
* MvcResources
* MvcViewControllers
Refactor associated BeanDefinitionParsers to delegate to new impls above
The following BeanDefinitionParser implementations now deal only
with the concern of XML parsing. Validation is handled by their
corresponding FeatureSpecification object. Bean definition creation
and registration is handled by their corresponding
FeatureSpecificationExecutor type.
* ComponentScanBeanDefinitionParser
* AnnotationDrivenBeanDefinitionParser (tx)
* AnnotationDrivenBeanDefinitionParser (mvc)
* DefaultServletHandlerBeanDefinitionParser
* ResourcesBeanDefinitionParser
* ViewControllerBeanDefinitionParser
Update AopNamespaceUtils to decouple from XML (DOM API)
Methods necessary for executing TxAnnotationDriven specification
(and eventually, the AspectJAutoProxy specification) have been
added that accept boolean arguments for whether to proxy
target classes and whether to expose the proxy via threadlocal.
Methods that accepted and introspected DOM Element objects still
exist but have been deprecated.
Introduce @FeatureConfiguration classes and @Feature methods
Allow for creation and configuration of FeatureSpecification objects
at the user level. A companion for @Configuration classes allowing
for completely code-driven configuration of the Spring container.
See changes in ConfigurationClassPostProcessor for implementation
details.
See Feature*Tests for usage examples.
FeatureTestSuite in .integration-tests is a JUnit test suite designed
to aggregate all BDP and Feature* related tests for a convenient way
to confirm that Feature-related changes don't break anything.
Uncomment this test and execute from Eclipse / IDEA. Due to classpath
issues, this cannot be compiled by Ant/Ivy at the command line.
Introduce @FeatureAnnotation meta-annotation and @ComponentScan impl
@FeatureAnnotation provides an alternate mechanism for creating
and executing FeatureSpecification objects. See @ComponentScan
and its corresponding ComponentScanAnnotationParser implementation
for details. See ComponentScanAnnotationIntegrationTests for usage
examples
Introduce Default[Formatting]ConversionService implementations
Allows for convenient instantiation of ConversionService objects
containing defaults appropriate for most environments. Replaces
similar support originally in ConversionServiceFactory (which is now
deprecated). This change was justified by the need to avoid use
of FactoryBeans in @Configuration classes (such as
FormattingConversionServiceFactoryBean). It is strongly preferred
that users simply instantiate and configure the objects that underlie
our FactoryBeans. In the case of the ConversionService types, the
easiest way to do this is to create Default* subtypes. This also
follows convention with the rest of the framework.
Minor updates to util classes
All in service of changes above. See diffs for self-explanatory
details.
* BeanUtils
* ObjectUtils
* ReflectionUtils
This commit is contained in:
@@ -65,6 +65,7 @@ public class FooServiceImpl implements FooService {
|
||||
|
||||
private boolean initCalled = false;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
if (this.initCalled) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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 example.scannable;
|
||||
|
||||
|
||||
/**
|
||||
* Marker class for example.scannable package.
|
||||
*
|
||||
* @see org.springframework.context.annotation.ComponentScan#basePackageClasses()
|
||||
*/
|
||||
public class _package { }
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 example.scannable_scoped;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@MyScope(BeanDefinition.SCOPE_PROTOTYPE)
|
||||
public class CustomScopeAnnotationBean {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 example.scannable_scoped;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
|
||||
public @interface MyScope {
|
||||
String value() default BeanDefinition.SCOPE_SINGLETON;
|
||||
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
/**
|
||||
* Tests that @FeatureConfiguration classes may implement Aware interfaces,
|
||||
* such as BeanFactoryAware. This is not generally recommended but occasionally
|
||||
* useful, particularly in testing.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class BeanFactoryAwareFeatureConfigurationTests {
|
||||
@Test
|
||||
public void test() {
|
||||
ConfigurableApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(FeatureConfig.class);
|
||||
FeatureConfig fc = ctx.getBean(FeatureConfig.class);
|
||||
assertThat(fc.featureMethodWasCalled, is(true));
|
||||
assertThat(fc.gotBeanFactoryInTime, is(true));
|
||||
assertThat(fc.beanFactory, is(ctx.getBeanFactory()));
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
static class FeatureConfig implements BeanFactoryAware {
|
||||
|
||||
ConfigurableListableBeanFactory beanFactory;
|
||||
boolean featureMethodWasCalled = false;
|
||||
boolean gotBeanFactoryInTime = false;
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = (ConfigurableListableBeanFactory)beanFactory;
|
||||
}
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f() {
|
||||
this.featureMethodWasCalled = true;
|
||||
this.gotBeanFactoryInTime = (this.beanFactory != null);
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.sameInstance;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.annotation.CustomAutowireConfigurer;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.SimpleMapScope;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.ComponentScanParserTests.CustomAnnotationAutowiredBean;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.util.SerializationTestUtils;
|
||||
|
||||
import example.scannable.FooService;
|
||||
import example.scannable.ScopedProxyTestBean;
|
||||
import example.scannable_scoped.CustomScopeAnnotationBean;
|
||||
import example.scannable_scoped.MyScope;
|
||||
|
||||
/**
|
||||
* Integration tests for processing ComponentScan-annotated Configuration
|
||||
* classes.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ComponentScanAnnotationIntegrationTests {
|
||||
@Test
|
||||
public void controlScan() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.scan(example.scannable._package.class.getPackage().getName());
|
||||
ctx.refresh();
|
||||
assertThat("control scan for example.scannable package failed to register FooServiceImpl bean",
|
||||
ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void viaContextRegistration() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanAnnotatedConfig.class);
|
||||
ctx.refresh();
|
||||
ctx.getBean(ComponentScanAnnotatedConfig.class);
|
||||
ctx.getBean(TestBean.class);
|
||||
assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig"), is(true));
|
||||
assertThat("@ComponentScan annotated @Configuration class registered directly against " +
|
||||
"AnnotationConfigApplicationContext did not trigger component scanning as expected",
|
||||
ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void viaContextRegistration_WithValueAttribute() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanAnnotatedConfig_WithValueAttribute.class);
|
||||
ctx.refresh();
|
||||
ctx.getBean(ComponentScanAnnotatedConfig_WithValueAttribute.class);
|
||||
ctx.getBean(TestBean.class);
|
||||
assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig_WithValueAttribute"), is(true));
|
||||
assertThat("@ComponentScan annotated @Configuration class registered directly against " +
|
||||
"AnnotationConfigApplicationContext did not trigger component scanning as expected",
|
||||
ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void viaBeanRegistration() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerBeanDefinition("componentScanAnnotatedConfig",
|
||||
genericBeanDefinition(ComponentScanAnnotatedConfig.class).getBeanDefinition());
|
||||
bf.registerBeanDefinition("configurationClassPostProcessor",
|
||||
genericBeanDefinition(ConfigurationClassPostProcessor.class).getBeanDefinition());
|
||||
GenericApplicationContext ctx = new GenericApplicationContext(bf);
|
||||
ctx.refresh();
|
||||
ctx.getBean(ComponentScanAnnotatedConfig.class);
|
||||
ctx.getBean(TestBean.class);
|
||||
assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig"), is(true));
|
||||
assertThat("@ComponentScan annotated @Configuration class registered " +
|
||||
"as bean definition did not trigger component scanning as expected",
|
||||
ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidComponentScanDeclaration_noPackagesSpecified() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanWithNoPackagesConfig.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
fail("Expected exception when parsing @ComponentScan definition that declares no packages");
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertThat(ex.getMessage(), containsString("At least one base package must be specified"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withCustomBeanNameGenerator() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanWithBeanNameGenenerator.class);
|
||||
ctx.refresh();
|
||||
assertThat(ctx.containsBean("custom_fooServiceImpl"), is(true));
|
||||
assertThat(ctx.containsBean("fooServiceImpl"), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withScopeResolver() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithScopeResolver.class);
|
||||
// custom scope annotation makes the bean prototype scoped. subsequent calls
|
||||
// to getBean should return distinct instances.
|
||||
assertThat(ctx.getBean(CustomScopeAnnotationBean.class), not(sameInstance(ctx.getBean(CustomScopeAnnotationBean.class))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withCustomTypeFilter() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithCustomTypeFilter.class);
|
||||
CustomAnnotationAutowiredBean testBean = ctx.getBean(CustomAnnotationAutowiredBean.class);
|
||||
assertThat(testBean.getDependency(), notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withScopedProxy() throws IOException, ClassNotFoundException {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanWithScopedProxy.class);
|
||||
ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope());
|
||||
ctx.refresh();
|
||||
// should cast to the interface
|
||||
FooService bean = (FooService) ctx.getBean("scopedProxyTestBean");
|
||||
// should be dynamic proxy
|
||||
assertThat(AopUtils.isJdkDynamicProxy(bean), is(true));
|
||||
// test serializability
|
||||
assertThat(bean.foo(1), equalTo("bar"));
|
||||
FooService deserialized = (FooService) SerializationTestUtils.serializeAndDeserialize(bean);
|
||||
assertThat(deserialized, notNullValue());
|
||||
assertThat(deserialized.foo(1), equalTo("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withBasePackagesAndValueAlias() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanWithBasePackagesAndValueAlias.class);
|
||||
ctx.refresh();
|
||||
assertThat(ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses=example.scannable._package.class)
|
||||
class ComponentScanAnnotatedConfig {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan("example.scannable")
|
||||
class ComponentScanAnnotatedConfig_WithValueAttribute {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
class ComponentScanWithNoPackagesConfig { }
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages="example.scannable", nameGenerator=MyBeanNameGenerator.class)
|
||||
class ComponentScanWithBeanNameGenenerator { }
|
||||
|
||||
class MyBeanNameGenerator extends AnnotationBeanNameGenerator {
|
||||
@Override
|
||||
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
|
||||
return "custom_" + super.generateBeanName(definition, registry);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages="example.scannable_scoped", scopeResolver=MyScopeMetadataResolver.class)
|
||||
class ComponentScanWithScopeResolver { }
|
||||
|
||||
class MyScopeMetadataResolver extends AnnotationScopeMetadataResolver {
|
||||
MyScopeMetadataResolver() {
|
||||
this.scopeAnnotationType = MyScope.class;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages="org.springframework.context.annotation",
|
||||
useDefaultFilters=false,
|
||||
includeFilters=@Filter(type=FilterType.CUSTOM, value=ComponentScanParserTests.CustomTypeFilter.class),
|
||||
// exclude this class from scanning since it's in the scanned package
|
||||
excludeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=ComponentScanWithCustomTypeFilter.class))
|
||||
class ComponentScanWithCustomTypeFilter {
|
||||
@Bean
|
||||
@SuppressWarnings({ "rawtypes", "serial", "unchecked" })
|
||||
public CustomAutowireConfigurer customAutowireConfigurer() {
|
||||
CustomAutowireConfigurer cac = new CustomAutowireConfigurer();
|
||||
cac.setCustomQualifierTypes(new HashSet() {{ add(ComponentScanParserTests.CustomAnnotation.class); }});
|
||||
return cac;
|
||||
}
|
||||
|
||||
public ComponentScanParserTests.CustomAnnotationAutowiredBean testBean() {
|
||||
return new ComponentScanParserTests.CustomAnnotationAutowiredBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages="example.scannable",
|
||||
scopedProxy=ScopedProxyMode.INTERFACES,
|
||||
useDefaultFilters=false,
|
||||
includeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=ScopedProxyTestBean.class))
|
||||
class ComponentScanWithScopedProxy { }
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
value="example.scannable",
|
||||
basePackages="example.scannable",
|
||||
basePackageClasses=example.scannable._package.class)
|
||||
class ComponentScanWithBasePackagesAndValueAlias { }
|
||||
@@ -18,8 +18,7 @@ package org.springframework.context.annotation;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
|
||||
import org.springframework.context.annotation.ComponentScan.ExcludeFilter;
|
||||
import org.springframework.context.annotation.ComponentScan.IncludeFilter;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
|
||||
/**
|
||||
@@ -27,11 +26,13 @@ import org.springframework.core.type.filter.TypeFilter;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see ComponentScanAnnotationIntegrationTests
|
||||
*/
|
||||
public class ComponentScanAnnotationTests {
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
public void noop() {
|
||||
// no-op; the @ComponentScan-annotated MyConfig class below simply excercises
|
||||
// available attributes of the annotation.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,22 +40,22 @@ public class ComponentScanAnnotationTests {
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
packageOf={TestBean.class},
|
||||
basePackageClasses={TestBean.class},
|
||||
nameGenerator = DefaultBeanNameGenerator.class,
|
||||
scopedProxy = ScopedProxyMode.NO,
|
||||
scopeResolver = AnnotationScopeMetadataResolver.class,
|
||||
useDefaultFilters = false,
|
||||
resourcePattern = "**/*custom.class",
|
||||
includeFilters = {
|
||||
@IncludeFilter(type = FilterType.ANNOTATION, value = MyAnnotation.class)
|
||||
@Filter(type = FilterType.ANNOTATION, value = MyAnnotation.class)
|
||||
},
|
||||
excludeFilters = {
|
||||
@ExcludeFilter(type = FilterType.CUSTOM, value = TypeFilter.class)
|
||||
@Filter(type = FilterType.CUSTOM, value = TypeFilter.class)
|
||||
}
|
||||
)
|
||||
class MyConfig {
|
||||
|
||||
}
|
||||
|
||||
@ComponentScan(packageOf=example.scannable.NamedComponent.class)
|
||||
@ComponentScan(basePackageClasses=example.scannable.NamedComponent.class)
|
||||
class SimpleConfig { }
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ComponentScanExecutor}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ComponentScanExecutorTests {
|
||||
|
||||
private ComponentScanExecutor executor;
|
||||
private ExecutorContext executorContext;
|
||||
private DefaultListableBeanFactory bf;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.bf = new DefaultListableBeanFactory();
|
||||
this.executor = new ComponentScanExecutor();
|
||||
this.executorContext = new ExecutorContext();
|
||||
this.executorContext.setRegistry(bf);
|
||||
this.executorContext.setResourceLoader(new DefaultResourceLoader());
|
||||
this.executorContext.setEnvironment(new MockEnvironment());
|
||||
this.executorContext.setRegistrar(new SimpleComponentRegistrar(bf));
|
||||
this.executorContext.setProblemReporter(new FailFastProblemReporter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validSpec() {
|
||||
this.executor.execute(new ComponentScanSpec("example.scannable"), this.executorContext);
|
||||
assertThat(bf.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
|
||||
@Test(expected=BeanDefinitionParsingException.class)
|
||||
public void invalidSpec() {
|
||||
// ff problem reporter should throw due to no packages specified
|
||||
this.executor.execute(new ComponentScanSpec(), this.executorContext);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ComponentScanFeatureTests {
|
||||
@Test
|
||||
public void viaContextRegistration() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ComponentScanFeatureConfig.class);
|
||||
ctx.register(ComponentScanFeatureConfig.Features.class);
|
||||
ctx.refresh();
|
||||
ctx.getBean(ComponentScanFeatureConfig.class);
|
||||
ctx.getBean(TestBean.class);
|
||||
assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanFeatureConfig"), is(true));
|
||||
assertThat("@ComponentScan annotated @Configuration class registered directly against " +
|
||||
"AnnotationConfigApplicationContext did not trigger component scanning as expected",
|
||||
ctx.containsBean("fooServiceImpl"), is(true));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
//@Import(ComponentScanFeatureConfig.Features.class)
|
||||
class ComponentScanFeatureConfig {
|
||||
@FeatureConfiguration
|
||||
static class Features {
|
||||
@Feature
|
||||
public ComponentScanSpec componentScan() {
|
||||
return new ComponentScanSpec(example.scannable._package.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.util.StringUtils.arrayToCommaDelimitedString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ComponentScanSpec}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ComponentScanSpecTests {
|
||||
|
||||
private CollatingProblemReporter problemReporter;
|
||||
private ClassLoader classLoader;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
problemReporter = new CollatingProblemReporter();
|
||||
classLoader = ClassUtils.getDefaultClassLoader();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includeAnnotationConfig() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
assertThat(spec.includeAnnotationConfig(), nullValue());
|
||||
spec.includeAnnotationConfig(true);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(true));
|
||||
spec.includeAnnotationConfig(false);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(false));
|
||||
spec.includeAnnotationConfig("trUE");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(true));
|
||||
spec.includeAnnotationConfig("falSE");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(false));
|
||||
spec.includeAnnotationConfig("");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(false));
|
||||
spec.includeAnnotationConfig((String)null);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), is(false));
|
||||
spec.includeAnnotationConfig((Boolean)null);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeAnnotationConfig(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourcePattern() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
assertThat(spec.resourcePattern(), nullValue());
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.resourcePattern("**/Foo*.class");
|
||||
assertThat(spec.resourcePattern(), is("**/Foo*.class"));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.resourcePattern("");
|
||||
assertThat(spec.resourcePattern(), is("**/Foo*.class"));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.resourcePattern(null);
|
||||
assertThat(spec.resourcePattern(), is("**/Foo*.class"));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void useDefaultFilters() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
assertThat(spec.useDefaultFilters(), nullValue());
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters((Boolean)null);
|
||||
assertThat(spec.useDefaultFilters(), nullValue());
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters(true);
|
||||
assertThat(spec.useDefaultFilters(), is(true));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters(false);
|
||||
assertThat(spec.useDefaultFilters(), is(false));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters("trUE");
|
||||
assertThat(spec.useDefaultFilters(), is(true));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters("falSE");
|
||||
assertThat(spec.useDefaultFilters(), is(false));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters("");
|
||||
assertThat(spec.useDefaultFilters(), is(false));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.useDefaultFilters((String)null);
|
||||
assertThat(spec.useDefaultFilters(), is(false));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includeFilters() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.includeFilters(
|
||||
new AnnotationTypeFilter(MyAnnotation.class),
|
||||
new AssignableTypeFilter(Object.class));
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeFilters().length, is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringIncludeExcludeFilters() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.addIncludeFilter("annotation", MyAnnotation.class.getName(), classLoader);
|
||||
spec.addExcludeFilter("assignable", Object.class.getName(), classLoader);
|
||||
spec.addExcludeFilter("annotation", Override.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.includeFilters().length, is(1));
|
||||
assertThat(spec.excludeFilters().length, is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bogusStringIncludeFilter() throws IOException {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.addIncludeFilter("bogus-type", "bogus-expr", classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
assertThat(spec.includeFilters().length, is(1));
|
||||
try {
|
||||
spec.includeFilters()[0].match(null, null);
|
||||
fail("expected placholder TypeFilter to throw exception");
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exerciseFilterTypes() throws IOException {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.addIncludeFilter("aspectj", "*..Bogus", classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.addIncludeFilter("regex", ".*Foo", classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.addIncludeFilter("custom", StubTypeFilter.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.addIncludeFilter("custom", "org.NonExistentTypeFilter", classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
spec.addIncludeFilter("custom", NonNoArgResolver.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingBasePackages() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec();
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withBasePackageViaAdderMethod() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec();
|
||||
spec.addBasePackage("org.p1");
|
||||
spec.addBasePackage("org.p2");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertExactContents(spec.basePackages(), "org.p1", "org.p2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withBasePackagesViaStringConstructor() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1", "org.p2");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertExactContents(spec.basePackages(), "org.p1", "org.p2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withBasePackagesViaClassConstructor() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec(java.lang.Object.class, java.io.Closeable.class);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertExactContents(spec.basePackages(), "java.lang", "java.io");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forDelimitedPackages() {
|
||||
ComponentScanSpec spec = ComponentScanSpec.forDelimitedPackages("pkg.one,pkg.two");
|
||||
assertTrue(ObjectUtils.containsElement(spec.basePackages(), "pkg.one"));
|
||||
assertTrue(ObjectUtils.containsElement(spec.basePackages(), "pkg.two"));
|
||||
assertThat(spec.basePackages().length, is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withSomeEmptyBasePackages() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1", "", "org.p3");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertExactContents(spec.basePackages(), "org.p1", "org.p3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withAllEmptyBasePackages() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("", "", "");
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withInstanceBeanNameGenerator() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
assertThat(spec.beanNameGenerator(), nullValue());
|
||||
BeanNameGenerator bng = new DefaultBeanNameGenerator();
|
||||
spec.beanNameGenerator(bng);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.beanNameGenerator(), is(bng));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withStringBeanNameGenerator() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.beanNameGenerator(DefaultBeanNameGenerator.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.beanNameGenerator(), instanceOf(DefaultBeanNameGenerator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withInstanceScopeMetadataResolver() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
assertThat(spec.scopeMetadataResolver(), nullValue());
|
||||
ScopeMetadataResolver smr = new AnnotationScopeMetadataResolver();
|
||||
spec.scopeMetadataResolver(smr);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.scopeMetadataResolver(), is(smr));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withStringScopeMetadataResolver() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopeMetadataResolver(AnnotationScopeMetadataResolver.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.scopeMetadataResolver(), instanceOf(AnnotationScopeMetadataResolver.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNonAssignableStringScopeMetadataResolver() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopeMetadataResolver(Object.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNonExistentStringScopeMetadataResolver() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopeMetadataResolver("org.Bogus", classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNonNoArgStringScopeMetadataResolver() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopeMetadataResolver(NonNoArgResolver.class.getName(), classLoader);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withStringScopedProxyMode() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopedProxyMode("targetCLASS");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.scopedProxyMode(), is(ScopedProxyMode.TARGET_CLASS));
|
||||
spec.scopedProxyMode("interFACES");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.scopedProxyMode(), is(ScopedProxyMode.INTERFACES));
|
||||
spec.scopedProxyMode("nO");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.scopedProxyMode(), is(ScopedProxyMode.NO));
|
||||
spec.scopedProxyMode("bogus");
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
assertThat(spec.scopedProxyMode(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withScopeMetadataResolverAndScopedProxyMode() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("org.p1");
|
||||
spec.scopeMetadataResolver(new AnnotationScopeMetadataResolver());
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
spec.scopedProxyMode(ScopedProxyMode.INTERFACES);
|
||||
assertThat(spec.validate(problemReporter), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addBasePackage() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec();
|
||||
spec.addBasePackage("foo.bar");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.basePackages().length, is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addBasePackageWithConstructor() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("my.pkg");
|
||||
spec.addBasePackage("foo.bar");
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.basePackages().length, is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addExcludeFilterString() {
|
||||
ComponentScanSpec spec = new ComponentScanSpec("my.pkg");
|
||||
spec.addExcludeFilter("annotation", MyAnnotation.class.getName(), ClassUtils.getDefaultClassLoader());
|
||||
assertThat(spec.validate(problemReporter), is(true));
|
||||
assertThat(spec.excludeFilters().length, is(1));
|
||||
assertThat(spec.excludeFilters()[0], instanceOf(AnnotationTypeFilter.class));
|
||||
}
|
||||
|
||||
@Test(expected=BeanDefinitionParsingException.class)
|
||||
public void withFailFastProblemReporter() {
|
||||
new ComponentScanSpec().validate(new FailFastProblemReporter());
|
||||
}
|
||||
|
||||
private <T> void assertExactContents(T[] actual, T... expected) {
|
||||
if (actual.length >= expected.length) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertThat(
|
||||
String.format("element number %d in actual is incorrect. actual: [%s], expected: [%s]",
|
||||
i, arrayToCommaDelimitedString(actual), arrayToCommaDelimitedString(expected)),
|
||||
actual[i], equalTo(expected[i]));
|
||||
}
|
||||
}
|
||||
assertThat(String.format("actual contains incorrect number of arguments. actual: [%s], expected: [%s]",
|
||||
arrayToCommaDelimitedString(actual), arrayToCommaDelimitedString(expected)),
|
||||
actual.length, equalTo(expected.length));
|
||||
}
|
||||
|
||||
|
||||
private static class CollatingProblemReporter implements ProblemReporter {
|
||||
|
||||
private List<Problem> errors = new ArrayList<Problem>();
|
||||
|
||||
private List<Problem> warnings = new ArrayList<Problem>();
|
||||
|
||||
|
||||
public void fatal(Problem problem) {
|
||||
throw new BeanDefinitionParsingException(problem);
|
||||
}
|
||||
|
||||
public void error(Problem problem) {
|
||||
this.errors.add(problem);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Problem[] getErrors() {
|
||||
return this.errors.toArray(new Problem[this.errors.size()]);
|
||||
}
|
||||
|
||||
public void warning(Problem problem) {
|
||||
System.out.println(problem);
|
||||
this.warnings.add(problem);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Problem[] getWarnings() {
|
||||
return this.warnings.toArray(new Problem[this.warnings.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class NonNoArgResolver implements ScopeMetadataResolver {
|
||||
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private static class StubTypeFilter implements TypeFilter {
|
||||
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
|
||||
throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.sameInstance;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.context.annotation.EarlyBeanReferenceProxyCreator.FINAL_CLASS_ERROR_MESSAGE;
|
||||
import static org.springframework.context.annotation.EarlyBeanReferenceProxyCreator.MISSING_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE;
|
||||
import static org.springframework.context.annotation.EarlyBeanReferenceProxyCreator.PRIVATE_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.MethodParameter;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link EarlyBeanReferenceProxyCreator}, ensuring that
|
||||
* {@link EarlyBeanReferenceProxy} objects behave properly.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class EarlyBeanReferenceProxyCreatorTests {
|
||||
|
||||
private DefaultListableBeanFactory bf;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
bf = new DefaultListableBeanFactory();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyToStringAvoidsEagerInstantiation() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy.toString(), equalTo("EarlyBeanReferenceProxy for bean of type TestBean"));
|
||||
}
|
||||
|
||||
@Test(expected=NoSuchBeanDefinitionException.class)
|
||||
public void proxyThrowsNoSuchBeanDefinitionExceptionWhenDelegatingMethodCallToNonExistentBean() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
proxy.getName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyHashCodeAvoidsEagerInstantiation() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy.hashCode(), equalTo(System.identityHashCode(proxy)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyEqualsAvoidsEagerInstantiation() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy.equals(new Object()), is(false));
|
||||
assertThat(proxy.equals(proxy), is(true));
|
||||
|
||||
TestBean proxy2 = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy, not(sameInstance(proxy2)));
|
||||
assertThat(proxy.equals(proxy2), is(false));
|
||||
assertThat(proxy2.equals(proxy), is(false));
|
||||
assertThat(proxy2.equals(proxy2), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyFinalizeAvoidsEagerInstantiation() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
|
||||
BeanWithFinalizer proxy = (BeanWithFinalizer) pc.createProxy(descriptorFor(BeanWithFinalizer.class));
|
||||
|
||||
assertThat(BeanWithFinalizer.finalizerWasCalled, is(false));
|
||||
BeanWithFinalizer.class.getDeclaredMethod("finalize").invoke(proxy);
|
||||
assertThat(BeanWithFinalizer.finalizerWasCalled, is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyMethodsDelegateToTargetBeanCausingSingletonRegistrationIfNecessary() throws Exception {
|
||||
bf.registerBeanDefinition("testBean",
|
||||
BeanDefinitionBuilder.rootBeanDefinition(TestBean.class)
|
||||
.addPropertyValue("name", "testBeanName").getBeanDefinition());
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(bf.containsBeanDefinition("testBean"), is(true));
|
||||
assertThat(bf.containsSingleton("testBean"), is(false));
|
||||
assertThat(proxy.getName(), equalTo("testBeanName"));
|
||||
assertThat(bf.containsSingleton("testBean"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanAnnotatedMethodsReturnEarlyProxyAsWell() throws Exception {
|
||||
bf.registerBeanDefinition("componentWithInterfaceBeanMethod", new RootBeanDefinition(ComponentWithInterfaceBeanMethod.class));
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
ComponentWithInterfaceBeanMethod proxy = (ComponentWithInterfaceBeanMethod) pc.createProxy(descriptorFor(ComponentWithInterfaceBeanMethod.class));
|
||||
|
||||
ITestBean bean = proxy.aBeanMethod();
|
||||
assertThat(bean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(bf.containsBeanDefinition("componentWithInterfaceBeanMethod"), is(true));
|
||||
assertThat("calling a @Bean method on an EarlyBeanReferenceProxy object " +
|
||||
"should not cause its instantation/registration",
|
||||
bf.containsSingleton("componentWithInterfaceBeanMethod"), is(false));
|
||||
|
||||
Object obj = proxy.normalInstanceMethod();
|
||||
assertThat(bf.containsSingleton("componentWithInterfaceBeanMethod"), is(true));
|
||||
assertThat(obj, not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxiesReturnedFromBeanAnnotatedMethodsDereferenceAndDelegateToTheirTargetBean() throws Exception {
|
||||
bf.registerBeanDefinition("componentWithConcreteBeanMethod", new RootBeanDefinition(ComponentWithConcreteBeanMethod.class));
|
||||
RootBeanDefinition beanMethodBeanDef = new RootBeanDefinition();
|
||||
beanMethodBeanDef.setFactoryBeanName("componentWithConcreteBeanMethod");
|
||||
beanMethodBeanDef.setFactoryMethodName("aBeanMethod");
|
||||
bf.registerBeanDefinition("aBeanMethod", beanMethodBeanDef);
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
ComponentWithConcreteBeanMethod proxy = (ComponentWithConcreteBeanMethod) pc.createProxy(descriptorFor(ComponentWithConcreteBeanMethod.class));
|
||||
|
||||
TestBean bean = proxy.aBeanMethod();
|
||||
assertThat(bean.getName(), equalTo("concrete"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interfaceBeansAreProxied() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
ITestBean proxy = (ITestBean) pc.createProxy(descriptorFor(ITestBean.class));
|
||||
|
||||
assertThat(proxy, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(AopUtils.isCglibProxyClass(proxy.getClass()), is(true));
|
||||
assertEquals(
|
||||
"interface-based bean proxies should have Object as superclass",
|
||||
proxy.getClass().getSuperclass(), Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void concreteBeansAreProxied() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
TestBean proxy = (TestBean) pc.createProxy(descriptorFor(TestBean.class));
|
||||
|
||||
assertThat(proxy, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(AopUtils.isCglibProxyClass(proxy.getClass()), is(true));
|
||||
assertEquals(
|
||||
"concrete bean proxies should have the bean class as superclass",
|
||||
proxy.getClass().getSuperclass(), TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanAnnotatedMethodsWithInterfaceReturnTypeAreProxied() throws Exception {
|
||||
bf.registerBeanDefinition("componentWithInterfaceBeanMethod", new RootBeanDefinition(ComponentWithInterfaceBeanMethod.class));
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
ComponentWithInterfaceBeanMethod proxy = (ComponentWithInterfaceBeanMethod) pc.createProxy(descriptorFor(ComponentWithInterfaceBeanMethod.class));
|
||||
|
||||
ITestBean bean = proxy.aBeanMethod();
|
||||
assertThat(bean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(AopUtils.isCglibProxyClass(bean.getClass()), is(true));
|
||||
assertEquals(
|
||||
"interface-based bean proxies should have Object as superclass",
|
||||
bean.getClass().getSuperclass(), Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanAnnotatedMethodsWithConcreteReturnTypeAreProxied() throws Exception {
|
||||
bf.registerBeanDefinition("componentWithConcreteBeanMethod", new RootBeanDefinition(ComponentWithConcreteBeanMethod.class));
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
ComponentWithConcreteBeanMethod proxy = (ComponentWithConcreteBeanMethod) pc.createProxy(descriptorFor(ComponentWithConcreteBeanMethod.class));
|
||||
|
||||
TestBean bean = proxy.aBeanMethod();
|
||||
assertThat(bean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(AopUtils.isCglibProxyClass(bean.getClass()), is(true));
|
||||
assertEquals(
|
||||
"concrete bean proxies should have the bean class as superclass",
|
||||
bean.getClass().getSuperclass(), TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attemptToProxyClassMissingNoArgConstructorFailsGracefully() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
try {
|
||||
pc.createProxy(descriptorFor(BeanMissingNoArgConstructor.class));
|
||||
fail("expected ProxyCreationException");
|
||||
} catch(ProxyCreationException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo(format(MISSING_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE, BeanMissingNoArgConstructor.class.getName())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attemptToProxyClassWithPrivateNoArgConstructorFailsGracefully() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
try {
|
||||
pc.createProxy(descriptorFor(BeanWithPrivateNoArgConstructor.class));
|
||||
fail("expected ProxyCreationException");
|
||||
} catch(ProxyCreationException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo(format(PRIVATE_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE, BeanWithPrivateNoArgConstructor.class.getName())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attemptToProxyFinalClassFailsGracefully() throws Exception {
|
||||
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
|
||||
try {
|
||||
pc.createProxy(descriptorFor(FinalBean.class));
|
||||
fail("expected ProxyCreationException");
|
||||
} catch(ProxyCreationException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo(format(FINAL_CLASS_ERROR_MESSAGE, FinalBean.class.getName())));
|
||||
}
|
||||
}
|
||||
|
||||
private DependencyDescriptor descriptorFor(Class<?> paramType) throws Exception {
|
||||
@SuppressWarnings("unused")
|
||||
class C {
|
||||
void m(ITestBean p) { }
|
||||
void m(TestBean p) { }
|
||||
void m(BeanMissingNoArgConstructor p) { }
|
||||
void m(BeanWithPrivateNoArgConstructor p) { }
|
||||
void m(FinalBean p) { }
|
||||
void m(BeanWithFinalizer p) { }
|
||||
void m(ComponentWithConcreteBeanMethod p) { }
|
||||
void m(ComponentWithInterfaceBeanMethod p) { }
|
||||
}
|
||||
|
||||
Method targetMethod = C.class.getDeclaredMethod("m", new Class<?>[] { paramType });
|
||||
MethodParameter mp = new MethodParameter(targetMethod, 0);
|
||||
DependencyDescriptor dd = new DependencyDescriptor(mp, true, false);
|
||||
return dd;
|
||||
}
|
||||
|
||||
|
||||
static class BeanMissingNoArgConstructor {
|
||||
BeanMissingNoArgConstructor(Object o) { }
|
||||
}
|
||||
|
||||
|
||||
static class BeanWithPrivateNoArgConstructor {
|
||||
private BeanWithPrivateNoArgConstructor() { }
|
||||
}
|
||||
|
||||
|
||||
static final class FinalBean {
|
||||
}
|
||||
|
||||
|
||||
static class BeanWithFinalizer {
|
||||
static Boolean finalizerWasCalled = false;
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
finalizerWasCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ComponentWithConcreteBeanMethod {
|
||||
@Bean
|
||||
public TestBean aBeanMethod() {
|
||||
return new TestBean("concrete");
|
||||
}
|
||||
|
||||
public Object normalInstanceMethod() {
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ComponentWithInterfaceBeanMethod {
|
||||
@Bean
|
||||
public ITestBean aBeanMethod() {
|
||||
return new TestBean("interface");
|
||||
}
|
||||
|
||||
public Object normalInstanceMethod() {
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
public class FeatureConfigurationClassTests {
|
||||
|
||||
@Test(expected=FeatureMethodExecutionException.class)
|
||||
public void featureConfigurationClassesMustNotContainBeanAnnotatedMethods() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(FeatureConfigWithBeanAnnotatedMethod.class);
|
||||
ctx.refresh();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
class FeatureConfigWithBeanAnnotatedMethod {
|
||||
/**
|
||||
* This is illegal use. @FeatureConfiguration classes cannot have @Bean methods.
|
||||
*/
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* This will never get called. An exception will first be raised regarding the illegal @Bean method above.
|
||||
*/
|
||||
@Feature
|
||||
public FeatureSpecification feature() {
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:c="http://www.springframework.org/schema/c"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
|
||||
|
||||
<bean class="test.beans.TestBean" c:name="beanFromXml"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests proving that @FeatureConfiguration classes may be use @ImportResource
|
||||
* and then parameter autowire beans declared in the imported resource(s).
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureConfigurationImportResourceTests {
|
||||
|
||||
@Test
|
||||
public void importResourceFromFeatureConfiguration() {
|
||||
ApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(ImportingFeatureConfig.class);
|
||||
TestBean testBean = ctx.getBean(TestBean.class);
|
||||
assertThat(testBean.getName(), equalTo("beanFromXml"));
|
||||
|
||||
// and just quickly prove that the target of the bean proxied for the Feature method
|
||||
// is indeed the same singleton instance as the one we just pulled from the container
|
||||
ImportingFeatureConfig ifc = ctx.getBean(ImportingFeatureConfig.class);
|
||||
TestBean proxyBean = ifc.testBean;
|
||||
assertThat(proxyBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertNotSame(proxyBean, testBean);
|
||||
assertSame(((EarlyBeanReferenceProxy)proxyBean).dereferenceTargetBean(), testBean);
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
@ImportResource("org/springframework/context/annotation/FeatureConfigurationImportResourceTests-context.xml")
|
||||
static class ImportingFeatureConfig {
|
||||
TestBean testBean;
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f(TestBean testBean) {
|
||||
this.testBean = testBean;
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
/**
|
||||
* Tests proving that @Configuration classes may @Import @FeatureConfiguration
|
||||
* classes, and vice versa.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureConfigurationImportTests {
|
||||
|
||||
@Test
|
||||
public void importFeatureConfigurationFromConfiguration() {
|
||||
ConfigurableApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(ImportingConfig.class);
|
||||
ImportedFeatureConfig ifc = ctx.getBean(ImportedFeatureConfig.class);
|
||||
assertThat(
|
||||
"@FeatureConfiguration class was imported and registered " +
|
||||
"as a bean but its @Feature method was never called",
|
||||
ifc.featureMethodWasCalled, is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importConfigurationFromFeatureConfiguration() {
|
||||
ConfigurableApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(ImportingFeatureConfig.class);
|
||||
ImportingFeatureConfig ifc = ctx.getBean(ImportingFeatureConfig.class);
|
||||
ImportedConfig ic = ctx.getBean(ImportedConfig.class);
|
||||
assertThat(
|
||||
"@FeatureConfiguration class was registered directly against " +
|
||||
"the container but its @Feature method was never called",
|
||||
ifc.featureMethodWasCalled, is(true));
|
||||
assertThat(
|
||||
"@Configuration class was @Imported but its @Bean method" +
|
||||
"was never registered / called",
|
||||
ic.beanMethodWasCalled, is(true));
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Import(ImportedFeatureConfig.class)
|
||||
static class ImportingConfig {
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class ImportedFeatureConfig {
|
||||
boolean featureMethodWasCalled = false;
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f() {
|
||||
this.featureMethodWasCalled = true;
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class ImportedConfig {
|
||||
boolean beanMethodWasCalled = true;
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
this.beanMethodWasCalled = true;
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
@Import(ImportedConfig.class)
|
||||
static class ImportingFeatureConfig {
|
||||
boolean featureMethodWasCalled = false;
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f() {
|
||||
this.featureMethodWasCalled = true;
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests proving that @Feature methods may reference the product of @Bean methods.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureMethodBeanReferenceTests {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(FeatureConfig.class, Config.class);
|
||||
ctx.refresh();
|
||||
|
||||
TestBean registeredBean = ctx.getBean("testBean", TestBean.class);
|
||||
TestBean proxiedBean = ctx.getBean(FeatureConfig.class).testBean;
|
||||
|
||||
assertThat(registeredBean, not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
assertThat(proxiedBean, notNullValue());
|
||||
assertThat(proxiedBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(proxiedBean.getSpouse(), is(registeredBean.getSpouse()));
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
static class FeatureConfig {
|
||||
TestBean testBean;
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f(TestBean testBean) {
|
||||
this.testBean = testBean;
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
@Bean
|
||||
public ITestBean testBean() {
|
||||
return new TestBean(new TestBean("mySpouse"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests that @Bean methods referenced from within @Feature methods
|
||||
* get proxied early to avoid premature instantiation of actual
|
||||
* bean instances.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureMethodEarlyBeanProxyTests {
|
||||
|
||||
@Test
|
||||
public void earlyProxyCreationAndBeanRegistrationLifecycle() {
|
||||
AnnotationConfigApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(FeatureConfig.class);
|
||||
|
||||
//
|
||||
// see additional assertions in FeatureConfig#feature()
|
||||
//
|
||||
|
||||
// sanity check that all the bean definitions we expecting are present
|
||||
assertThat(ctx.getBeanFactory().containsBeanDefinition("lazyHelperBean"), is(true));
|
||||
assertThat(ctx.getBeanFactory().containsBeanDefinition("eagerHelperBean"), is(true));
|
||||
assertThat(ctx.getBeanFactory().containsBeanDefinition("lazyPassthroughBean"), is(true));
|
||||
assertThat(ctx.getBeanFactory().containsBeanDefinition("eagerPassthroughBean"), is(true));
|
||||
|
||||
|
||||
// the lazy helper bean had methods invoked during feature method execution. it should be registered
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("lazyHelperBean"), is(true));
|
||||
|
||||
// the eager helper bean had methods invoked but should be registered in any case is it is non-lazy
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("eagerHelperBean"), is(true));
|
||||
|
||||
// the lazy passthrough bean was referenced in the feature method, but never invoked. it should not be registered
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("lazyPassthroughBean"), is(false));
|
||||
|
||||
// the eager passthrough bean should be registered in any case as it is non-lazy
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("eagerPassthroughBean"), is(true));
|
||||
|
||||
|
||||
// now actually fetch all the beans. none should be proxies
|
||||
assertThat(ctx.getBean("lazyHelperBean"), not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
assertThat(ctx.getBean("eagerHelperBean"), not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
assertThat(ctx.getBean("lazyPassthroughBean"), not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
assertThat(ctx.getBean("eagerPassthroughBean"), not(instanceOf(EarlyBeanReferenceProxy.class)));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void earlyProxyBeansMayBeInterfaceBasedOrConcrete() {
|
||||
new AnnotationConfigApplicationContext(FeatureConfigReferencingNonInterfaceBeans.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
@Import(TestBeanConfig.class)
|
||||
class FeatureConfig implements BeanFactoryAware {
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = (DefaultListableBeanFactory)beanFactory;
|
||||
}
|
||||
|
||||
@Feature
|
||||
public StubSpecification feature(TestBeanConfig beans) {
|
||||
|
||||
assertThat(
|
||||
"The @Configuration class instance itself should be an early-ref proxy",
|
||||
beans, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
|
||||
// invocation of @Bean methods within @Feature methods should return proxies
|
||||
ITestBean lazyHelperBean = beans.lazyHelperBean();
|
||||
ITestBean eagerHelperBean = beans.eagerHelperBean();
|
||||
ITestBean lazyPassthroughBean = beans.lazyPassthroughBean();
|
||||
ITestBean eagerPassthroughBean = beans.eagerPassthroughBean();
|
||||
|
||||
assertThat(lazyHelperBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(eagerHelperBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(lazyPassthroughBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
assertThat(eagerPassthroughBean, instanceOf(EarlyBeanReferenceProxy.class));
|
||||
|
||||
// but at this point, the proxy instances should not have
|
||||
// been registered as singletons with the container.
|
||||
assertThat(this.beanFactory.containsSingleton("lazyHelperBean"), is(false));
|
||||
assertThat(this.beanFactory.containsSingleton("eagerHelperBean"), is(false));
|
||||
assertThat(this.beanFactory.containsSingleton("lazyPassthroughBean"), is(false));
|
||||
assertThat(this.beanFactory.containsSingleton("eagerPassthroughBean"), is(false));
|
||||
|
||||
// invoking a method on the proxy should cause it to pass through
|
||||
// to the container, instantiate the actual bean in question and
|
||||
// register that actual underlying instance as a singleton.
|
||||
assertThat(lazyHelperBean.getName(), equalTo("lazyHelper"));
|
||||
assertThat(eagerHelperBean.getName(), equalTo("eagerHelper"));
|
||||
|
||||
assertThat(this.beanFactory.containsSingleton("lazyHelperBean"), is(true));
|
||||
assertThat(this.beanFactory.containsSingleton("eagerHelperBean"), is(true));
|
||||
|
||||
// since no methods were called on the passthrough beans, they should remain
|
||||
// uncreated / unregistered.
|
||||
assertThat(this.beanFactory.containsSingleton("lazyPassthroughBean"), is(false));
|
||||
assertThat(this.beanFactory.containsSingleton("eagerPassthroughBean"), is(false));
|
||||
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
class TestBeanConfig {
|
||||
|
||||
@Lazy @Bean
|
||||
public ITestBean lazyHelperBean() {
|
||||
return new TestBean("lazyHelper");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITestBean eagerHelperBean() {
|
||||
return new TestBean("eagerHelper");
|
||||
}
|
||||
|
||||
@Lazy @Bean
|
||||
public ITestBean lazyPassthroughBean() {
|
||||
return new TestBean("lazyPassthrough");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITestBean eagerPassthroughBean() {
|
||||
return new TestBean("eagerPassthrough");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@FeatureConfiguration
|
||||
@Import(NonInterfaceBeans.class)
|
||||
class FeatureConfigReferencingNonInterfaceBeans {
|
||||
@Feature
|
||||
public FeatureSpecification feature1(NonInterfaceBeans beans) throws Throwable {
|
||||
beans.testBean();
|
||||
return new StubSpecification();
|
||||
}
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification feature2(TestBean testBean) throws Throwable {
|
||||
return new StubSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
class NonInterfaceBeans {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean("invalid");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Tests proving that @Feature methods may reference beans using @Qualifier
|
||||
* as a parameter annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class FeatureMethodQualifiedBeanReferenceTests {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(Features.class, TestBeans.class);
|
||||
ctx.refresh();
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
static class Features {
|
||||
|
||||
@Feature
|
||||
public FeatureSpecification f(@Qualifier("testBean1") ITestBean testBean) {
|
||||
assertThat(testBean.getName(), equalTo("one"));
|
||||
return new StubSpecification();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestBeans {
|
||||
@Bean
|
||||
public ITestBean testBean1() {
|
||||
return new TestBean("one");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITestBean testBean2() {
|
||||
return new TestBean("two");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.context.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.configuration.StubSpecification;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple tests to ensure that @Feature methods are invoked and that the
|
||||
* resulting returned {@link FeatureSpecification} object is delegated to
|
||||
* the correct {@link FeatureSpecificationExecutor}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class SimpleFeatureMethodProcessingTests {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(FeatureConfig.class);
|
||||
assertThat(MySpecificationExecutor.executeMethodWasCalled, is(false));
|
||||
ctx.refresh();
|
||||
assertThat(MySpecificationExecutor.executeMethodWasCalled, is(true));
|
||||
}
|
||||
|
||||
@FeatureConfiguration
|
||||
static class FeatureConfig {
|
||||
@Feature
|
||||
public FeatureSpecification f() {
|
||||
return new StubSpecification(MySpecificationExecutor.class);
|
||||
}
|
||||
}
|
||||
|
||||
static class MySpecificationExecutor implements FeatureSpecificationExecutor {
|
||||
static boolean executeMethodWasCalled = false;
|
||||
public void execute(FeatureSpecification spec, ExecutorContext executorContext) {
|
||||
Assert.state(executeMethodWasCalled == false);
|
||||
executeMethodWasCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
@@ -43,7 +45,9 @@ import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.context.annotation.configuration;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* A configuration class that registers a placeholder configurer @Bean method
|
||||
* cannot also have @Value fields. Logically, the config class must be instantiated
|
||||
* in order to invoke the placeholder configurer bean method, and it is a
|
||||
* chicken-and-egg problem to process the @Value field.
|
||||
*
|
||||
* Therefore, placeholder configurers should be put in separate configuration classes
|
||||
* as has been done in the test below. Simply said, placeholder configurer @Bean methods
|
||||
* and @Value fields in the same configuration class are mutually exclusive.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class ConfigurationClassWithPlaceholderConfigurerBeanTests {
|
||||
|
||||
/**
|
||||
* Intentionally ignored test proving that a property placeholder bean
|
||||
* cannot be declared in the same configuration class that has a @Value
|
||||
* field in need of placeholder replacement. It's an obvious chicken-and-egg issue.
|
||||
* The solution is to do as {@link #valueFieldsAreProcessedWhenPlaceholderConfigurerIsSegregated()}
|
||||
* does and segragate the two bean definitions across configuration classes.
|
||||
*/
|
||||
@Ignore @Test
|
||||
public void valueFieldsAreNotProcessedWhenPlaceholderConfigurerIsIntegrated() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ConfigWithValueFieldAndPlaceholderConfigurer.class);
|
||||
System.setProperty("test.name", "foo");
|
||||
ctx.refresh();
|
||||
System.clearProperty("test.name");
|
||||
|
||||
TestBean testBean = ctx.getBean(TestBean.class);
|
||||
assertThat(testBean.getName(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valueFieldsAreProcessedWhenPlaceholderConfigurerIsSegregated() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ConfigWithValueField.class);
|
||||
ctx.register(ConfigWithPlaceholderConfigurer.class);
|
||||
System.setProperty("test.name", "foo");
|
||||
ctx.refresh();
|
||||
System.clearProperty("test.name");
|
||||
|
||||
TestBean testBean = ctx.getBean(TestBean.class);
|
||||
assertThat(testBean.getName(), equalTo("foo"));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class ConfigWithValueField {
|
||||
|
||||
@Value("${test.name}")
|
||||
private String name;
|
||||
|
||||
@Bean
|
||||
public ITestBean testBean() {
|
||||
return new TestBean(this.name);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class ConfigWithPlaceholderConfigurer {
|
||||
|
||||
@Bean
|
||||
public PropertySourcesPlaceholderConfigurer ppc() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class ConfigWithValueFieldAndPlaceholderConfigurer {
|
||||
|
||||
@Value("${test.name}")
|
||||
private String name;
|
||||
|
||||
@Bean
|
||||
public ITestBean testBean() {
|
||||
return new TestBean(this.name);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PropertySourcesPlaceholderConfigurer ppc() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean class="org.springframework.context.annotation.configuration.ColourHolder"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.context.annotation.configuration;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
import org.springframework.context.annotation.FeatureMethodEarlyBeanProxyTests;
|
||||
|
||||
/**
|
||||
* Test suite that groups all tests related to @Feature method lifecycle issues.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({
|
||||
FeatureMethodEarlyBeanProxyTests.class,
|
||||
ConfigurationClassWithPlaceholderConfigurerBeanTests.class,
|
||||
})
|
||||
public class FeatureMethodLifecycleIssueTestSuite {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.context.annotation.configuration;
|
||||
|
||||
import org.springframework.beans.factory.parsing.SimpleProblemCollector;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
|
||||
public class StubSpecification extends AbstractFeatureSpecification {
|
||||
|
||||
public StubSpecification() {
|
||||
this(StubSpecificationExecutor.class);
|
||||
}
|
||||
|
||||
public StubSpecification(Class<? extends FeatureSpecificationExecutor> excecutorType) {
|
||||
super(excecutorType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(SimpleProblemCollector reporter) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class StubSpecificationExecutor implements FeatureSpecificationExecutor {
|
||||
|
||||
public void execute(FeatureSpecification spec, ExecutorContext executorContext) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,7 +36,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
@@ -54,7 +54,7 @@ public class JodaTimeFormattingTests {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
|
||||
JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
|
||||
registrar.registerFormatters(conversionService);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -27,7 +27,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.annotation.NumberFormat;
|
||||
import org.springframework.format.annotation.NumberFormat.Style;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
@@ -46,7 +46,7 @@ public class NumberFormattingTests {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.setEmbeddedValueResolver(new StringValueResolver() {
|
||||
public String resolveStringValue(String strVal) {
|
||||
if ("${pattern}".equals(strVal)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.format.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@@ -27,10 +30,8 @@ import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.junit.After;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
@@ -40,7 +41,7 @@ import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.datetime.joda.DateTimeParser;
|
||||
import org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory;
|
||||
import org.springframework.format.datetime.joda.ReadablePartialPrinter;
|
||||
@@ -57,7 +58,7 @@ public class FormattingConversionServiceTests {
|
||||
@Before
|
||||
public void setUp() {
|
||||
formattingService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(formattingService);
|
||||
DefaultConversionService.addDefaultConverters(formattingService);
|
||||
LocaleContextHolder.setLocale(Locale.US);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -30,8 +30,6 @@ import java.util.TreeSet;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeanWithObjectProperty;
|
||||
import org.springframework.beans.DerivedTestBean;
|
||||
import org.springframework.beans.ITestBean;
|
||||
@@ -46,7 +44,7 @@ import org.springframework.beans.propertyeditors.CustomNumberEditor;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.support.ResourceBundleMessageSource;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.number.NumberFormatter;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -319,7 +317,7 @@ public class DataBinderTests extends TestCase {
|
||||
TestBean tb = new TestBean();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
@@ -350,7 +348,7 @@ public class DataBinderTests extends TestCase {
|
||||
TestBean tb = new TestBean();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
@@ -372,7 +370,7 @@ public class DataBinderTests extends TestCase {
|
||||
BeanWithIntegerList tb = new BeanWithIntegerList();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
@@ -393,7 +391,7 @@ public class DataBinderTests extends TestCase {
|
||||
BeanWithIntegerList tb = new BeanWithIntegerList();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
@@ -415,7 +413,7 @@ public class DataBinderTests extends TestCase {
|
||||
TestBean tb = new TestBean();
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
binder.initDirectFieldAccess();
|
||||
@@ -448,7 +446,7 @@ public class DataBinderTests extends TestCase {
|
||||
DataBinder binder = new DataBinder(tb);
|
||||
binder.initDirectFieldAccess();
|
||||
FormattingConversionService conversionService = new FormattingConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
DefaultConversionService.addDefaultConverters(conversionService);
|
||||
conversionService.addFormatterForFieldType(Float.class, new NumberFormatter());
|
||||
binder.setConversionService(conversionService);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
@@ -553,7 +551,7 @@ public class DataBinderTests extends TestCase {
|
||||
assertEquals(1, disallowedFields.length);
|
||||
assertEquals("age", disallowedFields[0]);
|
||||
|
||||
Map m = binder.getBindingResult().getModel();
|
||||
Map<?,?> m = binder.getBindingResult().getModel();
|
||||
assertTrue("There is one element in map", m.size() == 2);
|
||||
TestBean tb = (TestBean) m.get("person");
|
||||
assertTrue("Same object", tb.equals(rod));
|
||||
@@ -1415,6 +1413,7 @@ public class DataBinderTests extends TestCase {
|
||||
assertEquals("badName", nameError.getCode());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testBindingWithResortedList() {
|
||||
IndexedTestBean tb = new IndexedTestBean();
|
||||
DataBinder binder = new DataBinder(tb, "tb");
|
||||
|
||||
Reference in New Issue
Block a user