Remove "Feature" support introduced in 3.1 M1

Feature-related support such as @Feature, @FeatureConfiguration,
and FeatureSpecification types will be replaced by framework-provided
@Configuration classes and convenience annotations such as
@ComponentScan (already exists), @EnableAsync, @EnableScheduling,
@EnableTransactionManagement and others.

Issue: SPR-8012,SPR-8034,SPR-8039,SPR-8188,SPR-8206,SPR-8223,
SPR-8225,SPR-8226,SPR-8227
This commit is contained in:
Chris Beams
2011-05-06 19:03:52 +00:00
parent 0a790c143f
commit 111fb71fe1
80 changed files with 857 additions and 6737 deletions

View File

@@ -1,67 +0,0 @@
/*
* 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();
}
}
}

View File

@@ -1,67 +0,0 @@
/*
* 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.SpecificationContext;
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 SpecificationContext specificationContext;
private DefaultListableBeanFactory bf;
@Before
public void setUp() {
this.bf = new DefaultListableBeanFactory();
this.executor = new ComponentScanExecutor();
this.specificationContext = new SpecificationContext();
this.specificationContext.setRegistry(bf);
this.specificationContext.setResourceLoader(new DefaultResourceLoader());
this.specificationContext.setEnvironment(new MockEnvironment());
this.specificationContext.setRegistrar(new SimpleComponentRegistrar(bf));
this.specificationContext.setProblemReporter(new FailFastProblemReporter());
}
@Test
public void validSpec() {
this.executor.execute(new ComponentScanSpec("example.scannable"), this.specificationContext);
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.specificationContext);
}
}

View File

@@ -1,55 +0,0 @@
/*
* 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();
}
}

View File

@@ -1,411 +0,0 @@
/*
* 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();
}
}
}

View File

@@ -1,312 +0,0 @@
/*
* 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.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.createProxyIfPossible(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.createProxyIfPossible(descriptorFor(TestBean.class));
proxy.getName();
}
@Test
public void proxyHashCodeAvoidsEagerInstantiation() throws Exception {
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
TestBean proxy = (TestBean) pc.createProxyIfPossible(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.createProxyIfPossible(descriptorFor(TestBean.class));
assertThat(proxy.equals(new Object()), is(false));
assertThat(proxy.equals(proxy), is(true));
TestBean proxy2 = (TestBean) pc.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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.createProxyIfPossible(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 attemptToProxyFinalClassReturnsNonProxiedInstance() throws Exception {
bf.registerBeanDefinition("finalBean", new RootBeanDefinition(FinalBean.class));
EarlyBeanReferenceProxyCreator pc = new EarlyBeanReferenceProxyCreator(bf);
Object bean = pc.createProxyIfPossible(descriptorFor(FinalBean.class));
assertThat(bean, instanceOf(FinalBean.class));
assertThat(bean, not(instanceOf(EarlyBeanReferenceProxy.class)));
}
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();
}
}
}

View File

@@ -1,83 +0,0 @@
/*
* 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.junit.Assert.assertThat;
import org.junit.Test;
import org.springframework.context.annotation.configuration.StubSpecification;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.mock.env.MockEnvironment;
public class FeatureConfigurationClassTests {
@Test(expected=FeatureMethodExecutionException.class)
public void featureConfigurationClassesMustNotContainBeanAnnotatedMethods() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(FeatureConfigWithBeanAnnotatedMethod.class);
ctx.refresh();
}
@Test
public void featureMethodsMayAcceptResourceLoaderParameter() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.setDisplayName("enclosing app ctx");
ctx.setEnvironment(new MockEnvironment().withProperty("foo", "bar"));
ctx.register(FeatureMethodWithResourceLoaderParameter.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();
}
}
@FeatureConfiguration
class FeatureMethodWithResourceLoaderParameter {
@Feature
public FeatureSpecification feature(ResourceLoader rl,
Environment e) {
// prove that the injected Environment is that of the enclosing app context
assertThat(e.getProperty("foo"), is("bar"));
// prove that the injected ResourceLoader is actually the enclosing application context
Object target = ((EarlyBeanReferenceProxy)rl).dereferenceTargetBean();
assertThat(target, instanceOf(AnnotationConfigApplicationContext.class));
assertThat(((AnnotationConfigApplicationContext)target).getDisplayName(), is("enclosing app ctx"));
return new StubSpecification();
}
}

View File

@@ -1,10 +0,0 @@
<?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>

View File

@@ -1,69 +0,0 @@
/*
* 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();
}
}
}

View File

@@ -1,105 +0,0 @@
/*
* 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();
}
}
}

View File

@@ -1,76 +0,0 @@
/*
* 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"));
}
}
}

View File

@@ -1,192 +0,0 @@
/*
* 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");
}
}

View File

@@ -1,113 +0,0 @@
/*
* 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 static org.junit.Assert.fail;
import org.junit.Test;
import org.springframework.context.annotation.configuration.StubSpecification;
import org.springframework.context.config.FeatureSpecification;
import test.beans.TestBean;
/**
* Tests proving that @Feature methods may reference the product of @Bean methods.
*
* @author Chris Beams
* @since 3.1
*/
public class FeatureMethodErrorTests {
@Test
public void incorrectReturnType() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(FeatureConfig.class);
try {
ctx.refresh();
fail("expected exception");
} catch (FeatureMethodExecutionException ex) {
assertThat(ex.getCause().getMessage(),
equalTo("Return type for @Feature method FeatureConfig.f() must be " +
"assignable to FeatureSpecification"));
}
}
@Test
public void voidReturnType() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(VoidFeatureConfig.class);
try {
ctx.refresh();
fail("expected exception");
} catch (FeatureMethodExecutionException ex) {
assertThat(ex.getCause().getMessage(),
equalTo("Return type for @Feature method VoidFeatureConfig.f() must be " +
"assignable to FeatureSpecification"));
}
}
@Test
public void containsBeanMethod() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(FeatureConfigWithBeanMethod.class);
try {
ctx.refresh();
fail("expected exception");
} catch (FeatureMethodExecutionException ex) {
assertThat(ex.getMessage(),
equalTo("@FeatureConfiguration classes must not contain @Bean-annotated methods. " +
"FeatureConfigWithBeanMethod.testBean() is annotated with @Bean and must " +
"be removed in order to proceed. Consider moving this method into a dedicated " +
"@Configuration class and injecting the bean as a parameter into any @Feature " +
"method(s) that need it."));
}
}
@FeatureConfiguration
static class FeatureConfig {
@Feature
public Object f() {
return new StubSpecification();
}
}
@FeatureConfiguration
static class VoidFeatureConfig {
@Feature
public void f() {
}
}
@FeatureConfiguration
static class FeatureConfigWithBeanMethod {
@Feature
public FeatureSpecification f() {
return new StubSpecification();
}
@Bean
public TestBean testBean() {
return new TestBean();
}
}
}

