Add support for annotation scan in @ConditionalOn*Bean

This commit is contained in:
Dave Syer 2013-10-24 12:21:02 -04:00
parent 86a369b955
commit 18ee229748
5 changed files with 110 additions and 3 deletions

View File

@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.condition;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@ -45,6 +46,13 @@ public @interface ConditionalOnBean {
*/
Class<?>[] value() default {};
/**
* The annotation type decorating a bean that should be checked. The condition matches
* when each class specified is missing from beans in the {@link ApplicationContext}.
* @return the class types of beans to check
*/
Class<? extends Annotation>[] annotation() default {};
/**
* The names of beans to check. The condition matches when any of the bean names
* specified is contained in the {@link ApplicationContext}.

View File

@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.condition;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@ -45,6 +46,14 @@ public @interface ConditionalOnMissingBean {
*/
Class<?>[] value() default {};
/**
* The annotation type decorating a bean that should be checked. The condition matches
* when each class specified is missing from all beans in the
* {@link ApplicationContext}.
* @return the class types of beans to check
*/
Class<? extends Annotation>[] annotation() default {};
/**
* The names of beans to check. The condition matches when each bean name specified is
* missing in the {@link ApplicationContext}.

View File

@ -16,10 +16,12 @@
package org.springframework.boot.autoconfigure.condition;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
@ -36,6 +38,7 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.StringUtils;
/**
* {@link Condition} that checks for the presence or absence of specific beans.
@ -96,6 +99,11 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
context.getClassLoader(), considerHierarchy)));
}
for (String annotation : beans.getAnnotations()) {
beanNames.addAll(Arrays.asList(getBeanNamesForAnnotation(beanFactory,
annotation, context.getClassLoader(), considerHierarchy)));
}
for (String beanName : beans.getNames()) {
if (containsBean(beanFactory, beanName, considerHierarchy)) {
beanNames.add(beanName);
@ -132,10 +140,45 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
}
}
private String[] getBeanNamesForAnnotation(
ConfigurableListableBeanFactory beanFactory, String type,
ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
String[] result = NO_BEANS;
try {
@SuppressWarnings("unchecked")
Class<? extends Annotation> typeClass = (Class<? extends Annotation>) ClassUtils
.forName(type, classLoader);
Map<String, Object> annotated = beanFactory.getBeansWithAnnotation(typeClass);
result = annotated.keySet().toArray(new String[annotated.size()]);
if (considerHierarchy) {
if (beanFactory.getParentBeanFactory() instanceof ConfigurableListableBeanFactory) {
String[] parentResult = getBeanNamesForAnnotation(
(ConfigurableListableBeanFactory) beanFactory
.getParentBeanFactory(),
type, classLoader, true);
List<String> resultList = new ArrayList<String>();
resultList.addAll(Arrays.asList(result));
for (String beanName : parentResult) {
if (!resultList.contains(beanName)
&& !beanFactory.containsLocalBean(beanName)) {
resultList.add(beanName);
}
}
result = StringUtils.toStringArray(resultList);
}
}
return result;
}
catch (ClassNotFoundException ex) {
return NO_BEANS;
}
}
private static class BeanSearchSpec {
private List<String> names = new ArrayList<String>();
private List<String> types = new ArrayList<String>();
private List<String> annotations = new ArrayList<String>();
private SearchStrategy strategy;
public BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata,
@ -144,12 +187,16 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
.getAllAnnotationAttributes(annotationType.getName(), true);
collect(attributes, "name", this.names);
collect(attributes, "value", this.types);
collect(attributes, "annotation", this.annotations);
if (this.types.isEmpty() && this.names.isEmpty()) {
addDeducedBeanType(context, metadata, this.types);
}
Assert.isTrue(!this.types.isEmpty() || !this.names.isEmpty(), "@"
+ ClassUtils.getShortName(annotationType)
+ " annotations must specify at least one bean");
Assert.isTrue(
!this.types.isEmpty() || !this.names.isEmpty()
|| !this.annotations.isEmpty(),
"@"
+ ClassUtils.getShortName(annotationType)
+ " annotations must specify at least one bean (type, name or annotation)");
this.strategy = (SearchStrategy) metadata.getAnnotationAttributes(
annotationType.getName()).get("search");
}
@ -204,6 +251,10 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
return this.types;
}
public List<String> getAnnotations() {
return this.annotations;
}
@Override
public String toString() {
return new ToStringCreator(this).append("names", this.names)

View File

@ -23,6 +23,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@ -32,6 +33,7 @@ import static org.junit.Assert.assertTrue;
*
* @author Dave Syer
*/
// FIXME: unignore test
@Ignore
public class ConditionalOnBeanTests {
@ -77,6 +79,14 @@ public class ConditionalOnBeanTests {
assertEquals("bar", this.context.getBean("bar"));
}
@Test
public void testAnnotationOnBeanCondition() {
this.context.register(FooConfiguration.class, OnAnnotationConfiguration.class);
this.context.refresh();
assertTrue(this.context.containsBean("bar"));
assertEquals("bar", this.context.getBean("bar"));
}
@Configuration
@ConditionalOnBean(name = "foo")
protected static class OnBeanNameConfiguration {
@ -86,6 +96,15 @@ public class ConditionalOnBeanTests {
}
}
@Configuration
@ConditionalOnBean(annotation = EnableScheduling.class)
protected static class OnAnnotationConfiguration {
@Bean
public String bar() {
return "bar";
}
}
@Configuration
@ConditionalOnBean(String.class)
protected static class OnBeanClassConfiguration {
@ -96,6 +115,7 @@ public class ConditionalOnBeanTests {
}
@Configuration
@EnableScheduling
protected static class FooConfiguration {
@Bean
public String foo() {

View File

@ -20,6 +20,7 @@ import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
@ -84,6 +85,14 @@ public class ConditionalOnMissingBeanTests {
assertThat(this.context.getBeansOfType(ExampleBean.class).size(), equalTo(1));
}
@Test
public void testAnnotationOnMissingBeanCondition() {
this.context.register(FooConfiguration.class, OnAnnotationConfiguration.class);
this.context.refresh();
assertFalse(this.context.containsBean("bar"));
assertEquals("foo", this.context.getBean("foo"));
}
@Configuration
@ConditionalOnMissingBean(name = "foo")
protected static class OnBeanNameConfiguration {
@ -94,6 +103,16 @@ public class ConditionalOnMissingBeanTests {
}
@Configuration
@ConditionalOnMissingBean(annotation = EnableScheduling.class)
protected static class OnAnnotationConfiguration {
@Bean
public String bar() {
return "bar";
}
}
@Configuration
@EnableScheduling
protected static class FooConfiguration {
@Bean
public String foo() {