From 58369d3229d5201328dae764973d6a07ac58293c Mon Sep 17 00:00:00 2001 From: thibaultfaure Date: Fri, 10 Jun 2022 17:29:16 +0200 Subject: [PATCH] BAEL-5451 Code for the Finding All Beans with a Custom Annotation article (#12143) --- .../latestsspring/MyComponent.java | 9 ++++ .../latestsspring/MyConfigurationBean.java | 15 ++++++ .../latestsspring/MyCustomAnnotation.java | 9 ++++ .../latestsspring/MyService.java | 5 ++ .../factorybeans/AnnotatedBeansComponent.java | 19 ++++++++ .../olderspring/factorybeans/BeanUtils.java | 48 +++++++++++++++++++ .../factorybeans/MyConfigurationBean.java | 15 ++++++ .../factorybeans/MyCustomAnnotation.java | 9 ++++ .../olderspring/factorybeans/MyService.java | 5 ++ .../olderspring/qualifier/MyComponent.java | 9 ++++ .../qualifier/MyConfigurationBean.java | 15 ++++++ .../qualifier/MyCustomAnnotation.java | 12 +++++ .../olderspring/qualifier/MyService.java | 5 ++ .../AnnotatedBeansIntegrationTest.java | 28 +++++++++++ .../AnnotatedBeansIntegrationTest.java | 47 ++++++++++++++++++ .../AnnotatedBeansIntegrationTest.java | 35 ++++++++++++++ 16 files changed, 285 insertions(+) create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyComponent.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyConfigurationBean.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyCustomAnnotation.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyService.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/AnnotatedBeansComponent.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/BeanUtils.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyConfigurationBean.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyCustomAnnotation.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyService.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyComponent.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyConfigurationBean.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyCustomAnnotation.java create mode 100644 spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyService.java create mode 100644 spring-di-3/src/test/java/com/baeldung/countingbeans/latestsspring/AnnotatedBeansIntegrationTest.java create mode 100644 spring-di-3/src/test/java/com/baeldung/countingbeans/olderspring/factorybeans/AnnotatedBeansIntegrationTest.java create mode 100644 spring-di-3/src/test/java/com/baeldung/countingbeans/olderspring/qualifier/AnnotatedBeansIntegrationTest.java diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyComponent.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyComponent.java new file mode 100644 index 0000000000..2df3f1ee23 --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyComponent.java @@ -0,0 +1,9 @@ +package com.baeldung.countingbeans.latestsspring; + +import org.springframework.stereotype.Component; + +@Component +@MyCustomAnnotation +public class MyComponent { + +} \ No newline at end of file diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyConfigurationBean.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyConfigurationBean.java new file mode 100644 index 0000000000..c67a09356a --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyConfigurationBean.java @@ -0,0 +1,15 @@ +package com.baeldung.countingbeans.latestsspring; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MyConfigurationBean { + + @Bean + @MyCustomAnnotation + MyService myService() { + return new MyService(); + } + +} diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyCustomAnnotation.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyCustomAnnotation.java new file mode 100644 index 0000000000..32edc3441c --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyCustomAnnotation.java @@ -0,0 +1,9 @@ +package com.baeldung.countingbeans.latestsspring; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention( RetentionPolicy.RUNTIME ) +public @interface MyCustomAnnotation { + +} \ No newline at end of file diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyService.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyService.java new file mode 100644 index 0000000000..90b734a6c4 --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/latestsspring/MyService.java @@ -0,0 +1,5 @@ +package com.baeldung.countingbeans.latestsspring; + +public class MyService { + +} \ No newline at end of file diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/AnnotatedBeansComponent.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/AnnotatedBeansComponent.java new file mode 100644 index 0000000000..5828e8661d --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/AnnotatedBeansComponent.java @@ -0,0 +1,19 @@ +package com.baeldung.countingbeans.olderspring.factorybeans; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.stereotype.Component; + +@Component +public class AnnotatedBeansComponent { + + @Autowired + GenericApplicationContext applicationContext; + + public List getBeansWithAnnotation(Class annotationClass) { + return BeanUtils.getBeansWithAnnotation(applicationContext, annotationClass); + } + +} \ No newline at end of file diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/BeanUtils.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/BeanUtils.java new file mode 100644 index 0000000000..e5c642b974 --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/BeanUtils.java @@ -0,0 +1,48 @@ +package com.baeldung.countingbeans.olderspring.factorybeans; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class BeanUtils { + + // NB : this method lists only beans created via factory methods + public static List getBeansWithAnnotation(GenericApplicationContext applicationContext, Class annotationClass) { + List result = new ArrayList(); + ConfigurableListableBeanFactory factory = applicationContext.getBeanFactory(); + for(String name : factory.getBeanDefinitionNames()) { + BeanDefinition bd = factory.getBeanDefinition(name); + if(bd.getSource() instanceof AnnotatedTypeMetadata) { + AnnotatedTypeMetadata metadata = (AnnotatedTypeMetadata) bd.getSource(); + if (metadata.getAnnotationAttributes(annotationClass.getName()) != null) { + result.add(name); + } + } + } + return result; + } + + // NB : list beans created via factory methods using streams (same method as before, written differently) + public static List getBeansWithAnnotation_StreamVersion(GenericApplicationContext applicationContext, Class annotationClass) { + ConfigurableListableBeanFactory factory = applicationContext.getBeanFactory(); + return Arrays.stream(factory.getBeanDefinitionNames()) + .filter(name -> isAnnotated(factory, name, annotationClass)) + .collect(Collectors.toList()); + } + + private static boolean isAnnotated(ConfigurableListableBeanFactory factory, String beanName, Class clazz) { + BeanDefinition beanDefinition = factory.getBeanDefinition(beanName); + if(beanDefinition.getSource() instanceof AnnotatedTypeMetadata) { + AnnotatedTypeMetadata metadata = (AnnotatedTypeMetadata) beanDefinition.getSource(); + return metadata.getAnnotationAttributes(clazz.getName()) != null; + } + return false; + } + +} \ No newline at end of file diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyConfigurationBean.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyConfigurationBean.java new file mode 100644 index 0000000000..6937313c22 --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyConfigurationBean.java @@ -0,0 +1,15 @@ +package com.baeldung.countingbeans.olderspring.factorybeans; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MyConfigurationBean { + + @Bean + @MyCustomAnnotation + MyService myService() { + return new MyService(); + } + +} diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyCustomAnnotation.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyCustomAnnotation.java new file mode 100644 index 0000000000..36810adfd7 --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyCustomAnnotation.java @@ -0,0 +1,9 @@ +package com.baeldung.countingbeans.olderspring.factorybeans; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention( RetentionPolicy.RUNTIME ) +public @interface MyCustomAnnotation { + +} \ No newline at end of file diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyService.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyService.java new file mode 100644 index 0000000000..35e42c0483 --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/factorybeans/MyService.java @@ -0,0 +1,5 @@ +package com.baeldung.countingbeans.olderspring.factorybeans; + +public class MyService { + +} diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyComponent.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyComponent.java new file mode 100644 index 0000000000..9e8329c374 --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyComponent.java @@ -0,0 +1,9 @@ +package com.baeldung.countingbeans.olderspring.qualifier; + +import org.springframework.stereotype.Component; + +@Component +@MyCustomAnnotation +public class MyComponent { + +} \ No newline at end of file diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyConfigurationBean.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyConfigurationBean.java new file mode 100644 index 0000000000..4155e887bc --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyConfigurationBean.java @@ -0,0 +1,15 @@ +package com.baeldung.countingbeans.olderspring.qualifier; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MyConfigurationBean { + + @Bean + @MyCustomAnnotation + MyService myService() { + return new MyService(); + } + +} \ No newline at end of file diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyCustomAnnotation.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyCustomAnnotation.java new file mode 100644 index 0000000000..c81cc85c7f --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyCustomAnnotation.java @@ -0,0 +1,12 @@ +package com.baeldung.countingbeans.olderspring.qualifier; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.springframework.beans.factory.annotation.Qualifier; + +@Retention( RetentionPolicy.RUNTIME ) +@Qualifier +public @interface MyCustomAnnotation { + +} \ No newline at end of file diff --git a/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyService.java b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyService.java new file mode 100644 index 0000000000..d4e5c042ec --- /dev/null +++ b/spring-di-3/src/main/java/com/baeldung/countingbeans/olderspring/qualifier/MyService.java @@ -0,0 +1,5 @@ +package com.baeldung.countingbeans.olderspring.qualifier; + +public class MyService { + +} diff --git a/spring-di-3/src/test/java/com/baeldung/countingbeans/latestsspring/AnnotatedBeansIntegrationTest.java b/spring-di-3/src/test/java/com/baeldung/countingbeans/latestsspring/AnnotatedBeansIntegrationTest.java new file mode 100644 index 0000000000..8dc57b79e8 --- /dev/null +++ b/spring-di-3/src/test/java/com/baeldung/countingbeans/latestsspring/AnnotatedBeansIntegrationTest.java @@ -0,0 +1,28 @@ +package com.baeldung.countingbeans.latestsspring; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class AnnotatedBeansIntegrationTest { + + /** + * Note : this test fails with any spring version < 2.2 + * Before, the getBeansWithAnnotation method was not checking the beans created via factory method + * Please find the change here : https://github.com/spring-projects/spring-framework/commit/e0fe32af05ac525ef5e11c3ac5195a08759bb85e + */ + @Test + void whenApplicationContextStarted_ThenShouldDetectAllAnnotatedBeans() { + try (AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext( MyComponent.class, MyConfigurationBean.class )) { + Map beans = applicationContext.getBeansWithAnnotation(MyCustomAnnotation.class); + assertEquals(2, beans.size()); + assertTrue(beans.keySet().containsAll(Arrays.asList("myComponent", "myService"))); + } + } + +} \ No newline at end of file diff --git a/spring-di-3/src/test/java/com/baeldung/countingbeans/olderspring/factorybeans/AnnotatedBeansIntegrationTest.java b/spring-di-3/src/test/java/com/baeldung/countingbeans/olderspring/factorybeans/AnnotatedBeansIntegrationTest.java new file mode 100644 index 0000000000..2d6ce324c1 --- /dev/null +++ b/spring-di-3/src/test/java/com/baeldung/countingbeans/olderspring/factorybeans/AnnotatedBeansIntegrationTest.java @@ -0,0 +1,47 @@ +package com.baeldung.countingbeans.olderspring.factorybeans; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {MyConfigurationBean.class, AnnotatedBeansComponent.class}) +public class AnnotatedBeansIntegrationTest { + + @Autowired + AnnotatedBeansComponent annotatedBeansComponent; + + @Test + void whenBeanUtilsGetBeansWithAnnotation_ThenShouldListAnnotatedBean() { + try (AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigurationBean.class)) { + List result = BeanUtils.getBeansWithAnnotation(applicationContext, MyCustomAnnotation.class); + assertEquals(1, result.size()); + assertEquals("myService", result.get(0)); + } + } + + @Test + void whenBeanUtilsGetBeansWithAnnotationStreamVersion_ThenShouldListAnnotatedBean() { + try (AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigurationBean.class)) { + List result = BeanUtils.getBeansWithAnnotation(applicationContext, MyCustomAnnotation.class); + assertEquals(1, result.size()); + assertEquals("myService", result.get(0)); + + } + } + + @Test + void whenAnnotatedBeansComponentGetBeansWithAnnotation_ThenShouldListAnnotatedBean() { + List result = annotatedBeansComponent.getBeansWithAnnotation(MyCustomAnnotation.class); + assertEquals(1, result.size()); + assertEquals("myService", result.get(0)); + } + +} \ No newline at end of file diff --git a/spring-di-3/src/test/java/com/baeldung/countingbeans/olderspring/qualifier/AnnotatedBeansIntegrationTest.java b/spring-di-3/src/test/java/com/baeldung/countingbeans/olderspring/qualifier/AnnotatedBeansIntegrationTest.java new file mode 100644 index 0000000000..0b2bb43061 --- /dev/null +++ b/spring-di-3/src/test/java/com/baeldung/countingbeans/olderspring/qualifier/AnnotatedBeansIntegrationTest.java @@ -0,0 +1,35 @@ +package com.baeldung.countingbeans.olderspring.qualifier; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {MyComponent.class, MyConfigurationBean.class}) +public class AnnotatedBeansIntegrationTest { + + @Autowired + @MyCustomAnnotation + private List annotatedBeans; + + @Test + void whenAutowiring_ThenShouldDetectAllAnnotatedBeans() { + assertEquals(2, annotatedBeans.size()); + List classNames = annotatedBeans.stream() + .map(Object::getClass) + .map(Class::getName) + .map(s -> s.substring(s.lastIndexOf(".") + 1)) + .collect(Collectors.toList()); + assertTrue(classNames.containsAll(Arrays.asList("MyComponent", "MyService"))); + } + +}