Add support for annotation scan in @ConditionalOn*Bean
This commit is contained in:
parent
86a369b955
commit
18ee229748
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.condition;
|
package org.springframework.boot.autoconfigure.condition;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
|
@ -45,6 +46,13 @@ public @interface ConditionalOnBean {
|
||||||
*/
|
*/
|
||||||
Class<?>[] value() default {};
|
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
|
* The names of beans to check. The condition matches when any of the bean names
|
||||||
* specified is contained in the {@link ApplicationContext}.
|
* specified is contained in the {@link ApplicationContext}.
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.condition;
|
package org.springframework.boot.autoconfigure.condition;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
|
@ -45,6 +46,14 @@ public @interface ConditionalOnMissingBean {
|
||||||
*/
|
*/
|
||||||
Class<?>[] value() default {};
|
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
|
* The names of beans to check. The condition matches when each bean name specified is
|
||||||
* missing in the {@link ApplicationContext}.
|
* missing in the {@link ApplicationContext}.
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,12 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.condition;
|
package org.springframework.boot.autoconfigure.condition;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
|
|
@ -36,6 +38,7 @@ import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Condition} that checks for the presence or absence of specific beans.
|
* {@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)));
|
context.getClassLoader(), considerHierarchy)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (String annotation : beans.getAnnotations()) {
|
||||||
|
beanNames.addAll(Arrays.asList(getBeanNamesForAnnotation(beanFactory,
|
||||||
|
annotation, context.getClassLoader(), considerHierarchy)));
|
||||||
|
}
|
||||||
|
|
||||||
for (String beanName : beans.getNames()) {
|
for (String beanName : beans.getNames()) {
|
||||||
if (containsBean(beanFactory, beanName, considerHierarchy)) {
|
if (containsBean(beanFactory, beanName, considerHierarchy)) {
|
||||||
beanNames.add(beanName);
|
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 static class BeanSearchSpec {
|
||||||
|
|
||||||
private List<String> names = new ArrayList<String>();
|
private List<String> names = new ArrayList<String>();
|
||||||
private List<String> types = new ArrayList<String>();
|
private List<String> types = new ArrayList<String>();
|
||||||
|
private List<String> annotations = new ArrayList<String>();
|
||||||
private SearchStrategy strategy;
|
private SearchStrategy strategy;
|
||||||
|
|
||||||
public BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata,
|
public BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata,
|
||||||
|
|
@ -144,12 +187,16 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
|
||||||
.getAllAnnotationAttributes(annotationType.getName(), true);
|
.getAllAnnotationAttributes(annotationType.getName(), true);
|
||||||
collect(attributes, "name", this.names);
|
collect(attributes, "name", this.names);
|
||||||
collect(attributes, "value", this.types);
|
collect(attributes, "value", this.types);
|
||||||
|
collect(attributes, "annotation", this.annotations);
|
||||||
if (this.types.isEmpty() && this.names.isEmpty()) {
|
if (this.types.isEmpty() && this.names.isEmpty()) {
|
||||||
addDeducedBeanType(context, metadata, this.types);
|
addDeducedBeanType(context, metadata, this.types);
|
||||||
}
|
}
|
||||||
Assert.isTrue(!this.types.isEmpty() || !this.names.isEmpty(), "@"
|
Assert.isTrue(
|
||||||
|
!this.types.isEmpty() || !this.names.isEmpty()
|
||||||
|
|| !this.annotations.isEmpty(),
|
||||||
|
"@"
|
||||||
+ ClassUtils.getShortName(annotationType)
|
+ ClassUtils.getShortName(annotationType)
|
||||||
+ " annotations must specify at least one bean");
|
+ " annotations must specify at least one bean (type, name or annotation)");
|
||||||
this.strategy = (SearchStrategy) metadata.getAnnotationAttributes(
|
this.strategy = (SearchStrategy) metadata.getAnnotationAttributes(
|
||||||
annotationType.getName()).get("search");
|
annotationType.getName()).get("search");
|
||||||
}
|
}
|
||||||
|
|
@ -204,6 +251,10 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
|
||||||
return this.types;
|
return this.types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getAnnotations() {
|
||||||
|
return this.annotations;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringCreator(this).append("names", this.names)
|
return new ToStringCreator(this).append("names", this.names)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.ImportResource;
|
import org.springframework.context.annotation.ImportResource;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
@ -32,6 +33,7 @@ import static org.junit.Assert.assertTrue;
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*/
|
*/
|
||||||
|
// FIXME: unignore test
|
||||||
@Ignore
|
@Ignore
|
||||||
public class ConditionalOnBeanTests {
|
public class ConditionalOnBeanTests {
|
||||||
|
|
||||||
|
|
@ -77,6 +79,14 @@ public class ConditionalOnBeanTests {
|
||||||
assertEquals("bar", this.context.getBean("bar"));
|
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
|
@Configuration
|
||||||
@ConditionalOnBean(name = "foo")
|
@ConditionalOnBean(name = "foo")
|
||||||
protected static class OnBeanNameConfiguration {
|
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
|
@Configuration
|
||||||
@ConditionalOnBean(String.class)
|
@ConditionalOnBean(String.class)
|
||||||
protected static class OnBeanClassConfiguration {
|
protected static class OnBeanClassConfiguration {
|
||||||
|
|
@ -96,6 +115,7 @@ public class ConditionalOnBeanTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@EnableScheduling
|
||||||
protected static class FooConfiguration {
|
protected static class FooConfiguration {
|
||||||
@Bean
|
@Bean
|
||||||
public String foo() {
|
public String foo() {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import org.junit.Test;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
@ -84,6 +85,14 @@ public class ConditionalOnMissingBeanTests {
|
||||||
assertThat(this.context.getBeansOfType(ExampleBean.class).size(), equalTo(1));
|
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
|
@Configuration
|
||||||
@ConditionalOnMissingBean(name = "foo")
|
@ConditionalOnMissingBean(name = "foo")
|
||||||
protected static class OnBeanNameConfiguration {
|
protected static class OnBeanNameConfiguration {
|
||||||
|
|
@ -94,6 +103,16 @@ public class ConditionalOnMissingBeanTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ConditionalOnMissingBean(annotation = EnableScheduling.class)
|
||||||
|
protected static class OnAnnotationConfiguration {
|
||||||
|
@Bean
|
||||||
|
public String bar() {
|
||||||
|
return "bar";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableScheduling
|
||||||
protected static class FooConfiguration {
|
protected static class FooConfiguration {
|
||||||
@Bean
|
@Bean
|
||||||
public String foo() {
|
public String foo() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue