diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
index 4c16bb0e64c..2501ed0dfa7 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
@@ -17,7 +17,9 @@
package org.springframework.context.annotation;
import java.lang.annotation.Annotation;
+import java.util.function.Supplier;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
@@ -83,6 +85,7 @@ public class AnnotatedBeanDefinitionReader {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
+
/**
* Return the BeanDefinitionRegistry that this scanner operates on.
*/
@@ -117,28 +120,94 @@ public class AnnotatedBeanDefinitionReader {
(scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver());
}
+
+ /**
+ * Register one or more annotated classes to be processed.
+ *
Calls to {@code register} are idempotent; adding the same
+ * annotated class more than once has no additional effect.
+ * @param annotatedClasses one or more annotated classes,
+ * e.g. {@link Configuration @Configuration} classes
+ */
public void register(Class>... annotatedClasses) {
for (Class> annotatedClass : annotatedClasses) {
registerBean(annotatedClass);
}
}
+ /**
+ * Register a bean from the given bean class, deriving its metadata from
+ * class-declared annotations.
+ * @param annotatedClass the class of the bean
+ */
+ @SuppressWarnings("unchecked")
public void registerBean(Class> annotatedClass) {
- registerBean(annotatedClass, null, (Class extends Annotation>[]) null);
+ registerBean(annotatedClass, null, null, (Class extends Annotation>[]) null);
}
+ /**
+ * Register a bean from the given bean class, deriving its metadata from
+ * class-declared annotations.
+ * @param annotatedClass the class of the bean
+ * @param qualifiers specific qualifier annotations to consider,
+ * in addition to qualifiers at the bean class level
+ */
@SuppressWarnings("unchecked")
public void registerBean(Class> annotatedClass, Class extends Annotation>... qualifiers) {
- registerBean(annotatedClass, null, qualifiers);
+ registerBean(annotatedClass, null, null, qualifiers);
}
+ /**
+ * Register a bean from the given bean class, deriving its metadata from
+ * class-declared annotations.
+ * @param annotatedClass the class of the bean
+ * @param name an explicit name for the bean
+ * @param qualifiers specific qualifier annotations to consider,
+ * in addition to qualifiers at the bean class level
+ */
@SuppressWarnings("unchecked")
public void registerBean(Class> annotatedClass, String name, Class extends Annotation>... qualifiers) {
+ registerBean(annotatedClass, null, name, qualifiers);
+ }
+
+ /**
+ * Register a bean from the given bean class, deriving its metadata from
+ * class-declared annotations, using the given supplier for obtaining a new
+ * instance (possibly declared as a lambda expression or method reference).
+ * @param annotatedClass the class of the bean
+ * @param instanceSupplier a callback for creating an instance of the bean
+ * (may be {@code null})
+ * @return the registered bean definition, or {@code null} if skipped due to
+ * a declared condition
+ * @since 5.0
+ */
+ @SuppressWarnings("unchecked")
+ public AnnotatedBeanDefinition registerBean(Class annotatedClass, Supplier instanceSupplier) {
+ return registerBean(annotatedClass, instanceSupplier, null, (Class extends Annotation>[]) null);
+ }
+
+ /**
+ * Register a bean from the given bean class, deriving its metadata from
+ * class-declared annotations.
+ * @param annotatedClass the class of the bean
+ * @param instanceSupplier a callback for creating an instance of the bean
+ * (may be {@code null})
+ * @param name an explicit name for the bean
+ * @param qualifiers specific qualifier annotations to consider, if any,
+ * in addition to qualifiers at the bean class level
+ * @return the registered bean definition, or {@code null} if skipped due to
+ * a declared condition
+ * @since 5.0
+ */
+ @SuppressWarnings("unchecked")
+ public AnnotatedBeanDefinition registerBean(Class annotatedClass, Supplier instanceSupplier,
+ String name, Class extends Annotation>... qualifiers) {
+
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
- return;
+ return null;
}
+ abd.setInstanceSupplier(instanceSupplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
@@ -160,6 +229,7 @@ public class AnnotatedBeanDefinitionReader {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
+ return abd;
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java
index 98ab303e294..3e553e257d6 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package org.springframework.context.annotation;
+import java.util.function.Supplier;
+
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.GenericApplicationContext;
@@ -135,6 +138,16 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
this.scanner.setScopeMetadataResolver(scopeMetadataResolver);
}
+ @Override
+ protected void prepareRefresh() {
+ this.scanner.clearCache();
+ super.prepareRefresh();
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of AnnotationConfigRegistry
+ //---------------------------------------------------------------------
/**
* Register one or more annotated classes to be processed.
@@ -164,10 +177,78 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
}
- @Override
- protected void prepareRefresh() {
- this.scanner.clearCache();
- super.prepareRefresh();
+ //---------------------------------------------------------------------
+ // Convenient methods for registering individual beans
+ //---------------------------------------------------------------------
+
+ /**
+ * Register a bean from the given bean class, deriving its metadata from
+ * class-declared annotations, using the given supplier for obtaining a new
+ * instance (possibly declared as a lambda expression or method reference).
+ *
The bean name will be generated according to annotated component rules.
+ * @param annotatedClass the class of the bean
+ * @param instanceSupplier a callback for creating an instance of the bean
+ * @since 5.0
+ * @see #setBeanNameGenerator
+ */
+ @SuppressWarnings("unchecked")
+ public void registerBean(Class annotatedClass, Supplier instanceSupplier) {
+ registerBean(null, annotatedClass, instanceSupplier);
+ }
+
+ /**
+ * Register a bean from the given bean class, deriving its metadata from
+ * class-declared annotations, using the given supplier for obtaining a new
+ * instance (possibly declared as a lambda expression or method reference).
+ * @param beanName the name of the bean (may be {@code null})
+ * @param annotatedClass the class of the bean
+ * @param instanceSupplier a callback for creating an instance of the bean
+ * @since 5.0
+ */
+ @SuppressWarnings("unchecked")
+ public void registerBean(String beanName, Class annotatedClass, Supplier instanceSupplier) {
+ Assert.notNull(instanceSupplier, "Supplier must not be null");
+ this.reader.registerBean(annotatedClass, instanceSupplier, beanName);
+ }
+
+ /**
+ * Register a bean from the given bean class, deriving its metadata from
+ * class-declared annotations, and optionally providing explicit constructor
+ * arguments for consideration in the autowiring process.
+ *
The bean name will be generated according to annotated component rules.
+ * @param annotatedClass the class of the bean
+ * @param constructorArguments argument values to be fed into Spring's
+ * constructor resolution algorithm, resolving either all arguments or just
+ * specific ones, with the rest to be resolved through regular autowiring
+ * (may be {@code null} or empty)
+ * @since 5.0
+ * @see #setBeanNameGenerator
+ */
+ @SuppressWarnings("unchecked")
+ public void registerBean(Class annotatedClass, Object... constructorArguments) {
+ registerBean(null, annotatedClass, constructorArguments);
+ }
+
+ /**
+ * Register a bean from the given bean class, deriving its metadata from
+ * class-declared annotations, and optionally providing explicit constructor
+ * arguments for consideration in the autowiring process.
+ * @param beanName the name of the bean (may be {@code null})
+ * @param annotatedClass the class of the bean
+ * @param constructorArguments argument values to be fed into Spring's
+ * constructor resolution algorithm, resolving either all arguments or just
+ * specific ones, with the rest to be resolved through regular autowiring
+ * (may be {@code null} or empty)
+ * @since 5.0
+ */
+ @SuppressWarnings("unchecked")
+ public void registerBean(String beanName, Class annotatedClass, Object... constructorArguments) {
+ AnnotatedBeanDefinition abd = this.reader.registerBean(annotatedClass, null, beanName);
+ if (constructorArguments != null) {
+ for (Object arg : constructorArguments) {
+ abd.getConstructorArgumentValues().addGenericArgumentValue(arg);
+ }
+ }
}
}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java
index f485eeb3d53..3d18a9b1532 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -86,7 +86,7 @@ public class AnnotationConfigApplicationContextTests {
public void getBeanByType() {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
TestBean testBean = context.getBean(TestBean.class);
- assertNotNull("getBean() should not return null", testBean);
+ assertNotNull(testBean);
assertThat(testBean.name, equalTo("foo"));
}
@@ -116,6 +116,102 @@ public class AnnotationConfigApplicationContextTests {
assertNotNull(configObject);
}
+ @Test
+ public void individualBeans() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.register(BeanA.class, BeanB.class, BeanC.class);
+ context.refresh();
+ assertSame(context.getBean(BeanB.class), context.getBean(BeanA.class).b);
+ assertSame(context.getBean(BeanC.class), context.getBean(BeanA.class).c);
+ assertSame(context, context.getBean(BeanB.class).applicationContext);
+ }
+
+ @Test
+ public void individualNamedBeans() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.registerBean("a", BeanA.class);
+ context.registerBean("b", BeanB.class);
+ context.registerBean("c", BeanC.class);
+ context.refresh();
+ assertSame(context.getBean("b"), context.getBean("a", BeanA.class).b);
+ assertSame(context.getBean("c"), context.getBean("a", BeanA.class).c);
+ assertSame(context, context.getBean("b", BeanB.class).applicationContext);
+ }
+
+ @Test
+ public void individualBeanWithSupplier() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.registerBean(BeanA.class,
+ () -> new BeanA(context.getBean(BeanB.class), context.getBean(BeanC.class)));
+ context.registerBean(BeanB.class, BeanB::new);
+ context.registerBean(BeanC.class, BeanC::new);
+ context.refresh();
+ assertSame(context.getBean(BeanB.class), context.getBean(BeanA.class).b);
+ assertSame(context.getBean(BeanC.class), context.getBean(BeanA.class).c);
+ assertSame(context, context.getBean(BeanB.class).applicationContext);
+ }
+
+ @Test
+ public void individualNamedBeanWithSupplier() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.registerBean("a", BeanA.class,
+ () -> new BeanA(context.getBean(BeanB.class), context.getBean(BeanC.class)));
+ context.registerBean("b", BeanB.class, BeanB::new);
+ context.registerBean("c", BeanC.class, BeanC::new);
+ context.refresh();
+ assertSame(context.getBean("b", BeanB.class), context.getBean(BeanA.class).b);
+ assertSame(context.getBean("c"), context.getBean("a", BeanA.class).c);
+ assertSame(context, context.getBean("b", BeanB.class).applicationContext);
+ }
+
+ @Test
+ public void individualBeanWithSpecifiedConstructorArguments() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ BeanB b = new BeanB();
+ BeanC c = new BeanC();
+ context.registerBean(BeanA.class, b, c);
+ context.refresh();
+ assertSame(b, context.getBean(BeanA.class).b);
+ assertSame(c, context.getBean(BeanA.class).c);
+ assertNull(b.applicationContext);
+ }
+
+ @Test
+ public void individualNamedBeanWithSpecifiedConstructorArguments() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ BeanB b = new BeanB();
+ BeanC c = new BeanC();
+ context.registerBean("a", BeanA.class, b, c);
+ context.refresh();
+ assertSame(b, context.getBean("a", BeanA.class).b);
+ assertSame(c, context.getBean("a", BeanA.class).c);
+ assertNull(b.applicationContext);
+ }
+
+ @Test
+ public void individualBeanWithMixedConstructorArguments() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ BeanC c = new BeanC();
+ context.registerBean(BeanA.class, c);
+ context.registerBean(BeanB.class);
+ context.refresh();
+ assertSame(context.getBean(BeanB.class), context.getBean(BeanA.class).b);
+ assertSame(c, context.getBean(BeanA.class).c);
+ assertSame(context, context.getBean(BeanB.class).applicationContext);
+ }
+
+ @Test
+ public void individualNamedBeanWithMixedConstructorArguments() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ BeanC c = new BeanC();
+ context.registerBean("a", BeanA.class, c);
+ context.registerBean("b", BeanB.class);
+ context.refresh();
+ assertSame(context.getBean("b", BeanB.class), context.getBean("a", BeanA.class).b);
+ assertSame(c, context.getBean("a", BeanA.class).c);
+ assertSame(context, context.getBean("b", BeanB.class).applicationContext);
+ }
+
@Test
public void getBeanByTypeRaisesNoSuchBeanDefinitionException() {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
@@ -123,12 +219,11 @@ public class AnnotationConfigApplicationContextTests {
// attempt to retrieve a bean that does not exist
Class> targetType = Pattern.class;
try {
- Object bean = context.getBean(targetType);
- fail("should have thrown NoSuchBeanDefinitionException, instead got: " + bean);
+ context.getBean(targetType);
+ fail("Should have thrown NoSuchBeanDefinitionException");
}
catch (NoSuchBeanDefinitionException ex) {
- assertThat(ex.getMessage(), containsString(
- format("No qualifying bean of type '%s'", targetType.getName())));
+ assertThat(ex.getMessage(), containsString(format("No qualifying bean of type '%s'", targetType.getName())));
}
}
@@ -188,6 +283,7 @@ public class AnnotationConfigApplicationContextTests {
@Configuration
static class Config {
+
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
@@ -198,6 +294,7 @@ public class AnnotationConfigApplicationContextTests {
@Configuration("customConfigBeanName")
static class ConfigWithCustomName {
+
@Bean
public TestBean testBean() {
return new TestBean();
@@ -205,6 +302,7 @@ public class AnnotationConfigApplicationContextTests {
}
static class ConfigMissingAnnotation {
+
@Bean
public TestBean testBean() {
return new TestBean();
@@ -213,18 +311,26 @@ public class AnnotationConfigApplicationContextTests {
@Configuration
static class TwoTestBeanConfig {
- @Bean TestBean tb1() { return new TestBean(); }
- @Bean TestBean tb2() { return new TestBean(); }
+
+ @Bean TestBean tb1() {
+ return new TestBean();
+ }
+
+ @Bean TestBean tb2() {
+ return new TestBean();
+ }
}
@Configuration
static class NameConfig {
+
@Bean String name() { return "foo"; }
}
@Configuration
@Import(NameConfig.class)
static class AutowiredConfig {
+
@Autowired String autowiredName;
@Bean TestBean testBean() {
@@ -234,6 +340,27 @@ public class AnnotationConfigApplicationContextTests {
}
}
+ static class BeanA {
+
+ BeanB b;
+ BeanC c;
+
+ @Autowired public BeanA(BeanB b, BeanC c) {
+ this.b = b;
+ this.c = c;
+ }
+ }
+
+ static class BeanB {
+
+ @Autowired ApplicationContext applicationContext;
+
+ public BeanB() {
+ }
+ }
+
+ static class BeanC {}
+
static class UntypedFactoryBean implements FactoryBean