View File

@@ -1,70 +0,0 @@
/*
* 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");
}
}
}

View File

@@ -1,135 +0,0 @@
/*
* 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.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.configuration.StubSpecification;
/**
* Tests ensuring that @Feature methods can accept @Value-annoted
* parameters, particularly String types. SPR-7974 revealed this
* was failing due to attempting to proxy objects of type String,
* which cannot be done.
*
* @author Chris Beams
*/
public class FeatureMethodValueInjectionTests {
@Test
public void control() {
System.setProperty("foo", "bar");
System.setProperty("num", "2");
Config config = new AnnotationConfigApplicationContext(Config.class).getBean(Config.class);
System.clearProperty("foo");
System.clearProperty("num");
assertThat(config.foo, is("bar"));
assertThat(config.num, is(2));
}
@Test
public void spelValueInjection() {
System.setProperty("foo", "bar");
new AnnotationConfigApplicationContext(SpelValueInjectionFeatureConfig.class);
System.clearProperty("foo");
}
@Test
public void spelIntValueInjection() {
System.setProperty("num", "5");
new AnnotationConfigApplicationContext(SpelIntValueInjectionFeatureConfig.class);
System.clearProperty("num");
}
@Test
public void stringBeanInjection() {
new AnnotationConfigApplicationContext(StringBeanConfig.class, StringBeanInjectionByTypeFeatureConfig.class);
}
@Test
public void qualifiedStringBeanInjection() {
new AnnotationConfigApplicationContext(StringBeanSubConfig.class, StringBeanInjectionByQualifierFeatureConfig.class);
}
@FeatureConfiguration
static class SpelValueInjectionFeatureConfig {
@Feature
public StubSpecification feature(@Value("#{environment['foo']}") String foo) {
return new StubSpecification();
}
}
@FeatureConfiguration
static class SpelIntValueInjectionFeatureConfig {
@Feature
public StubSpecification feature(@Value("#{environment['num']}") int num) {
assertThat(num, is(5));
return new StubSpecification();
}
}
@Configuration
static class StringBeanConfig {
@Bean
public String stringBean() {
return "sb";
}
}
@Configuration
static class StringBeanSubConfig extends StringBeanConfig {
@Bean
public String stringBean2() {
return "sb2";
}
}
@FeatureConfiguration
static class StringBeanInjectionByTypeFeatureConfig {
@Feature
public StubSpecification feature(String string) {
assertThat(string, is("sb"));
return new StubSpecification();
}
}
@FeatureConfiguration
static class StringBeanInjectionByQualifierFeatureConfig {
@Feature
public StubSpecification feature(@Qualifier("stringBean2") String string) {
assertThat(string, is("sb2"));
return new StubSpecification();
}
}
@Configuration
static class Config {
@Value("#{environment['foo']}") String foo;
@Value("#{environment['num']}") int num;
}
}

View File

@@ -1,64 +0,0 @@
/*
* 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.SpecificationContext;
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, SpecificationContext specificationContext) {
Assert.state(executeMethodWasCalled == false);
executeMethodWasCalled = true;
}
}
}

View File

@@ -1,8 +0,0 @@
<?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>

View File

@@ -1,37 +0,0 @@
/*
* 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 {
}

View File

@@ -1,46 +0,0 @@
/*
* 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.ProblemCollector;
import org.springframework.context.config.AbstractFeatureSpecification;
import org.springframework.context.config.FeatureSpecification;
import org.springframework.context.config.FeatureSpecificationExecutor;
import org.springframework.context.config.SpecificationContext;
public class StubSpecification extends AbstractFeatureSpecification {
public StubSpecification() {
this(StubSpecificationExecutor.class);
}
public StubSpecification(Class<? extends FeatureSpecificationExecutor> excecutorType) {
super(excecutorType);
}
@Override
protected void doValidate(ProblemCollector problems) {
}
}
class StubSpecificationExecutor implements FeatureSpecificationExecutor {
public void execute(FeatureSpecification spec, SpecificationContext specificationContext) {
}
}