diff --git a/org.springframework.beans/.classpath b/org.springframework.beans/.classpath
index 3ffedda5269..32041b1cea4 100644
--- a/org.springframework.beans/.classpath
+++ b/org.springframework.beans/.classpath
@@ -8,6 +8,7 @@
+
diff --git a/org.springframework.beans/beans.iml b/org.springframework.beans/beans.iml
index 3c72a061f01..4a343bb3961 100644
--- a/org.springframework.beans/beans.iml
+++ b/org.springframework.beans/beans.iml
@@ -1,28 +1,29 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.springframework.beans/ivy.xml b/org.springframework.beans/ivy.xml
index bbd5a1baa2a..bf9e51672fa 100644
--- a/org.springframework.beans/ivy.xml
+++ b/org.springframework.beans/ivy.xml
@@ -21,6 +21,7 @@
+
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
index d60a75cec78..fdfe35e7c9f 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
@@ -83,6 +83,8 @@ import org.springframework.util.ReflectionUtils;
* a special case of such a general config method. Such config methods
* do not have to be public.
*
+ * Also supports JSR-330's {@link javax.inject.Inject} annotation, if available.
+ *
*
Note: A default AutowiredAnnotationBeanPostProcessor will be registered
* by the "context:annotation-config" and "context:component-scan" XML tags.
* Remove or turn off the default annotation configuration there if you intend
@@ -100,8 +102,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
protected final Log logger = LogFactory.getLog(AutowiredAnnotationBeanPostProcessor.class);
- @SuppressWarnings("unchecked")
- private Class extends Annotation>[] autowiredAnnotationTypes = new Class[] {Autowired.class, Value.class};
+ private final Set> autowiredAnnotationTypes =
+ new LinkedHashSet>();
private String requiredParameterName = "required";
@@ -119,20 +121,24 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
/**
- * Set the 'autowired' annotation types, to be used on constructors, fields,
- * setter methods and arbitrary config methods.
- * The default autowired annotation type is the Spring-provided
- * {@link Autowired} annotation, as well as {@link Value} and raw
- * use of the {@link Qualifier} annotation.
- *
This setter property exists so that developers can provide their own
- * (non-Spring-specific) annotation types to indicate that a member is
- * supposed to be autowired.
+ * Create a new AutowiredAnnotationBeanPostProcessor
+ * for Spring's standard {@link Autowired} annotation.
+ *
Also supports JSR-330's {@link javax.inject.Inject} annotation, if available.
*/
- public void setAutowiredAnnotationTypes(Class extends Annotation>[] autowiredAnnotationTypes) {
- Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty");
- this.autowiredAnnotationTypes = autowiredAnnotationTypes;
+ @SuppressWarnings("unchecked")
+ public AutowiredAnnotationBeanPostProcessor() {
+ this.autowiredAnnotationTypes.add(Autowired.class);
+ this.autowiredAnnotationTypes.add(Value.class);
+ ClassLoader cl = AutowiredAnnotationBeanPostProcessor.class.getClassLoader();
+ try {
+ this.autowiredAnnotationTypes.add((Class extends Annotation>) cl.loadClass("javax.inject.Inject"));
+ }
+ catch (ClassNotFoundException ex) {
+ // JSR-330 API not available - simply skip.
+ }
}
+
/**
* Set the 'autowired' annotation type, to be used on constructors, fields,
* setter methods and arbitrary config methods.
@@ -142,10 +148,26 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
* (non-Spring-specific) annotation type to indicate that a member is
* supposed to be autowired.
*/
- @SuppressWarnings("unchecked")
public void setAutowiredAnnotationType(Class extends Annotation> autowiredAnnotationType) {
Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null");
- this.autowiredAnnotationTypes = new Class[] {autowiredAnnotationType};
+ this.autowiredAnnotationTypes.clear();
+ this.autowiredAnnotationTypes.add(autowiredAnnotationType);
+ }
+
+ /**
+ * Set the 'autowired' annotation types, to be used on constructors, fields,
+ * setter methods and arbitrary config methods.
+ *
The default autowired annotation type is the Spring-provided
+ * {@link Autowired} annotation, as well as {@link Value} and raw
+ * use of the {@link Qualifier} annotation.
+ *
This setter property exists so that developers can provide their own
+ * (non-Spring-specific) annotation types to indicate that a member is
+ * supposed to be autowired.
+ */
+ public void setAutowiredAnnotationTypes(Set> autowiredAnnotationTypes) {
+ Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty");
+ this.autowiredAnnotationTypes.clear();
+ this.autowiredAnnotationTypes.addAll(autowiredAnnotationTypes);
}
/**
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
index 72068cdb156..4654f4a46e1 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
@@ -18,7 +18,7 @@ package org.springframework.beans.factory.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@@ -38,9 +38,11 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
- * {@link AutowireCandidateResolver} implementation that matches bean definition
- * qualifiers against qualifier annotations on the field or parameter to be autowired.
- * Also supports suggested expression values through a value annotation.
+ * {@link AutowireCandidateResolver} implementation that matches bean definition qualifiers
+ * against {@link Qualifier qualifier annotations} on the field or parameter to be autowired.
+ * Also supports suggested expression values through a {@link Value value} annotation.
+ *
+ * Also supports JSR-330's {@link javax.inject.Qualifier} annotation, if available.
*
* @author Mark Fisher
* @author Juergen Hoeller
@@ -51,7 +53,7 @@ import org.springframework.util.ObjectUtils;
*/
public class QualifierAnnotationAutowireCandidateResolver implements AutowireCandidateResolver, BeanFactoryAware {
- private final Set> qualifierTypes;
+ private final Set> qualifierTypes = new LinkedHashSet>();
private Class extends Annotation> valueAnnotationType = Value.class;
@@ -61,10 +63,18 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
/**
* Create a new QualifierAnnotationAutowireCandidateResolver
* for Spring's standard {@link Qualifier} annotation.
+ * Also supports JSR-330's {@link javax.inject.Qualifier} annotation, if available.
*/
+ @SuppressWarnings("unchecked")
public QualifierAnnotationAutowireCandidateResolver() {
- this.qualifierTypes = new HashSet>(1);
this.qualifierTypes.add(Qualifier.class);
+ ClassLoader cl = QualifierAnnotationAutowireCandidateResolver.class.getClassLoader();
+ try {
+ this.qualifierTypes.add((Class extends Annotation>) cl.loadClass("javax.inject.Qualifier"));
+ }
+ catch (ClassNotFoundException ex) {
+ // JSR-330 API not available - simply skip.
+ }
}
/**
@@ -74,7 +84,6 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
*/
public QualifierAnnotationAutowireCandidateResolver(Class extends Annotation> qualifierType) {
Assert.notNull(qualifierType, "'qualifierType' must not be null");
- this.qualifierTypes = new HashSet>(1);
this.qualifierTypes.add(qualifierType);
}
@@ -85,7 +94,7 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
*/
public QualifierAnnotationAutowireCandidateResolver(Set> qualifierTypes) {
Assert.notNull(qualifierTypes, "'qualifierTypes' must not be null");
- this.qualifierTypes = new HashSet>(qualifierTypes);
+ this.qualifierTypes.addAll(qualifierTypes);
}
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java
index 84aaf252259..0f30540e676 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2009 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.
@@ -43,6 +43,7 @@ public interface AutowireCandidateResolver {
* @param descriptor the descriptor for the target method parameter or field
* @return the value suggested (typically an expression String),
* or null if none found
+ * @since 3.0
*/
Object getSuggestedValue(DependencyDescriptor descriptor);
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
index 52211c2fd20..2ae21e45431 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
@@ -33,6 +33,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import javax.inject.Provider;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
@@ -642,9 +643,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
- if (ObjectFactory.class.equals(descriptor.getDependencyType())) {
+ if (descriptor.getDependencyType().equals(ObjectFactory.class)) {
return new DependencyObjectFactory(descriptor, beanName);
}
+ else if (descriptor.getDependencyType().getName().equals("javax.inject.Provider")) {
+ return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName);
+ }
else {
return doResolveDependency(descriptor, descriptor.getDependencyType(), beanName, autowiredBeanNames, typeConverter);
}
@@ -961,4 +965,30 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
+
+ /**
+ * Serializable ObjectFactory for lazy resolution of a dependency.
+ */
+ private class DependencyProvider extends DependencyObjectFactory implements Provider {
+
+ public DependencyProvider(DependencyDescriptor descriptor, String beanName) {
+ super(descriptor, beanName);
+ }
+
+ public Object get() throws BeansException {
+ return getObject();
+ }
+ }
+
+
+ /**
+ * Separate inner class for avoiding a hard dependency on the javax.inject API.
+ */
+ private class DependencyProviderFactory {
+
+ public Object createDependencyProvider(DependencyDescriptor descriptor, String beanName) {
+ return new DependencyProvider(descriptor, beanName);
+ }
+ }
+
}
diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
index 4e21315ecdc..fba1691903b 100644
--- a/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
+++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
@@ -573,6 +573,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
@Test
public void testObjectFactoryQualifierInjection() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
@@ -580,6 +581,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
bf.registerBeanDefinition("testBean", bd);
+ bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
assertSame(bf.getBean("testBean"), bean.getTestBean());
@@ -1282,7 +1284,7 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
public static class ObjectFactoryQualifierInjectionBean {
@Autowired
- //@Qualifier("testBean")
+ @Qualifier("testBean")
private ObjectFactory> testBeanFactory;
public TestBean getTestBean() {
diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java
new file mode 100644
index 00000000000..0bf98607551
--- /dev/null
+++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java
@@ -0,0 +1,867 @@
+/*
+ * Copyright 2002-2009 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.beans.factory.annotation;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+import test.beans.ITestBean;
+import test.beans.IndexedTestBean;
+import test.beans.NestedTestBean;
+import test.beans.TestBean;
+
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.AutowireCandidateQualifier;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.util.SerializationTestUtils;
+
+/**
+ * Unit tests for {@link org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor}
+ * processing the JSR-303 {@link javax.inject.Inject} annotation.
+ *
+ * @author Juergen Hoeller
+ * @since 3.0
+ */
+public class InjectAnnotationBeanPostProcessorTests {
+
+ @Test
+ public void testIncompleteBeanDefinition() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("testBean", new GenericBeanDefinition());
+ try {
+ bf.getBean("testBean");
+ }
+ catch (BeanCreationException ex) {
+ assertTrue(ex.getRootCause() instanceof IllegalStateException);
+ }
+ }
+
+ @Test
+ public void testResourceInjection() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ RootBeanDefinition bd = new RootBeanDefinition(ResourceInjectionBean.class);
+ bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
+ bf.registerBeanDefinition("annotatedBean", bd);
+ TestBean tb = new TestBean();
+ bf.registerSingleton("testBean", tb);
+
+ ResourceInjectionBean bean = (ResourceInjectionBean) bf.getBean("annotatedBean");
+ assertSame(tb, bean.getTestBean());
+ assertSame(tb, bean.getTestBean2());
+
+ bean = (ResourceInjectionBean) bf.getBean("annotatedBean");
+ assertSame(tb, bean.getTestBean());
+ assertSame(tb, bean.getTestBean2());
+ }
+
+ @Test
+ public void testExtendedResourceInjection() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerResolvableDependency(BeanFactory.class, bf);
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class);
+ bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
+ bf.registerBeanDefinition("annotatedBean", bd);
+ TestBean tb = new TestBean();
+ bf.registerSingleton("testBean", tb);
+ NestedTestBean ntb = new NestedTestBean();
+ bf.registerSingleton("nestedTestBean", ntb);
+
+ TypedExtendedResourceInjectionBean bean = (TypedExtendedResourceInjectionBean) bf.getBean("annotatedBean");
+ assertSame(tb, bean.getTestBean());
+ assertSame(tb, bean.getTestBean2());
+ assertSame(tb, bean.getTestBean3());
+ assertSame(tb, bean.getTestBean4());
+ assertSame(ntb, bean.getNestedTestBean());
+ assertSame(bf, bean.getBeanFactory());
+
+ bean = (TypedExtendedResourceInjectionBean) bf.getBean("annotatedBean");
+ assertSame(tb, bean.getTestBean());
+ assertSame(tb, bean.getTestBean2());
+ assertSame(tb, bean.getTestBean3());
+ assertSame(tb, bean.getTestBean4());
+ assertSame(ntb, bean.getNestedTestBean());
+ assertSame(bf, bean.getBeanFactory());
+ }
+
+ @Test
+ public void testExtendedResourceInjectionWithOverriding() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerResolvableDependency(BeanFactory.class, bf);
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ RootBeanDefinition annotatedBd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class);
+ TestBean tb2 = new TestBean();
+ annotatedBd.getPropertyValues().addPropertyValue("testBean2", tb2);
+ bf.registerBeanDefinition("annotatedBean", annotatedBd);
+ TestBean tb = new TestBean();
+ bf.registerSingleton("testBean", tb);
+ NestedTestBean ntb = new NestedTestBean();
+ bf.registerSingleton("nestedTestBean", ntb);
+
+ TypedExtendedResourceInjectionBean bean = (TypedExtendedResourceInjectionBean) bf.getBean("annotatedBean");
+ assertSame(tb, bean.getTestBean());
+ assertSame(tb2, bean.getTestBean2());
+ assertSame(tb, bean.getTestBean3());
+ assertSame(tb, bean.getTestBean4());
+ assertSame(ntb, bean.getNestedTestBean());
+ assertSame(bf, bean.getBeanFactory());
+ bf.destroySingletons();
+ }
+
+ @Test
+ public void testExtendedResourceInjectionWithAtRequired() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerResolvableDependency(BeanFactory.class, bf);
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.addBeanPostProcessor(new RequiredAnnotationBeanPostProcessor());
+ RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class);
+ bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
+ bf.registerBeanDefinition("annotatedBean", bd);
+ TestBean tb = new TestBean();
+ bf.registerSingleton("testBean", tb);
+ NestedTestBean ntb = new NestedTestBean();
+ bf.registerSingleton("nestedTestBean", ntb);
+
+ TypedExtendedResourceInjectionBean bean = (TypedExtendedResourceInjectionBean) bf.getBean("annotatedBean");
+ assertSame(tb, bean.getTestBean());
+ assertSame(tb, bean.getTestBean2());
+ assertSame(tb, bean.getTestBean3());
+ assertSame(tb, bean.getTestBean4());
+ assertSame(ntb, bean.getNestedTestBean());
+ assertSame(bf, bean.getBeanFactory());
+ }
+
+ @Test
+ public void testConstructorResourceInjection() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerResolvableDependency(BeanFactory.class, bf);
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class);
+ bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
+ bf.registerBeanDefinition("annotatedBean", bd);
+ TestBean tb = new TestBean();
+ bf.registerSingleton("testBean", tb);
+ NestedTestBean ntb = new NestedTestBean();
+ bf.registerSingleton("nestedTestBean", ntb);
+
+ ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
+ assertSame(tb, bean.getTestBean());
+ assertSame(tb, bean.getTestBean2());
+ assertSame(tb, bean.getTestBean3());
+ assertSame(tb, bean.getTestBean4());
+ assertSame(ntb, bean.getNestedTestBean());
+ assertSame(bf, bean.getBeanFactory());
+
+ bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
+ assertSame(tb, bean.getTestBean());
+ assertSame(tb, bean.getTestBean2());
+ assertSame(tb, bean.getTestBean3());
+ assertSame(tb, bean.getTestBean4());
+ assertSame(ntb, bean.getNestedTestBean());
+ assertSame(bf, bean.getBeanFactory());
+ }
+
+ @Test
+ public void testConstructorResourceInjectionWithMultipleCandidatesAsCollection() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(
+ ConstructorsCollectionResourceInjectionBean.class));
+ TestBean tb = new TestBean();
+ bf.registerSingleton("testBean", tb);
+ NestedTestBean ntb1 = new NestedTestBean();
+ bf.registerSingleton("nestedTestBean1", ntb1);
+ NestedTestBean ntb2 = new NestedTestBean();
+ bf.registerSingleton("nestedTestBean2", ntb2);
+
+ ConstructorsCollectionResourceInjectionBean bean = (ConstructorsCollectionResourceInjectionBean) bf.getBean("annotatedBean");
+ assertNull(bean.getTestBean3());
+ assertSame(tb, bean.getTestBean4());
+ assertEquals(2, bean.getNestedTestBeans().size());
+ assertSame(ntb1, bean.getNestedTestBeans().get(0));
+ assertSame(ntb2, bean.getNestedTestBeans().get(1));
+ bf.destroySingletons();
+ }
+
+ @Test
+ public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ConstructorsResourceInjectionBean.class));
+ TestBean tb = new TestBean();
+ bf.registerSingleton("testBean", tb);
+
+ ConstructorsResourceInjectionBean bean = (ConstructorsResourceInjectionBean) bf.getBean("annotatedBean");
+ assertSame(tb, bean.getTestBean3());
+ assertNull(bean.getTestBean4());
+ bf.destroySingletons();
+ }
+
+ @Test
+ public void testConstructorInjectionWithMap() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ RootBeanDefinition bd = new RootBeanDefinition(MapConstructorInjectionBean.class);
+ bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
+ bf.registerBeanDefinition("annotatedBean", bd);
+ TestBean tb1 = new TestBean();
+ TestBean tb2 = new TestBean();
+ bf.registerSingleton("testBean1", tb1);
+ bf.registerSingleton("testBean2", tb1);
+
+ MapConstructorInjectionBean bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean");
+ assertEquals(2, bean.getTestBeanMap().size());
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean1"));
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean2"));
+ assertTrue(bean.getTestBeanMap().values().contains(tb1));
+ assertTrue(bean.getTestBeanMap().values().contains(tb2));
+
+ bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean");
+ assertEquals(2, bean.getTestBeanMap().size());
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean1"));
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean2"));
+ assertTrue(bean.getTestBeanMap().values().contains(tb1));
+ assertTrue(bean.getTestBeanMap().values().contains(tb2));
+ }
+
+ @Test
+ public void testFieldInjectionWithMap() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ RootBeanDefinition bd = new RootBeanDefinition(MapFieldInjectionBean.class);
+ bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
+ bf.registerBeanDefinition("annotatedBean", bd);
+ TestBean tb1 = new TestBean();
+ TestBean tb2 = new TestBean();
+ bf.registerSingleton("testBean1", tb1);
+ bf.registerSingleton("testBean2", tb1);
+
+ MapFieldInjectionBean bean = (MapFieldInjectionBean) bf.getBean("annotatedBean");
+ assertEquals(2, bean.getTestBeanMap().size());
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean1"));
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean2"));
+ assertTrue(bean.getTestBeanMap().values().contains(tb1));
+ assertTrue(bean.getTestBeanMap().values().contains(tb2));
+
+ bean = (MapFieldInjectionBean) bf.getBean("annotatedBean");
+ assertEquals(2, bean.getTestBeanMap().size());
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean1"));
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean2"));
+ assertTrue(bean.getTestBeanMap().values().contains(tb1));
+ assertTrue(bean.getTestBeanMap().values().contains(tb2));
+ }
+
+ @Test
+ public void testMethodInjectionWithMap() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ RootBeanDefinition bd = new RootBeanDefinition(MapMethodInjectionBean.class);
+ bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
+ bf.registerBeanDefinition("annotatedBean", bd);
+ TestBean tb = new TestBean();
+ bf.registerSingleton("testBean", tb);
+
+ MapMethodInjectionBean bean = (MapMethodInjectionBean) bf.getBean("annotatedBean");
+ assertEquals(1, bean.getTestBeanMap().size());
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean"));
+ assertTrue(bean.getTestBeanMap().values().contains(tb));
+ assertSame(tb, bean.getTestBean());
+
+ bean = (MapMethodInjectionBean) bf.getBean("annotatedBean");
+ assertEquals(1, bean.getTestBeanMap().size());
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean"));
+ assertTrue(bean.getTestBeanMap().values().contains(tb));
+ assertSame(tb, bean.getTestBean());
+ }
+
+ @Test
+ public void testMethodInjectionWithMapAndMultipleMatches() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(MapMethodInjectionBean.class));
+ bf.registerBeanDefinition("testBean1", new RootBeanDefinition(TestBean.class));
+ bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
+
+ try {
+ bf.getBean("annotatedBean");
+ fail("should have failed, more than one bean of type");
+ }
+ catch (BeanCreationException e) {
+ // expected
+ }
+ bf.destroySingletons();
+ }
+
+ @Test
+ public void testMethodInjectionWithMapAndMultipleMatchesButOnlyOneAutowireCandidate() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(MapMethodInjectionBean.class));
+ bf.registerBeanDefinition("testBean1", new RootBeanDefinition(TestBean.class));
+ RootBeanDefinition rbd2 = new RootBeanDefinition(TestBean.class);
+ rbd2.setAutowireCandidate(false);
+ bf.registerBeanDefinition("testBean2", rbd2);
+
+ MapMethodInjectionBean bean = (MapMethodInjectionBean) bf.getBean("annotatedBean");
+ TestBean tb = (TestBean) bf.getBean("testBean1");
+ assertEquals(1, bean.getTestBeanMap().size());
+ assertTrue(bean.getTestBeanMap().keySet().contains("testBean1"));
+ assertTrue(bean.getTestBeanMap().values().contains(tb));
+ assertSame(tb, bean.getTestBean());
+ bf.destroySingletons();
+ }
+
+ @Test
+ public void testObjectFactoryInjection() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
+ RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
+ bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
+ bf.registerBeanDefinition("testBean", bd);
+ bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
+
+ ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
+ assertSame(bf.getBean("testBean"), bean.getTestBean());
+ bf.destroySingletons();
+ }
+
+ @Test
+ public void testObjectFactoryQualifierInjection() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
+ RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
+ bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
+ bf.registerBeanDefinition("testBean", bd);
+
+ ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
+ assertSame(bf.getBean("testBean"), bean.getTestBean());
+ bf.destroySingletons();
+ }
+
+ @Test
+ public void testObjectFactorySerialization() throws Exception {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryInjectionBean.class));
+ bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
+ bf.setSerializationId("test");
+
+ ObjectFactoryInjectionBean bean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
+ assertSame(bf.getBean("testBean"), bean.getTestBean());
+ bean = (ObjectFactoryInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
+ assertSame(bf.getBean("testBean"), bean.getTestBean());
+ bf.destroySingletons();
+ }
+
+ /**
+ * Verifies that a dependency on a {@link org.springframework.beans.factory.FactoryBean} can be autowired via
+ * {@link org.springframework.beans.factory.annotation.Autowired @Inject}, specifically addressing the JIRA issue
+ * raised in SPR-4040.
+ */
+ @Test
+ public void testBeanAutowiredWithFactoryBean() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
+ bpp.setBeanFactory(bf);
+ bf.addBeanPostProcessor(bpp);
+ bf.registerBeanDefinition("factoryBeanDependentBean", new RootBeanDefinition(FactoryBeanDependentBean.class));
+ bf.registerSingleton("stringFactoryBean", new StringFactoryBean());
+
+ final StringFactoryBean factoryBean = (StringFactoryBean) bf.getBean("&stringFactoryBean");
+ final FactoryBeanDependentBean bean = (FactoryBeanDependentBean) bf.getBean("factoryBeanDependentBean");
+
+ assertNotNull("The singleton StringFactoryBean should have been registered.", factoryBean);
+ assertNotNull("The factoryBeanDependentBean should have been registered.", bean);
+ assertEquals("The FactoryBeanDependentBean should have been autowired 'by type' with the StringFactoryBean.",
+ factoryBean, bean.getFactoryBean());
+
+ bf.destroySingletons();
+ }
+
+
+ public static class ResourceInjectionBean {
+
+ @Inject
+ private TestBean testBean;
+
+ private TestBean testBean2;
+
+
+ @Inject
+ public void setTestBean2(TestBean testBean2) {
+ if (this.testBean2 != null) {
+ throw new IllegalStateException("Already called");
+ }
+ this.testBean2 = testBean2;
+ }
+
+ public TestBean getTestBean() {
+ return this.testBean;
+ }
+
+ public TestBean getTestBean2() {
+ return this.testBean2;
+ }
+ }
+
+
+ public static class ExtendedResourceInjectionBean extends ResourceInjectionBean {
+
+ @Inject
+ protected ITestBean testBean3;
+
+ private T nestedTestBean;
+
+ private ITestBean testBean4;
+
+ private BeanFactory beanFactory;
+
+ public ExtendedResourceInjectionBean() {
+ }
+
+ @Inject @Required
+ public void setTestBean2(TestBean testBean2) {
+ super.setTestBean2(testBean2);
+ }
+
+ @Inject
+ private void inject(ITestBean testBean4, T nestedTestBean) {
+ this.testBean4 = testBean4;
+ this.nestedTestBean = nestedTestBean;
+ }
+
+ @Inject
+ protected void initBeanFactory(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+ public ITestBean getTestBean3() {
+ return this.testBean3;
+ }
+
+ public ITestBean getTestBean4() {
+ return this.testBean4;
+ }
+
+ public T getNestedTestBean() {
+ return this.nestedTestBean;
+ }
+
+ public BeanFactory getBeanFactory() {
+ return this.beanFactory;
+ }
+ }
+
+
+ public static class TypedExtendedResourceInjectionBean extends ExtendedResourceInjectionBean {
+
+ }
+
+
+ public static class OptionalResourceInjectionBean extends ResourceInjectionBean {
+
+ @Inject
+ protected ITestBean testBean3;
+
+ private IndexedTestBean indexedTestBean;
+
+ private NestedTestBean[] nestedTestBeans;
+
+ @Inject
+ public NestedTestBean[] nestedTestBeansField;
+
+ private ITestBean testBean4;
+
+ @Inject
+ public void setTestBean2(TestBean testBean2) {
+ super.setTestBean2(testBean2);
+ }
+
+ @Inject
+ private void inject(ITestBean testBean4, NestedTestBean[] nestedTestBeans, IndexedTestBean indexedTestBean) {
+ this.testBean4 = testBean4;
+ this.indexedTestBean = indexedTestBean;
+ this.nestedTestBeans = nestedTestBeans;
+ }
+
+ public ITestBean getTestBean3() {
+ return this.testBean3;
+ }
+
+ public ITestBean getTestBean4() {
+ return this.testBean4;
+ }
+
+ public IndexedTestBean getIndexedTestBean() {
+ return this.indexedTestBean;
+ }
+
+ public NestedTestBean[] getNestedTestBeans() {
+ return this.nestedTestBeans;
+ }
+ }
+
+
+ public static class OptionalCollectionResourceInjectionBean extends ResourceInjectionBean {
+
+ @Inject
+ protected ITestBean testBean3;
+
+ private IndexedTestBean indexedTestBean;
+
+ private List nestedTestBeans;
+
+ public List nestedTestBeansSetter;
+
+ @Inject
+ public List nestedTestBeansField;
+
+ private ITestBean testBean4;
+
+ @Inject
+ public void setTestBean2(TestBean testBean2) {
+ super.setTestBean2(testBean2);
+ }
+
+ @Inject
+ private void inject(ITestBean testBean4, List nestedTestBeans, IndexedTestBean indexedTestBean) {
+ this.testBean4 = testBean4;
+ this.indexedTestBean = indexedTestBean;
+ this.nestedTestBeans = nestedTestBeans;
+ }
+
+ @Inject
+ public void setNestedTestBeans(List nestedTestBeans) {
+ this.nestedTestBeansSetter = nestedTestBeans;
+ }
+
+ public ITestBean getTestBean3() {
+ return this.testBean3;
+ }
+
+ public ITestBean getTestBean4() {
+ return this.testBean4;
+ }
+
+ public IndexedTestBean getIndexedTestBean() {
+ return this.indexedTestBean;
+ }
+
+ public List getNestedTestBeans() {
+ return this.nestedTestBeans;
+ }
+ }
+
+
+ public static class ConstructorResourceInjectionBean extends ResourceInjectionBean {
+
+ @Inject
+ protected ITestBean testBean3;
+
+ private ITestBean testBean4;
+
+ private NestedTestBean nestedTestBean;
+
+ private ConfigurableListableBeanFactory beanFactory;
+
+
+ public ConstructorResourceInjectionBean() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ConstructorResourceInjectionBean(ITestBean testBean3) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Inject
+ public ConstructorResourceInjectionBean(ITestBean testBean4, NestedTestBean nestedTestBean,
+ ConfigurableListableBeanFactory beanFactory) {
+ this.testBean4 = testBean4;
+ this.nestedTestBean = nestedTestBean;
+ this.beanFactory = beanFactory;
+ }
+
+ public ConstructorResourceInjectionBean(NestedTestBean nestedTestBean) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ConstructorResourceInjectionBean(ITestBean testBean3, ITestBean testBean4, NestedTestBean nestedTestBean) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Inject
+ public void setTestBean2(TestBean testBean2) {
+ super.setTestBean2(testBean2);
+ }
+
+ public ITestBean getTestBean3() {
+ return this.testBean3;
+ }
+
+ public ITestBean getTestBean4() {
+ return this.testBean4;
+ }
+
+ public NestedTestBean getNestedTestBean() {
+ return this.nestedTestBean;
+ }
+
+ public ConfigurableListableBeanFactory getBeanFactory() {
+ return this.beanFactory;
+ }
+ }
+
+
+ public static class ConstructorsResourceInjectionBean {
+
+ protected ITestBean testBean3;
+
+ private ITestBean testBean4;
+
+ private NestedTestBean[] nestedTestBeans;
+
+ public ConstructorsResourceInjectionBean() {
+ }
+
+ @Inject
+ public ConstructorsResourceInjectionBean(ITestBean testBean3) {
+ this.testBean3 = testBean3;
+ }
+
+ public ConstructorsResourceInjectionBean(ITestBean testBean4, NestedTestBean[] nestedTestBeans) {
+ this.testBean4 = testBean4;
+ this.nestedTestBeans = nestedTestBeans;
+ }
+
+ public ConstructorsResourceInjectionBean(NestedTestBean nestedTestBean) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ConstructorsResourceInjectionBean(ITestBean testBean3, ITestBean testBean4, NestedTestBean nestedTestBean) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ITestBean getTestBean3() {
+ return this.testBean3;
+ }
+
+ public ITestBean getTestBean4() {
+ return this.testBean4;
+ }
+
+ public NestedTestBean[] getNestedTestBeans() {
+ return this.nestedTestBeans;
+ }
+ }
+
+
+ public static class ConstructorsCollectionResourceInjectionBean {
+
+ protected ITestBean testBean3;
+
+ private ITestBean testBean4;
+
+ private List nestedTestBeans;
+
+ public ConstructorsCollectionResourceInjectionBean() {
+ }
+
+ public ConstructorsCollectionResourceInjectionBean(ITestBean testBean3) {
+ this.testBean3 = testBean3;
+ }
+
+ @Inject
+ public ConstructorsCollectionResourceInjectionBean(ITestBean testBean4, List nestedTestBeans) {
+ this.testBean4 = testBean4;
+ this.nestedTestBeans = nestedTestBeans;
+ }
+
+ public ConstructorsCollectionResourceInjectionBean(NestedTestBean nestedTestBean) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ConstructorsCollectionResourceInjectionBean(ITestBean testBean3, ITestBean testBean4,
+ NestedTestBean nestedTestBean) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ITestBean getTestBean3() {
+ return this.testBean3;
+ }
+
+ public ITestBean getTestBean4() {
+ return this.testBean4;
+ }
+
+ public List getNestedTestBeans() {
+ return this.nestedTestBeans;
+ }
+ }
+
+
+ public static class MapConstructorInjectionBean {
+
+ private Map testBeanMap;
+
+ @Inject
+ public MapConstructorInjectionBean(Map testBeanMap) {
+ this.testBeanMap = testBeanMap;
+ }
+
+ public Map getTestBeanMap() {
+ return this.testBeanMap;
+ }
+ }
+
+
+ public static class MapFieldInjectionBean {
+
+ @Inject
+ private Map testBeanMap;
+
+
+ public Map getTestBeanMap() {
+ return this.testBeanMap;
+ }
+ }
+
+
+ public static class MapMethodInjectionBean {
+
+ private TestBean testBean;
+
+ private Map testBeanMap;
+
+ @Inject
+ public void setTestBeanMap(TestBean testBean, Map testBeanMap) {
+ this.testBean = testBean;
+ this.testBeanMap = testBeanMap;
+ }
+
+ public TestBean getTestBean() {
+ return this.testBean;
+ }
+
+ public Map getTestBeanMap() {
+ return this.testBeanMap;
+ }
+ }
+
+
+ public static class ObjectFactoryInjectionBean implements Serializable {
+
+ @Inject
+ private Provider testBeanFactory;
+
+ public TestBean getTestBean() {
+ return this.testBeanFactory.get();
+ }
+ }
+
+
+ public static class ObjectFactoryQualifierInjectionBean {
+
+ @Inject
+ @Named("testBean")
+ private Provider> testBeanFactory;
+
+ public TestBean getTestBean() {
+ return (TestBean) this.testBeanFactory.get();
+ }
+ }
+
+
+ /**
+ * Bean with a dependency on a {@link org.springframework.beans.factory.FactoryBean}.
+ */
+ private static class FactoryBeanDependentBean {
+
+ @Inject
+ private FactoryBean> factoryBean;
+
+ public final FactoryBean> getFactoryBean() {
+ return this.factoryBean;
+ }
+ }
+
+
+ public static class StringFactoryBean implements FactoryBean {
+
+ public String getObject() throws Exception {
+ return "";
+ }
+
+ public Class getObjectType() {
+ return String.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+ }
+
+}
diff --git a/org.springframework.beans/template.mf b/org.springframework.beans/template.mf
index b7ffe9ccd3a..3078108f9ce 100644
--- a/org.springframework.beans/template.mf
+++ b/org.springframework.beans/template.mf
@@ -4,6 +4,7 @@ Bundle-Vendor: SpringSource
Bundle-ManifestVersion: 2
Import-Template:
javax.el.*;version="[1.0.0, 2.0.0)";resolution:=optional,
+ javax.inject.*;version="[0.9.0, 2.0.0)";resolution:=optional,
javax.xml.*;version="0";resolution:=optional,
net.sf.cglib.*;version="[2.1.3, 2.2.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
diff --git a/org.springframework.context/.classpath b/org.springframework.context/.classpath
index 08d728bbd0a..769d9eed842 100644
--- a/org.springframework.context/.classpath
+++ b/org.springframework.context/.classpath
@@ -18,6 +18,7 @@
+
diff --git a/org.springframework.context/context.iml b/org.springframework.context/context.iml
index 6a929d18fa7..ca85d7c64bc 100644
--- a/org.springframework.context/context.iml
+++ b/org.springframework.context/context.iml
@@ -1,168 +1,169 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.springframework.context/ivy.xml b/org.springframework.context/ivy.xml
index ee322654a0e..2d5a59eaa06 100644
--- a/org.springframework.context/ivy.xml
+++ b/org.springframework.context/ivy.xml
@@ -35,9 +35,10 @@
+
-
+
@@ -45,7 +46,6 @@
-
@@ -60,6 +60,7 @@
+
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java
index 235e7668716..70c45036f05 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2009 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.
@@ -39,6 +39,8 @@ import org.springframework.util.StringUtils;
* themselves annotated with
* {@link org.springframework.stereotype.Component @Component}.
*
+ *
Also supports JSR-330's {@link javax.inject.Named} annotation, if available.
+ *
*
If the annotation's value doesn't indicate a bean name, an appropriate
* name will be built based on the short name of the class (with the first
* letter lower-cased). For example:
@@ -52,6 +54,7 @@ import org.springframework.util.StringUtils;
* @see org.springframework.stereotype.Repository#value()
* @see org.springframework.stereotype.Service#value()
* @see org.springframework.stereotype.Controller#value()
+ * @see javax.inject.Named#value()
*/
public class AnnotationBeanNameGenerator implements BeanNameGenerator {
@@ -107,7 +110,8 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
Set metaAnnotationTypes, Map attributes) {
boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
- (metaAnnotationTypes != null && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME));
+ (metaAnnotationTypes != null && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME)) ||
+ annotationType.equals("javax.inject.Named");
return (isStereotype && attributes != null && attributes.containsKey("value"));
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
index b15210e8e45..2428e935890 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
@@ -199,6 +199,8 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
for (String basePackage : basePackages) {
Set candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
+ ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
+ candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
@@ -219,8 +221,7 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
- ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
- definitionHolder = applyScope(definitionHolder, scopeMetadata);
+ definitionHolder = applyScopedProxyMode(definitionHolder, scopeMetadata);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
@@ -301,19 +302,17 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
/**
* Apply the specified scope to the given bean definition.
- * @param definitionHolder the bean definition to configure
- * @param scopeMetadata the corresponding scope metadata
+ * @param definition the bean definition to configure
+ * @param metadata the corresponding scope metadata
* @return the final bean definition to use (potentially a proxy)
*/
- private BeanDefinitionHolder applyScope(BeanDefinitionHolder definitionHolder, ScopeMetadata scopeMetadata) {
- String scope = scopeMetadata.getScopeName();
- ScopedProxyMode scopedProxyMode = scopeMetadata.getScopedProxyMode();
- definitionHolder.getBeanDefinition().setScope(scope);
+ private BeanDefinitionHolder applyScopedProxyMode(BeanDefinitionHolder definition, ScopeMetadata metadata) {
+ ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
- return definitionHolder;
+ return definition;
}
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
- return ScopedProxyCreator.createScopedProxy(definitionHolder, this.registry, proxyTargetClass);
+ return ScopedProxyCreator.createScopedProxy(definition, this.registry, proxyTargetClass);
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
index 4578bab663d..b2bb113ad78 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
@@ -17,6 +17,7 @@
package org.springframework.context.annotation;
import java.io.IOException;
+import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
@@ -164,8 +165,20 @@ public class ClassPathScanningCandidateComponentProvider implements ResourceLoad
* {@link Repository @Repository}, {@link Service @Service}, and
* {@link Controller @Controller} stereotype annotations.
*/
+ @SuppressWarnings("unchecked")
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
+ this.includeFilters.add(new AnnotationTypeFilter(Scope.class));
+ ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
+ try {
+ this.includeFilters.add(new AnnotationTypeFilter(
+ ((Class extends Annotation>) cl.loadClass("javax.inject.Named"))));
+ this.includeFilters.add(new AnnotationTypeFilter(
+ ((Class extends Annotation>) cl.loadClass("javax.inject.Scope"))));
+ }
+ catch (ClassNotFoundException ex) {
+ // JSR-330 API not available - simply skip.
+ }
}
diff --git a/org.springframework.context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java b/org.springframework.context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java
new file mode 100644
index 00000000000..92921dbfb19
--- /dev/null
+++ b/org.springframework.context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java
@@ -0,0 +1,691 @@
+/*
+ * Copyright 2002-2009 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.beans.factory.support;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Qualifier;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+import org.springframework.aop.scope.ScopedProxyUtils;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.UnsatisfiedDependencyException;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.context.annotation.AnnotationConfigUtils;
+import org.springframework.context.support.GenericApplicationContext;
+
+/**
+ * Integration tests for handling JSR-303 {@link javax.inject.Qualifier} annotations.
+ *
+ * @author Juergen Hoeller
+ * @since 3.0
+ */
+public class InjectAnnotationAutowireContextTests {
+
+ private static final String JUERGEN = "juergen";
+
+ private static final String MARK = "mark";
+
+
+ @Test
+ public void testAutowiredFieldWithSingleNonQualifiedCandidate() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs = new ConstructorArgumentValues();
+ cavs.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null);
+ context.registerBeanDefinition(JUERGEN, person);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedFieldTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ try {
+ context.refresh();
+ fail("expected BeanCreationException");
+ }
+ catch (BeanCreationException e) {
+ assertTrue(e.getRootCause() instanceof NoSuchBeanDefinitionException);
+ assertEquals("autowired", e.getBeanName());
+ }
+ }
+
+ @Test
+ public void testAutowiredMethodParameterWithSingleNonQualifiedCandidate() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs = new ConstructorArgumentValues();
+ cavs.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null);
+ context.registerBeanDefinition(JUERGEN, person);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ try {
+ context.refresh();
+ fail("expected BeanCreationException");
+ }
+ catch (BeanCreationException e) {
+ assertTrue(e.getRootCause() instanceof NoSuchBeanDefinitionException);
+ assertEquals("autowired", e.getBeanName());
+ }
+ }
+
+ @Test
+ public void testAutowiredConstructorArgumentWithSingleNonQualifiedCandidate() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs = new ConstructorArgumentValues();
+ cavs.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null);
+ context.registerBeanDefinition(JUERGEN, person);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ try {
+ context.refresh();
+ fail("expected BeanCreationException");
+ }
+ catch (BeanCreationException e) {
+ assertTrue(e instanceof UnsatisfiedDependencyException);
+ assertEquals("autowired", e.getBeanName());
+ }
+ }
+
+ @Test
+ public void testAutowiredFieldWithSingleQualifiedCandidate() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs = new ConstructorArgumentValues();
+ cavs.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null);
+ person.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
+ context.registerBeanDefinition(JUERGEN, person);
+ context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedFieldTestBean bean = (QualifiedFieldTestBean) context.getBean("autowired");
+ assertEquals(JUERGEN, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredMethodParameterWithSingleQualifiedCandidate() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs = new ConstructorArgumentValues();
+ cavs.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null);
+ person.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
+ context.registerBeanDefinition(JUERGEN, person);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedMethodParameterTestBean bean =
+ (QualifiedMethodParameterTestBean) context.getBean("autowired");
+ assertEquals(JUERGEN, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredMethodParameterWithStaticallyQualifiedCandidate() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs = new ConstructorArgumentValues();
+ cavs.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person = new RootBeanDefinition(QualifiedPerson.class, cavs, null);
+ context.registerBeanDefinition(JUERGEN,
+ ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(person, JUERGEN), context, true).getBeanDefinition());
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedMethodParameterTestBean bean =
+ (QualifiedMethodParameterTestBean) context.getBean("autowired");
+ assertEquals(JUERGEN, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredMethodParameterWithStaticallyQualifiedCandidateAmongOthers() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs = new ConstructorArgumentValues();
+ cavs.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person = new RootBeanDefinition(QualifiedPerson.class, cavs, null);
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ context.registerBeanDefinition(JUERGEN, person);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedMethodParameterTestBean bean =
+ (QualifiedMethodParameterTestBean) context.getBean("autowired");
+ assertEquals(JUERGEN, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredConstructorArgumentWithSingleQualifiedCandidate() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs = new ConstructorArgumentValues();
+ cavs.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null);
+ person.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
+ context.registerBeanDefinition(JUERGEN, person);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedConstructorArgumentTestBean bean =
+ (QualifiedConstructorArgumentTestBean) context.getBean("autowired");
+ assertEquals(JUERGEN, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredFieldWithMultipleNonQualifiedCandidates() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedFieldTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ try {
+ context.refresh();
+ fail("expected BeanCreationException");
+ }
+ catch (BeanCreationException e) {
+ assertTrue(e.getRootCause() instanceof NoSuchBeanDefinitionException);
+ assertEquals("autowired", e.getBeanName());
+ }
+ }
+
+ @Test
+ public void testAutowiredMethodParameterWithMultipleNonQualifiedCandidates() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ try {
+ context.refresh();
+ fail("expected BeanCreationException");
+ }
+ catch (BeanCreationException e) {
+ assertTrue(e.getRootCause() instanceof NoSuchBeanDefinitionException);
+ assertEquals("autowired", e.getBeanName());
+ }
+ }
+
+ @Test
+ public void testAutowiredConstructorArgumentWithMultipleNonQualifiedCandidates() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ try {
+ context.refresh();
+ fail("expected BeanCreationException");
+ }
+ catch (BeanCreationException e) {
+ assertTrue(e instanceof UnsatisfiedDependencyException);
+ assertEquals("autowired", e.getBeanName());
+ }
+ }
+
+ @Test
+ public void testAutowiredFieldResolvesQualifiedCandidate() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ person1.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedFieldTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedFieldTestBean bean = (QualifiedFieldTestBean) context.getBean("autowired");
+ assertEquals(JUERGEN, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredMethodParameterResolvesQualifiedCandidate() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ person1.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedMethodParameterTestBean bean =
+ (QualifiedMethodParameterTestBean) context.getBean("autowired");
+ assertEquals(JUERGEN, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredConstructorArgumentResolvesQualifiedCandidate() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ person1.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedConstructorArgumentTestBean bean =
+ (QualifiedConstructorArgumentTestBean) context.getBean("autowired");
+ assertEquals(JUERGEN, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredFieldResolvesQualifiedCandidateWithDefaultValueAndNoValueOnBeanDefinition() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ // qualifier added, but includes no value
+ person1.addQualifier(new AutowireCandidateQualifier(TestQualifierWithDefaultValue.class));
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedFieldWithDefaultValueTestBean bean =
+ (QualifiedFieldWithDefaultValueTestBean) context.getBean("autowired");
+ assertEquals(JUERGEN, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredFieldDoesNotResolveCandidateWithDefaultValueAndConflictingValueOnBeanDefinition() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ // qualifier added, and non-default value specified
+ person1.addQualifier(new AutowireCandidateQualifier(TestQualifierWithDefaultValue.class, "not the default"));
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ try {
+ context.refresh();
+ fail("expected BeanCreationException");
+ }
+ catch (BeanCreationException e) {
+ assertTrue(e.getRootCause() instanceof NoSuchBeanDefinitionException);
+ assertEquals("autowired", e.getBeanName());
+ }
+ }
+
+ @Test
+ public void testAutowiredFieldResolvesWithDefaultValueAndExplicitDefaultValueOnBeanDefinition() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ // qualifier added, and value matches the default
+ person1.addQualifier(new AutowireCandidateQualifier(TestQualifierWithDefaultValue.class, "default"));
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedFieldWithDefaultValueTestBean bean =
+ (QualifiedFieldWithDefaultValueTestBean) context.getBean("autowired");
+ assertEquals(JUERGEN, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredFieldResolvesWithMultipleQualifierValues() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(TestQualifierWithMultipleAttributes.class);
+ qualifier.setAttribute("number", 456);
+ person1.addQualifier(qualifier);
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ AutowireCandidateQualifier qualifier2 = new AutowireCandidateQualifier(TestQualifierWithMultipleAttributes.class);
+ qualifier2.setAttribute("number", 123);
+ person2.addQualifier(qualifier2);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedFieldWithMultipleAttributesTestBean bean =
+ (QualifiedFieldWithMultipleAttributesTestBean) context.getBean("autowired");
+ assertEquals(MARK, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredFieldDoesNotResolveWithMultipleQualifierValuesAndConflictingDefaultValue() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(TestQualifierWithMultipleAttributes.class);
+ qualifier.setAttribute("number", 456);
+ person1.addQualifier(qualifier);
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ AutowireCandidateQualifier qualifier2 = new AutowireCandidateQualifier(TestQualifierWithMultipleAttributes.class);
+ qualifier2.setAttribute("number", 123);
+ qualifier2.setAttribute("value", "not the default");
+ person2.addQualifier(qualifier2);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ try {
+ context.refresh();
+ fail("expected BeanCreationException");
+ }
+ catch (BeanCreationException e) {
+ assertTrue(e.getRootCause() instanceof NoSuchBeanDefinitionException);
+ assertEquals("autowired", e.getBeanName());
+ }
+ }
+
+ @Test
+ public void testAutowiredFieldResolvesWithMultipleQualifierValuesAndExplicitDefaultValue() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(TestQualifierWithMultipleAttributes.class);
+ qualifier.setAttribute("number", 456);
+ person1.addQualifier(qualifier);
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ AutowireCandidateQualifier qualifier2 = new AutowireCandidateQualifier(TestQualifierWithMultipleAttributes.class);
+ qualifier2.setAttribute("number", 123);
+ qualifier2.setAttribute("value", "default");
+ person2.addQualifier(qualifier2);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ context.refresh();
+ QualifiedFieldWithMultipleAttributesTestBean bean =
+ (QualifiedFieldWithMultipleAttributesTestBean) context.getBean("autowired");
+ assertEquals(MARK, bean.getPerson().getName());
+ }
+
+ @Test
+ public void testAutowiredFieldDoesNotResolveWithMultipleQualifierValuesAndMultipleMatchingCandidates() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue(JUERGEN);
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(TestQualifierWithMultipleAttributes.class);
+ qualifier.setAttribute("number", 123);
+ person1.addQualifier(qualifier);
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue(MARK);
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ AutowireCandidateQualifier qualifier2 = new AutowireCandidateQualifier(TestQualifierWithMultipleAttributes.class);
+ qualifier2.setAttribute("number", 123);
+ qualifier2.setAttribute("value", "default");
+ person2.addQualifier(qualifier2);
+ context.registerBeanDefinition(JUERGEN, person1);
+ context.registerBeanDefinition(MARK, person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ try {
+ context.refresh();
+ fail("expected BeanCreationException");
+ }
+ catch (BeanCreationException e) {
+ assertTrue(e.getRootCause() instanceof NoSuchBeanDefinitionException);
+ assertEquals("autowired", e.getBeanName());
+ }
+ }
+
+ @Test
+ public void testAutowiredFieldDoesNotResolveWithBaseQualifierAndNonDefaultValueAndMultipleMatchingCandidates() {
+ GenericApplicationContext context = new GenericApplicationContext();
+ ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
+ cavs1.addGenericArgumentValue("the real juergen");
+ RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
+ person1.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "juergen"));
+ ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
+ cavs2.addGenericArgumentValue("juergen imposter");
+ RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
+ person2.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "juergen"));
+ context.registerBeanDefinition("juergen1", person1);
+ context.registerBeanDefinition("juergen2", person2);
+ context.registerBeanDefinition("autowired",
+ new RootBeanDefinition(QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean.class));
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ try {
+ context.refresh();
+ fail("expected BeanCreationException");
+ }
+ catch (BeanCreationException e) {
+ assertTrue(e instanceof UnsatisfiedDependencyException);
+ assertEquals("autowired", e.getBeanName());
+ }
+ }
+
+
+ private static class QualifiedFieldTestBean {
+
+ @Inject
+ @TestQualifier
+ private Person person;
+
+ public Person getPerson() {
+ return this.person;
+ }
+ }
+
+
+ private static class QualifiedMethodParameterTestBean {
+
+ private Person person;
+
+ @Inject
+ public void setPerson(@TestQualifier Person person) {
+ this.person = person;
+ }
+
+ public Person getPerson() {
+ return this.person;
+ }
+ }
+
+
+ private static class QualifiedConstructorArgumentTestBean {
+
+ private Person person;
+
+ @Inject
+ public QualifiedConstructorArgumentTestBean(@TestQualifier Person person) {
+ this.person = person;
+ }
+
+ public Person getPerson() {
+ return this.person;
+ }
+
+ }
+
+
+ public static class QualifiedFieldWithDefaultValueTestBean {
+
+ @Inject
+ @TestQualifierWithDefaultValue
+ private Person person;
+
+ public Person getPerson() {
+ return this.person;
+ }
+ }
+
+
+ public static class QualifiedFieldWithMultipleAttributesTestBean {
+
+ @Inject
+ @TestQualifierWithMultipleAttributes(number=123)
+ private Person person;
+
+ public Person getPerson() {
+ return this.person;
+ }
+ }
+
+
+ private static class QualifiedFieldWithBaseQualifierDefaultValueTestBean {
+
+ @Inject
+ private Person person;
+
+ public Person getPerson() {
+ return this.person;
+ }
+ }
+
+
+ public static class QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean {
+
+ private Person person;
+
+ @Inject
+ public QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean(
+ @Named("juergen") Person person) {
+ this.person = person;
+ }
+
+ public Person getPerson() {
+ return this.person;
+ }
+ }
+
+
+ private static class Person {
+
+ private String name;
+
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+ }
+
+
+ @TestQualifier
+ private static class QualifiedPerson extends Person {
+
+ public QualifiedPerson() {
+ super(null);
+ }
+
+ public QualifiedPerson(String name) {
+ super(name);
+ }
+ }
+
+
+ @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
+ @Retention(RetentionPolicy.RUNTIME)
+ @Qualifier
+ public static @interface TestQualifier {
+ }
+
+
+ @Target(ElementType.FIELD)
+ @Retention(RetentionPolicy.RUNTIME)
+ @Qualifier
+ public static @interface TestQualifierWithDefaultValue {
+
+ public abstract String value() default "default";
+ }
+
+
+ @Target(ElementType.FIELD)
+ @Retention(RetentionPolicy.RUNTIME)
+ @Qualifier
+ public static @interface TestQualifierWithMultipleAttributes {
+
+ public abstract String value() default "default";
+
+ public abstract int number();
+ }
+
+}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java
index 4cfe5ab2c47..7d31e003d02 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java
@@ -16,6 +16,12 @@
package org.springframework.context.annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Retention;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
@@ -69,6 +75,15 @@ public final class AnnotationScopeMetadataResolverTests {
assertEquals(ScopedProxyMode.TARGET_CLASS, scopeMetadata.getScopedProxyMode());
}
+ @Test
+ public void testCustomRequestScope() {
+ AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(AnnotatedWithCustomRequestScope.class);
+ ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(bd);
+ assertNotNull("resolveScopeMetadata(..) must *never* return null.", scopeMetadata);
+ assertEquals("request", scopeMetadata.getScopeName());
+ assertEquals(ScopedProxyMode.NO, scopeMetadata.getScopedProxyMode());
+ }
+
@Test(expected=IllegalArgumentException.class)
public void testCtorWithNullScopedProxyMode() {
new AnnotationScopeMetadataResolver(null);
@@ -94,4 +109,17 @@ public final class AnnotationScopeMetadataResolverTests {
private static final class AnnotatedWithScopedProxy {
}
+
+ @CustomRequestScope
+ private static final class AnnotatedWithCustomRequestScope {
+ }
+
+
+ @Target({ElementType.TYPE, ElementType.METHOD})
+ @Retention(RetentionPolicy.RUNTIME)
+ @Scope("request")
+ public @interface CustomRequestScope {
+
+ }
+
}
diff --git a/org.springframework.context/template.mf b/org.springframework.context/template.mf
index 67c9c5c0fb8..262398c5082 100644
--- a/org.springframework.context/template.mf
+++ b/org.springframework.context/template.mf
@@ -14,6 +14,7 @@ Import-Template:
groovy.*;version="[1.5.0, 2.0.0)";resolution:=optional,
javax.annotation.*;version="0";resolution:=optional,
javax.ejb.*;version="[2.1.0, 4.0.0)";resolution:=optional,
+ javax.inject.*;version="[0.9.0, 2.0.0)";resolution:=optional,
javax.interceptor.*;version="[3.0.0, 4.0.0)";resolution:=optional,
javax.jms.*;version="[1.1.0, 2.0.0)";resolution:=optional,
javax.management.*;version="0";resolution:=optional,
diff --git a/org.springframework.integration-tests/.classpath b/org.springframework.integration-tests/.classpath
index 0fd0bfab5a4..53bf684735c 100644
--- a/org.springframework.integration-tests/.classpath
+++ b/org.springframework.integration-tests/.classpath
@@ -25,6 +25,7 @@
+
diff --git a/org.springframework.integration-tests/integration-tests.iml b/org.springframework.integration-tests/integration-tests.iml
index a5713abde85..23fc185a62f 100644
--- a/org.springframework.integration-tests/integration-tests.iml
+++ b/org.springframework.integration-tests/integration-tests.iml
@@ -27,6 +27,7 @@
+
diff --git a/org.springframework.integration-tests/ivy.xml b/org.springframework.integration-tests/ivy.xml
index 3504365bdb8..7737084bc10 100644
--- a/org.springframework.integration-tests/ivy.xml
+++ b/org.springframework.integration-tests/ivy.xml
@@ -30,6 +30,7 @@
+
diff --git a/org.springframework.integration-tests/src/test/java/org/springframework/context/annotation/jsr330/ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests.java b/org.springframework.integration-tests/src/test/java/org/springframework/context/annotation/jsr330/ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests.java
new file mode 100644
index 00000000000..705229cedf3
--- /dev/null
+++ b/org.springframework.integration-tests/src/test/java/org/springframework/context/annotation/jsr330/ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2002-2009 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.jsr330;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.junit.After;
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
+import org.springframework.context.annotation.ScopeMetadata;
+import org.springframework.context.annotation.ScopeMetadataResolver;
+import org.springframework.context.annotation.ScopedProxyMode;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpSession;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.context.support.GenericWebApplicationContext;
+
+/**
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @author Chris Beams
+ */
+public class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
+
+ private static final String DEFAULT_NAME = "default";
+
+ private static final String MODIFIED_NAME = "modified";
+
+ private ServletRequestAttributes oldRequestAttributes;
+
+ private ServletRequestAttributes newRequestAttributes;
+
+ private ServletRequestAttributes oldRequestAttributesWithSession;
+
+ private ServletRequestAttributes newRequestAttributesWithSession;
+
+
+ @Before
+ public void setUp() {
+ this.oldRequestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
+ this.newRequestAttributes = new ServletRequestAttributes(new MockHttpServletRequest());
+
+ MockHttpServletRequest oldRequestWithSession = new MockHttpServletRequest();
+ oldRequestWithSession.setSession(new MockHttpSession());
+ this.oldRequestAttributesWithSession = new ServletRequestAttributes(oldRequestWithSession);
+
+ MockHttpServletRequest newRequestWithSession = new MockHttpServletRequest();
+ newRequestWithSession.setSession(new MockHttpSession());
+ this.newRequestAttributesWithSession = new ServletRequestAttributes(newRequestWithSession);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ RequestContextHolder.setRequestAttributes(null);
+ }
+
+
+ @Test
+ public void testPrototype() {
+ ApplicationContext context = createContext(ScopedProxyMode.NO);
+ ScopedTestBean bean = (ScopedTestBean) context.getBean("prototype");
+ assertTrue(context.isPrototype("prototype"));
+ assertFalse(context.isSingleton("prototype"));
+ }
+
+ @Test
+ public void testSingletonScopeWithNoProxy() {
+ RequestContextHolder.setRequestAttributes(oldRequestAttributes);
+ ApplicationContext context = createContext(ScopedProxyMode.NO);
+ ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
+ assertTrue(context.isSingleton("singleton"));
+ assertFalse(context.isPrototype("singleton"));
+
+ // should not be a proxy
+ assertFalse(AopUtils.isAopProxy(bean));
+
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ RequestContextHolder.setRequestAttributes(newRequestAttributes);
+ // not a proxy so this should not have changed
+ assertEquals(MODIFIED_NAME, bean.getName());
+
+ // singleton bean, so name should be modified even after lookup
+ ScopedTestBean bean2 = (ScopedTestBean) context.getBean("singleton");
+ assertEquals(MODIFIED_NAME, bean2.getName());
+ }
+
+ @Test
+ public void testSingletonScopeIgnoresProxyInterfaces() {
+ RequestContextHolder.setRequestAttributes(oldRequestAttributes);
+ ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
+ ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
+
+ // should not be a proxy
+ assertFalse(AopUtils.isAopProxy(bean));
+
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ RequestContextHolder.setRequestAttributes(newRequestAttributes);
+ // not a proxy so this should not have changed
+ assertEquals(MODIFIED_NAME, bean.getName());
+
+ // singleton bean, so name should be modified even after lookup
+ ScopedTestBean bean2 = (ScopedTestBean) context.getBean("singleton");
+ assertEquals(MODIFIED_NAME, bean2.getName());
+ }
+
+ @Test
+ public void testSingletonScopeIgnoresProxyTargetClass() {
+ RequestContextHolder.setRequestAttributes(oldRequestAttributes);
+ ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
+ ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
+
+ // should not be a proxy
+ assertFalse(AopUtils.isAopProxy(bean));
+
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ RequestContextHolder.setRequestAttributes(newRequestAttributes);
+ // not a proxy so this should not have changed
+ assertEquals(MODIFIED_NAME, bean.getName());
+
+ // singleton bean, so name should be modified even after lookup
+ ScopedTestBean bean2 = (ScopedTestBean) context.getBean("singleton");
+ assertEquals(MODIFIED_NAME, bean2.getName());
+ }
+
+ @Test
+ public void testRequestScopeWithNoProxy() {
+ RequestContextHolder.setRequestAttributes(oldRequestAttributes);
+ ApplicationContext context = createContext(ScopedProxyMode.NO);
+ ScopedTestBean bean = (ScopedTestBean) context.getBean("request");
+
+ // should not be a proxy
+ assertFalse(AopUtils.isAopProxy(bean));
+
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ RequestContextHolder.setRequestAttributes(newRequestAttributes);
+ // not a proxy so this should not have changed
+ assertEquals(MODIFIED_NAME, bean.getName());
+
+ // but a newly retrieved bean should have the default name
+ ScopedTestBean bean2 = (ScopedTestBean) context.getBean("request");
+ assertEquals(DEFAULT_NAME, bean2.getName());
+ }
+
+ @Test
+ public void testRequestScopeWithProxiedInterfaces() {
+ RequestContextHolder.setRequestAttributes(oldRequestAttributes);
+ ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
+ IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
+
+ // should be dynamic proxy, implementing both interfaces
+ assertTrue(AopUtils.isJdkDynamicProxy(bean));
+ assertTrue(bean instanceof AnotherScopeTestInterface);
+
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ RequestContextHolder.setRequestAttributes(newRequestAttributes);
+ // this is a proxy so it should be reset to default
+ assertEquals(DEFAULT_NAME, bean.getName());
+
+ RequestContextHolder.setRequestAttributes(oldRequestAttributes);
+ assertEquals(MODIFIED_NAME, bean.getName());
+ }
+
+ @Test
+ public void testRequestScopeWithProxiedTargetClass() {
+ RequestContextHolder.setRequestAttributes(oldRequestAttributes);
+ ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
+ IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
+
+ // should be a class-based proxy
+ assertTrue(AopUtils.isCglibProxy(bean));
+ assertTrue(bean instanceof RequestScopedTestBean);
+
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ RequestContextHolder.setRequestAttributes(newRequestAttributes);
+ // this is a proxy so it should be reset to default
+ assertEquals(DEFAULT_NAME, bean.getName());
+
+ RequestContextHolder.setRequestAttributes(oldRequestAttributes);
+ assertEquals(MODIFIED_NAME, bean.getName());
+ }
+
+ @Test
+ public void testSessionScopeWithNoProxy() {
+ RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
+ ApplicationContext context = createContext(ScopedProxyMode.NO);
+ ScopedTestBean bean = (ScopedTestBean) context.getBean("session");
+
+ // should not be a proxy
+ assertFalse(AopUtils.isAopProxy(bean));
+
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ RequestContextHolder.setRequestAttributes(newRequestAttributesWithSession);
+ // not a proxy so this should not have changed
+ assertEquals(MODIFIED_NAME, bean.getName());
+
+ // but a newly retrieved bean should have the default name
+ ScopedTestBean bean2 = (ScopedTestBean) context.getBean("session");
+ assertEquals(DEFAULT_NAME, bean2.getName());
+ }
+
+ @Test
+ public void testSessionScopeWithProxiedInterfaces() {
+ RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
+ ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
+ IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
+
+ // should be dynamic proxy, implementing both interfaces
+ assertTrue(AopUtils.isJdkDynamicProxy(bean));
+ assertTrue(bean instanceof AnotherScopeTestInterface);
+
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ RequestContextHolder.setRequestAttributes(newRequestAttributesWithSession);
+ // this is a proxy so it should be reset to default
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ IScopedTestBean bean2 = (IScopedTestBean) context.getBean("session");
+ assertEquals(MODIFIED_NAME, bean2.getName());
+ bean2.setName(DEFAULT_NAME);
+ assertEquals(DEFAULT_NAME, bean.getName());
+
+ RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
+ assertEquals(MODIFIED_NAME, bean.getName());
+ }
+
+ @Test
+ public void testSessionScopeWithProxiedTargetClass() {
+ RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
+ ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
+ IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
+
+ // should be a class-based proxy
+ assertTrue(AopUtils.isCglibProxy(bean));
+ assertTrue(bean instanceof ScopedTestBean);
+ assertTrue(bean instanceof SessionScopedTestBean);
+
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ RequestContextHolder.setRequestAttributes(newRequestAttributesWithSession);
+ // this is a proxy so it should be reset to default
+ assertEquals(DEFAULT_NAME, bean.getName());
+ bean.setName(MODIFIED_NAME);
+
+ IScopedTestBean bean2 = (IScopedTestBean) context.getBean("session");
+ assertEquals(MODIFIED_NAME, bean2.getName());
+ bean2.setName(DEFAULT_NAME);
+ assertEquals(DEFAULT_NAME, bean.getName());
+
+ RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
+ assertEquals(MODIFIED_NAME, bean.getName());
+ }
+
+
+ private ApplicationContext createContext(final ScopedProxyMode scopedProxyMode) {
+ GenericWebApplicationContext context = new GenericWebApplicationContext();
+ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
+ scanner.setIncludeAnnotationConfig(false);
+ scanner.setScopeMetadataResolver(new ScopeMetadataResolver() {
+ public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
+ ScopeMetadata metadata = new ScopeMetadata();
+ if (definition instanceof AnnotatedBeanDefinition) {
+ AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
+ for (String type : annDef.getMetadata().getAnnotationTypes()) {
+ if (type.equals(javax.inject.Singleton.class.getName())) {
+ metadata.setScopeName(BeanDefinition.SCOPE_SINGLETON);
+ break;
+ }
+ else if (annDef.getMetadata().getMetaAnnotationTypes(type).contains(javax.inject.Scope.class.getName())) {
+ metadata.setScopeName(type.substring(type.length() - 13, type.length() - 6).toLowerCase());
+ metadata.setScopedProxyMode(scopedProxyMode);
+ break;
+ }
+ else if (type.startsWith("javax.inject")) {
+ metadata.setScopeName(BeanDefinition.SCOPE_PROTOTYPE);
+ }
+ }
+ }
+ return metadata;
+ }
+ });
+
+ // Scan twice in order to find errors in the bean definition compatibility check.
+ scanner.scan(getClass().getPackage().getName());
+ scanner.scan(getClass().getPackage().getName());
+
+ context.registerAlias("classPathBeanDefinitionScannerJsr330ScopeIntegrationTests.SessionScopedTestBean", "session");
+ context.refresh();
+ return context;
+ }
+
+
+ public static interface IScopedTestBean {
+
+ String getName();
+
+ void setName(String name);
+ }
+
+
+ public static abstract class ScopedTestBean implements IScopedTestBean {
+
+ private String name = DEFAULT_NAME;
+
+ public String getName() { return this.name; }
+
+ public void setName(String name) { this.name = name; }
+ }
+
+
+ @Named("prototype")
+ public static class PrototypeScopedTestBean extends ScopedTestBean {
+ }
+
+
+ @Named("singleton")
+ @Singleton
+ public static class SingletonScopedTestBean extends ScopedTestBean {
+ }
+
+
+ public static interface AnotherScopeTestInterface {
+ }
+
+
+ @RequestScoped
+ @Named("request")
+ public static class RequestScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
+ }
+
+
+ @SessionScoped
+ public static class SessionScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
+ }
+
+
+ @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
+ @Retention(RetentionPolicy.RUNTIME)
+ @javax.inject.Scope
+ public static @interface RequestScoped {
+ }
+
+
+ @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
+ @Retention(RetentionPolicy.RUNTIME)
+ @javax.inject.Scope
+ public static @interface SessionScoped {
+ }
+
+}
diff --git a/org.springframework.integration-tests/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerScopeIntegrationTests.java b/org.springframework.integration-tests/src/test/java/org/springframework/context/annotation/scope/ClassPathBeanDefinitionScannerScopeIntegrationTests.java
similarity index 95%
rename from org.springframework.integration-tests/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerScopeIntegrationTests.java
rename to org.springframework.integration-tests/src/test/java/org/springframework/context/annotation/scope/ClassPathBeanDefinitionScannerScopeIntegrationTests.java
index 0a120ce8327..8b48e58da82 100644
--- a/org.springframework.integration-tests/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerScopeIntegrationTests.java
+++ b/org.springframework.integration-tests/src/test/java/org/springframework/context/annotation/scope/ClassPathBeanDefinitionScannerScopeIntegrationTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.context.annotation;
+package org.springframework.context.annotation.scope;
import org.junit.After;
import static org.junit.Assert.*;
@@ -26,6 +26,9 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.ScopedProxyMode;
+import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
+import org.springframework.context.annotation.Scope;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
@@ -279,15 +282,11 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
private ApplicationContext createContext(ScopedProxyMode scopedProxyMode) {
GenericWebApplicationContext context = new GenericWebApplicationContext();
- ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, false);
+ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
- scanner.addIncludeFilter(new AnnotationTypeFilter(ScopeTestComponent.class));
scanner.setBeanNameGenerator(new BeanNameGenerator() {
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
- String beanClassName = ClassUtils.getShortName(definition.getBeanClassName());
- int begin = beanClassName.lastIndexOf('.') + 1;
- int end = beanClassName.lastIndexOf("ScopedTestBean");
- return beanClassName.substring(begin, end).toLowerCase();
+ return definition.getScope();
}
});
scanner.setScopedProxyMode(scopedProxyMode);
@@ -301,10 +300,6 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
}
- public static @interface ScopeTestComponent {
- }
-
-
public static interface IScopedTestBean {
String getName();
@@ -323,8 +318,8 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
}
- @ScopeTestComponent
- public static class SingletonScopedTestBean extends ScopedTestBean {
+ @Scope("singleton")
+ public static class SingletonScopedTestBean extends ScopedTestBean {
}
@@ -333,13 +328,11 @@ public class ClassPathBeanDefinitionScannerScopeIntegrationTests {
@Scope("request")
- @ScopeTestComponent
- public static class RequestScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
+ public static class RequestScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
}
@Scope("session")
- @ScopeTestComponent
public static class SessionScopedTestBean extends ScopedTestBean implements AnotherScopeTestInterface {
}